{"id":166,"date":"2009-09-11T07:25:54","date_gmt":"2009-09-11T14:25:54","guid":{"rendered":"http:\/\/blog.monnet-usa.com\/?p=166"},"modified":"2009-09-30T05:54:04","modified_gmt":"2009-09-30T12:54:04","slug":"running-the-camping-microframework-on-ironruby","status":"publish","type":"post","link":"https:\/\/blog.monnet-usa.com\/?p=166","title":{"rendered":"Running the Camping Microframework on IronRuby"},"content":{"rendered":"<table>\n<tr>\n<td width=\"70%\">\n    <img src='.\/wp-content\/IronCamping.png' \/>\n    <\/td>\n<td>\n<div style=\"text-align:right; font-size:10px;\">\n<ul style=\"list-style: none;\">\n<li><a href=\"#why\">Homage to _why<\/a><\/li>\n<li><a href=\"#microframework\">Camping, a Micro-Framework<\/a><\/li>\n<li><a href=\"#campingexample\">Camping Examples<\/a><\/li>\n<li><a href=\"#ironrubycampsite\">IronRuby Camp Site<\/a><\/li>\n<li><a href=\"#needafewmorepoles\">Need A Few More Poles For The Tent!<\/a><\/li>\n<li><a href=\"#raisingthecampflag\">Raising The Camp Flag<\/a><\/li>\n<li><a href=\"ironrubyconsole\">Running In The IronRuby Console<\/a><\/li>\n<li><a href=\"#sowhat\">So What?<\/a><\/li>\n<li><a href=\"#referencesandresources\">References and Resources<\/a><\/li>\n<\/ul><\/div>\n<\/td>\n<\/tr>\n<\/table>\n<h3>Homage to <a href=\"http:\/\/en.wikipedia.org\/wiki\/Why_the_lucky_stiff\"><b>_why<\/b><\/a> <\/h3>\n<table border='0'>\n<tr valign=\"top\">\n<td>\n<p>Six or seven years ago, when I read about Ruby, I quickly came across <a href=\"http:\/\/mislav.uniqpath.com\/poignant-guide\/book\/\">Why&#8217;s Poignant Guide To Ruby<\/a>.<br \/>\n    I immediately loved <a href=\"http:\/\/en.wikipedia.org\/wiki\/Why_the_lucky_stiff\">_why&#8217;s<\/a> artistic style, his passion for teaching, as well as the depth of some of the concepts he was trying to present.<\/p>\n<p>    On August 12th, 2009, _why went &#8220;off-line&#8221;,stirring a lot of <a href=\"http:\/\/ejohn.org\/blog\/eulogy-to-_why\/\"> emotions from the Ruby community<\/a>.<br \/>\n_why was a prolific developer, artist, and teacher, and inspired many people through his <a href=\"http:\/\/whymirror.github.com\/\">many contributions<\/a>.<\/p>\n<p>_why&#8217;s code treasure trove included <a href=\"\">Camping<\/a>, a <a href=\"microframework\">micro-framework<\/a> aimed at providing a lean base for your web apps.\n      <\/p>\n<\/td>\n<td>\n      <a href=\"http:\/\/mislav.uniqpath.com\/poignant-guide\/book\/chapter-7.html\"><img src='wp-content\/_why-cartoon.gif' \/><\/a>\n    <\/td>\n<\/tr>\n<\/table>\n<p><a name=\"microframework\"><\/a><\/p>\n<h3>Camping, a Micro-Framework<\/h3>\n<p>\nA <b><u>micro<\/u><\/b>-framework consists of:\n<\/p>\n<ol>\n<li>The bare essentials to host a web app &#8211; Camping leverages <a href=\"http:\/\/rack.rubyforge.org\/\">Rack<\/a> for the base request and response processing but provides its own basic <b>session<\/b> mechanim<\/li>\n<li>A core set of base classes to provide some <b>structure<\/b> to your apps &#8211; Camping promotes the <a href=\"http:\/\/c2.com\/cgi\/wiki?ModelViewController\">Model-View-Controller pattern<\/a><\/li>\n<li>Constructs to create your models &#8211; Camping leverages the modelling conventions introduced by <a href=\"http:\/\/ar.rubyonrails.org\/\">ActiveRecord<\/a><\/li>\n<li>Integration with a <b>persistence<\/b> engine &#8211; Camping uses ActiveRecord<\/li>\n<li>A <b>templating<\/b> engine for the <b>view<\/b> &#8211; Camping supports layouts<\/li>\n<li>A <b>simplified url routing<\/b> mechanism for controllers<\/li>\n<li>An <b>easy<\/b> way to access <b>session and form data<\/b> inside controllers<\/li>\n<\/ol>\n<p>\nWhy a <b><u>micro<\/u><\/b>-framework as opposed to a <b>regular<\/b> framework? Well consider the following benefits:\n<\/p>\n<ol>\n<li>There are less <b>moving parts<\/b> in the framework making it easier to understand<\/li>\n<li>There are <b>fewer conventions<\/b> to learn about (e.g. compared to Rails)<\/li>\n<li>The <b>minimalistic<\/b> implementation encourages you to adopt the same approach for your app<\/li>\n<li>You will be able to <b>focus<\/b> more on the <b>core concepts<\/b> of your application<\/li>\n<li>The resulting <b>total footprint<\/b> is much smaller and tighter making it possible to run under more stringent infrastructure constraints<\/li>\n<\/ol>\n<p>A micro-framework like Camping provides <b>just enough structure and core functionality<\/b> to let you incrementally design and build your application.<br \/>\nOnce your application reaches a higher level of sophistication and technical feature requirements, you can migrate the core app to a more feature-rich framework like Rails for example.\n<\/p>\n<p>\nAlso there is <b>less &#8220;friction&#8221;<\/b> when you want to prototype an application to figure out the core concepts. I have been using Camping to try out some ideas,<br \/>\nthen realized that the implementation was just fine, so I just went ahead and deployed it as-is. The lower friction comes into play if you are building some of these apps in your own time<br \/>\nwhere you want to <b>maximize your productive time investment<\/b> and make measurable progress\tin less time.<br \/>\nNathaniel Talbott expanded on that point in his <a href=\"#whycampingmattersvideo\">&#8220;Why Camping Matters&#8221; presentation<\/a>.\n<\/p>\n<p>To get started, <a href=\"http:\/\/ironruby.net\/Download\">download IronRuby<\/a> and get Camping:<\/p>\n<pre class=\"brush: ruby\">\r\n  gem install camping\r\n<\/pre>\n<p> <a name=\"campingexample\"><\/a><\/p>\n<h3>Camping Examples<\/h3>\n<p>\n  Below are 3 progressively more functional examples illustrating the Model-View-Controller approach in Camping.\n<\/p>\n<p><a name=\"picoexample\"><\/a><\/p>\n<h5 style=\"margin-left: 20px;\">A) Controller Sample<\/h5>\n<p>\n  Here is a very small example similar to a Pico-Sinatra application with a module including just a controller with one single route.\n<\/p>\n<ol>\n<li>The <b>Index<\/b> class represents a micro-controller for the <b>\/<\/b> route.<\/li>\n<li>A <b>get<\/b> method \/ action is defined for the <b>HTTP GET<\/b> method on that route.<\/li>\n<\/ol>\n<pre class=\"brush: ruby\">\r\n  require 'camping'\r\n\r\n  Camping.goes :Pico\r\n\r\n  module Pico\r\n    def Pico.create\r\n    end\r\n  end\r\n\r\n  module Pico::Controllers\r\n    class Index < R '\/'\r\n    def get\r\n      \"Welcome to the Pico Camping app - #{Time.now}\"\r\n    end\r\n    end\r\n  end\r\n<\/pre>\n<h5 style=\"margin-left: 20px;\">B) Controller And View Sample<\/h5>\n<p>\n  Here is a variation of the previous example with a corresponding view module providing a <b>layout<\/b> serving as a template very much like in Rails.<\/b>\n<\/p>\n<ol>\n<li>An <b>index<\/b> method is defined in the view module.<\/li>\n<li>The view uses <a href=\"#markaby\">Markaby<\/a> to express the content to be rendered.<\/b><\/li>\n<li>The <b>get<\/b> method on the <b>Index<\/b> controller calls the <b>render :index<\/b> method to select the view to render.<\/li>\n<\/ol>\n<pre class=\"brush: ruby\">\r\n  require 'camping'\r\n\r\n  Camping.goes :Nano\r\n\r\n  module Nano\r\n    def Nano.create\r\n    end\r\n  end\r\n\r\n  module Nano::Controllers\r\n    class Index < R '\/'\r\n    def get\r\n      render :index\r\n    end\r\n    end\r\n  end\r\n\r\n  module Nano::Views\r\n    def layout\r\n    html do\r\n      body do\r\n        self << yield\r\n        hr\r\n        div.footer! { \"#{Time.now}\" } \r\n      end\r\n    end\r\n    end\r\n    \r\n    def index\r\n    h3 \"Welcome to the Nano Camping app!\"\r\n    end\r\n  end\r\n<\/pre>\n<h5 style=\"margin-left: 20px;\">C) Model View Controller Sample<\/h5>\n<p>\n  Here is a variation of the previous example with the following additions:\n<\/p>\n<ol>\n<li>A new <b>module<\/b> called Models for our ActiveRecord-based models (line 18)<\/li>\n<li>A new <b>model<\/b> class: <b>Announcement<\/b> mapped to a <b>micro_announcements<\/b> table (line 19)<\/li>\n<li>A method to setup the <b>database schema<\/b> and a demo record (lines 06 and 22)<\/li>\n<li><b>Retrieval<\/b> of the announcements in the <b>get<\/b> method of the <b>Index controller<\/b> (line 41)<\/li>\n<li><b>Rendering<\/b> of the announcements in the <b>index<\/b> method of the <b>view<\/b> (line 60)<\/li>\n<\/ol>\n<pre class=\"brush: ruby\">\r\n  require 'camping'\r\n\r\n  Camping.goes :Micro\r\n\r\n  module Micro\r\n    def Micro.create\r\n      Camping::Models::Base.establish_connection  :adapter => 'mssql',\r\n        :host => 'localhost', \r\n        :database => 'camping',\r\n        :username => '*USERNAME*', \r\n        :password => '*PASSWORD*'\r\n\r\n      Micro::Models.create_schema :assume => (Micro::Models::Announcement.table_exists? ? 1.0 : 0.0)\r\n    end\r\n  end\r\n\r\n\r\n  module Micro::Models\r\n    class Announcement < Base\r\n    end\r\n    \r\n    class SetupTheDB < V 1.0\r\n      def self.up\r\n      create_table :micro_announcements, :force => true do |t|\r\n        t.column :id,       :integer, :null => false\r\n        t.column :title,    :string,  :limit => 255\r\n      end\r\n      \r\n      Announcement.create :title => 'Welcome to the Micro Camping app!'\r\n      end\r\n      \r\n      def self.down\r\n      drop_table :micro_announcements\r\n      end\r\n    end\r\n  end\r\n\r\n  module Micro::Controllers\r\n    class Index < R '\/'\r\n    def get\r\n      @announcements = Announcement.find(:all)\r\n      render :index\r\n    end\r\n    end\r\n\r\n  end\r\n\r\n  module Micro::Views\r\n    def layout\r\n    html do\r\n      body do\r\n        self << yield\r\n        hr\r\n        div.footer! { \"#{Time.now}\" } \r\n      end\r\n    end\r\n    end\r\n    \r\n    def index\r\n    @announcements { | a | h3 a }\r\n    end\r\n  end \r\n<\/pre>\n<p>\nThese three examples are minimalistic. You have access to the full power of ActiveRecord to express relationships between models, add validation and constraints.<br \/>\nAt the controller level Camping also provide a really easy way to access both form data, querystring parameters, and session parameters through hashes (HashWithIndifferentAccess).<\/p>\n<ul>\n<li>The <b>input<\/b> variable, available at the controller level, holds on to all form data and querystring parameters.<\/li>\n<li>The <b>@state<\/b> variable, available at the view and controller levels, holds all session data<\/li>\n<\/ul>\n<p>So don't let these examples fool you, the micro-framework is not as \"micro\" as you might think! For a richer example, check-out the <a href=\"http:\/\/github.com\/camping\/camping\/blob\/fa8802511fd745dedcbeab61d809ddf15af80e43\/examples\/blog.rb\">Camping Blog<\/a> example.<br \/>\nTo test Camping on IronRuby, I started with the <a href=\"#picoexample\">basic example (pico)<\/a> and then progressively worked up the level of functionality.\n<\/p>\n<p><a name=\"ironrubycampsite\"><\/a><\/p>\n<h3>IronRuby Camp Site<\/h3>\n<p>\nI have been actively following the development of IronRuby since the days Microsoft hired <a href=\"http:\/\/www.iunknown.com\/\">John Lam<\/a> to work on <a href=:\"http:\/\/www.ironruby.net\/\">IronRuby<\/a>.<br \/>\n(See my <a href=\"#myotherrelatedposts\">other post(s) on the topic<\/a>).<br \/>\nI got pretty excited when I ready Jimmy Schementi's post on <a href=\"#deployingrackoniis\">getting Rack to run on IIS<\/a>.<br \/>\nThis opened the ability to run Camping since it has very lightweight needs and works on top of Rack. <\/p>\n<p>So as a prerequisite I started to install <a href=\"deployingrackoniis\">IronRack<\/a> and its example.<br \/>\nUnfortunately I could not get the example to work - maybe due to some of the permissions around the LogUtils component.<br \/>\nSo I built a slightly altered version \/ configuration and switched logging to <a href=\"http:\/\/logging.apache.org\/log4net\/index.html\">Log4Net<\/a>.<br \/>\nAnd bingo! I was able to run the basic IronRack example.<\/p>\n<p>I then created a custom IronCamping web app, modeled very closely after the IronRack model with a few additions:\n<\/p>\n<ol>\n<li>Log4Net logging<\/li>\n<li>Preload of the Camping gem - with the ability to configure the gem version to use<\/li>\n<li>Customized the config.ru (Rack Up) to start a very tiny Camping app:\n<pre class=\"brush: ruby\">\r\n    require 'rubygems'\r\n    require 'rack'\r\n    require 'camping'\r\n    require 'demo'  # where my Camping demo code resides\r\n\r\n    # Initialize the Demo module using the create method\r\n    Demo.create\r\n\r\n    # Instantiate a Rack-Camping adapter for our Demo module\r\n    demo_module_adapter = Rack::Adapter::Camping.new(Demo)\r\n\r\n    # Run the Camping Demo app\r\n    run demo_module_adapter\r\n    <\/pre>\n<\/li>\n<li>I also configured my IIS virtual directory to explicitly map the <b>.*<\/b> to the ASP.NET ISAPI module<\/li>\n<\/ol>\n<p><a name=\"needafewmorepoles\" ><\/a><\/p>\n<h3>Need A Few More Poles For The Tent!<\/h3>\n<p>Ok, so I could not get the famous raising of the tent to work using the traditional statement:<\/p>\n<pre class=\"brush: ruby\">\r\nCamping.goes :Pico\r\n<\/pre>\n<p>\nSo I started to really read the <a href=\"http:\/\/github.com\/camping\/camping\/blob\/206b8573c8d1442926d7c7e30e9475ef11b3ba40\/lib\/camping-unabridged.rb\">(unabridged!) Camping framework code<\/a><br \/>\nto understand the core as well as to isolate code fragments<br \/>\nand compare their execution in IronRuby and in standard Ruby (MRI).<br \/>\nOnce I got the minimalistic app to come up, I moved on to <a href=\"http:\/\/github.com\/camping\/camping\/blob\/fa8802511fd745dedcbeab61d809ddf15af80e43\/examples\/blog.rb\">_why's Camping Blog application<\/a>.<br \/>\nI ran into additional sets of issues which required more digging, analyzing and testing.<br \/>\nHere is a summary of my findings and work-arounds:\n<\/p>\n<ol>\n<li>The \"goes\" statement performs some text substitutions in the basic camping module, loads and binds them to the TOPLEVEL_BINDING.<br \/>\n  Apparently at the time of this writing, that global constant was not set by the base IronRack code.<br \/>\n  A suggestion was made on the mailing list to update the RubyEngine class of IronRack.<br \/>\n  I will update the post once this is available.<\/p>\n<p>  The work-around was to explicitly require the <a href=\"\">'hacks.rb'<\/a> module from IronRuby like so:<\/p>\n<pre class=\"brush: ruby\">\r\n  require 'hacks'  # for TOPLEVEL_BINDING\r\n  require 'camping'\r\n  Camping.goes :Pico\r\n  <\/pre>\n<\/li>\n<li><b>[*'a'..'b']<\/b> does not work in IR the same way it does in MRI.<br \/>\n        As a result, on line 27 of <a href=\"http:\/\/github.com\/camping\/camping\/blob\/206b8573c8d1442926d7c7e30e9475ef11b3ba40\/lib\/camping\/session.rb\">camping\/session.rb<\/a> the session id cannot be generated correctly leading to an error.<\/p>\n<p>        The solution was to explicitly do a <b>to_a<\/b> conversion and rewrite line 27 as:<\/p>\n<pre class=\"brush: ruby\">\r\n  # OLD:  RAND_CHARS = [*'A'..'Z'] + [*'0'..'9'] + [*'a'..'z']\r\n  # NEW: User standard interval notation plus an explicit to_a  to return an array\r\n  RAND_CHARS = ('A'..'Z').to_a + ('0'..'9').to_a + ('a'..'z').to_a\t\t\t\t\t\r\n  <\/pre>\n<\/li>\n<li>Not sure exactly why but when the session is loaded, the @state is not restored as a HashWithIndifferentAccess\n<p>  The solution was to explicitly cast the state as a HashWithIndifferentAccess to explicitly re-cast the hash as a HashWithIndifferentAccess after line 111 of the<br \/>\n  <b>service(*a)<\/b> method of the Session module in session.rb, right after the line initializing @state:<\/p>\n<pre class=\"brush: ruby\">\r\n    @state = (session[app] ||= Camping::H[])\r\n    @state = Camping::H[@state]  # Explicit re-cast as a HashWithIndifferentAccess\r\n  <\/pre>\n<\/li>\n<li>On line 399 (of the <a href=\"http:\/\/github.com\/camping\/camping\/blob\/3302af538d449bd5e9e1389b9358b4a69693cfd8\/lib\/camping-unabridged.rb\">camping-unabridged.rb<\/a>),<br \/>\n  the code creating the value to be written to the document cookie returns an array instead of a string.<br \/>\n   This causes the cookie to not be populated correctly which prevents the session from working.<\/p>\n<p>  The solution was to rewrite the line:<\/p>\n<pre class=\"brush: ruby\">\r\n  # OLD:  @headers['Set-Cookie'] = @cookies.map { |k,v| \"#{k}=#{C.escape(v)}; path=#{self\/\"\/\"}\" if v != @k[k] } - [nil]\r\n  # NEW: evaluates and renders the expression as a string using \"#{}\"\r\n  @headers[\"Set-Cookie\"] = \"#{@cookies.map{|k,v|\"#{k}=#{C.escape(v)}; path=#{self\/'\/'}\" if v != @k[k] } - [nil]}\"\r\n  <\/pre>\n<\/li>\n<li>Since I would like the app to run on MS SQL Server, I added the <a href=\"http:\/\/github.com\/jschementi\/activerecord-mssql-adapter\/tree\/master\">ActiveRecord MS SQL adapter<\/a><br \/>\n  to my <b>activerecord-2.3.3libactive_recordconnection_adapters<\/b> folder.<br \/>\n  I also made a change to the <a ref=\"http:\/\/github.com\/jschementi\/activerecord-mssql-adapter\/blob\/7864d47ffe80610c71bceb9827489a12f45d41a3\/mssql_adapter.rb\">includes_id_field<\/a><br \/>\n  method to ensure that the <b>SET IDENTITY_INSERT<\/b> statement is only generated for the id column.<\/p>\n<pre class=\"brush: ruby\">\r\n  def includes_id_field(sql)\r\n    sql =~ \/[id]|[w*_id]\/i\r\n  end\r\n  <\/pre>\n<p>Then I added the following ActiveRecord configuration statement to my application module:<\/p>\n<pre class=\"brush: ruby\">\r\n  Camping::Models::Base.establish_connection\r\n    :adapter => 'mssql',\r\n    :host => 'localhost', \r\n    :database => 'camping',\r\n    :username => '*USERNAME*', \r\n    :password => '*PASSWORD*'\r\n  <\/pre>\n<\/li>\n<li>Finally, the harder problem I had to track down was that the setup of the <b>rack.input<\/b> context for Rack<br \/>\n  (line 159) in the <a href=\"http:\/\/github.com\/ironruby\/ironruby\/blob\/adb33b18780e5f2a7e050fd38ebc58750ec1cf5b\/Merlin\/Main\/Hosts\/IronRuby.Rack\/IIS.cs\">IIS class for IronRack<\/a>.<\/p>\n<pre class=\"brush: ruby\">\r\n    env[\"rack.input\"] = RubyEngine.Execute(\"StringIO.new(__request.body || '')\", handle_scope);\r\n  <\/pre>\n<p>  The setup would work for a GET but not a POST with form data. This would trigger a [InvalidOperationException: can't convert System::String into String] in the DLR.<br \/>\n  <br \/>\n  The solution was to rewrite the line as follows:<\/p>\n<pre class=\"brush: ruby\">\r\n    if (request.Body != null)\r\n    {\r\n      \/\/ Explicitly pass the request body as a string\r\n      string myInput = request.Body.ToString(); \r\n      handle_scope.SetVariable(\"__myinput\", myInput);\r\n      env[\"rack.input\"] = RubyEngine.Execute(\"StringIO.new(__myinput.to_s)\", handle_scope);\r\n    }\r\n    else\r\n    {\r\n      \/\/ Original code\r\n      env[\"rack.input\"] = RubyEngine.Execute(\"StringIO.new(__request.body || '')\", handle_scope);\r\n    }\r\n  <\/pre>\n<\/li>\n<\/ol>\n<p><a name=\"raisingthecampflag\" ><\/a><\/p>\n<h3>Raising The Camp Flag<\/h3>\n<table border='0'>\n<tr valign=\"top\">\n<td>\n    So in summary, once I had:<\/p>\n<ul>\n<li>tweaked the original Camping code<\/li>\n<li>tweaked the IronRuby.Rack code<\/li>\n<li>added and tweaked the ActiveRecord SQL server adapter<\/li>\n<li>created a test database in SQL Server<\/li>\n<li>created a login account for the app<\/li>\n<li>granted the login account access to the test database<\/li>\n<li>configured the ActiveRecord connection<\/li>\n<li>added an IIS mapping for the .* extension\n    <\/ul>\n<p>    I was then able to get the Camping Blog app to run! Yippee!<\/td>\n<td><img decoding=\"async\" src=\"CampingBlogOnIronRubyRack.png\" \/><\/td>\n<\/tr>\n<\/table>\n<p>As a footnote, I noticed that the new branch of the Camping code maintained by <a href=\"http:\/\/judofyr.net\/\">Judofyr<\/a> includes more recent changes<br \/>\nthat may not require the Camping tweaks I made. I will update this post once I get a chance to test that thoroughly.<\/p>\n<p><a name=\"ironrubyconsole\" ><\/a><\/p>\n<h3>Running In The IronRuby Console<\/h3>\n<p>\nIf you want the ability to run Camping from the command line IronRuby console, you also need to:\n<\/p>\n<ol>\n<li>Copy C:IronRubybin<b>irake.bat<\/b> to C:IronRubybin<b><u>ir<\/u><\/b>camping.bat<\/li>\n<li>Copy C:rubybin<b>camping<\/b> to C:IronRubybin<b><u>ir<\/u><\/b>camping<\/li>\n<li>Create a file named <b>.campingrc<\/b> in your %USERPROFILE% directory<\/li>\n<li>Edit <b>.campingrc<\/b> and add the following content:\n<pre class=\"brush: ruby\">\r\n      database: \r\n        adapter: mssql  \r\n        host: localhost \r\n        database: camping\r\n        username: *USERNAME* \r\n        password: *PASSWORD*\r\n    <\/pre>\n<\/li>\n<\/ol>\n<p>Then you can start your Camping app as follows:<\/p>\n<pre class=\"brush: ruby\">\r\n  ircamping micro.rb\r\n<\/pre>\n<p>You can then open up a browser and go to http:\/\/localhost:3301\/<\/p>\n<p><a name=\"sowhat\"><\/a><\/p>\n<h3>So What?<\/h3>\n<p>\nI really like the power of Rails for building elaborate apps. But when I want to prototype a concept really quickly,<br \/>\nI enjoy using Camping because of its <b>minimalistic approach<\/b> combined with the <b>structure of MVC<\/b>.<br \/> <br \/>\nAnd having most of the code in a <b>single file<\/b> (albeit with some structure) makes it very convenient to make quick changes and test immediately<br \/>\n(thanks to the Camping <a href=\"http:\/\/camping.rubyforge.org\/classes\/Camping\/Reloader.html\">reloader<\/a>)<br \/>\nAnother nice thing is that Camping has such a <b>small footprint<\/b> that you can use it on shared web hosts with little processing overhead.<br \/>\nThis makes it thin enough to serve <b>REST<\/b> services using the <a href=\"http:\/\/reststop.rubyforge.org\/\">Restop<\/a> framework.<\/p>\n<p>Thanks to all the great work of the IronRuby team, I can now enjoy Camping on .NET.<br \/>\nI might even start mixing in .NET classes!!!<\/p>\n<p>Hopefully this will give you an idea of what is possible with Camping and IronRuby.\n<\/p>\n<p><a href=\"http:\/\/mislav.uniqpath.com\/poignant-guide\/book\/chapter-3.html\"><img decoding=\"async\" src=\"http:\/\/mislav.uniqpath.com\/poignant-guide\/images\/the.foxes-4c.png\"\/><\/a><\/p>\n<p><a name=\"referencesandresources\" ><\/a><\/p>\n<h3>References and Resources<\/h3>\n<h5>Software:<\/h5>\n<ol>\n<li><a href=\"http:\/\/whymirror.github.com\/\">_why's code contributions<\/a> - new mirror on GitHub<\/li>\n<li><a href=\"http:\/\/github.com\/judofyr\/camping\/tree\/master\">judofyr's fork of Camping<\/a>: active branch of Camping with enhancements<\/li>\n<li><a href=\"http:\/\/github.com\/ironruby\/ironruby\/tree\/v0.9.0\">IronRuby v0.9<\/a>: Source on GitHub<\/li>\n<li><a href=\"http:\/\/ar.rubyonrails.org\/\">ActiveRecord<\/a><\/li>\n<li><a href=\"http:\/\/github.com\/jschementi\/activerecord-mssql-adapter\/tree\/master\">ActiveRecord MS SQL Server Adapter<\/a><\/li>\n<li><a href=\"http:\/\/logging.apache.org\/log4net\/index.html\">Log4Net<\/a>: Apache logging for .NET<\/li>\n<li><a href=\"http:\/\/github.com\/joho\/markaby\/tree\/master\">Markaby Source<\/a>: Repository on GitHub<\/li>\n<li><a href=\"http:\/\/github.com\/camping\/camping\/blob\/fa8802511fd745dedcbeab61d809ddf15af80e43\/examples\/blog.rb\">Camping Blog Application<\/a>: Full Camping Example<\/li>\n<\/ol>\n<h5>Sites:<\/h5>\n<ol>\n<li><a href=\"http:\/\/mislav.uniqpath.com\/poignant-guide\/book\/\">Why's Poignant Guide To Ruby<\/a>: An cartoon-centric approach to learning Ruby<\/li>\n<li><a href=\"http:\/\/www.ironruby.net\/\">IronRuby<\/a>: Main site<\/li>\n<li><a href=\"http:\/\/rubyforge.org\/mailman\/listinfo\/ironruby-core\">IronRuby-Core<\/a>: Mailing list<\/li>\n<li><a href=\"http:\/\/camping.rubyforge.org\/\">Camping<\/a> - Documentation<\/li>\n<li><a href=\"http:\/\/c2.com\/cgi\/wiki?ModelViewController\">Model-View-Controller Pattern<\/a> <\/li>\n<li><a href=\"http:\/\/wiki.github.com\/ironruby\/ironruby\">IronRuby Wiki<\/a>: GitHub wiki<\/li>\n<li><a href=\"http:\/\/rack.rubyforge.org\/\">Rack<\/a><\/li>\n<li><a name=\"deployingrackoniis\" href=\"http:\/\/blog.jimmy.schementi.com\/2009\/05\/ironruby-at-railsconf-2009.html#iis\">Deploying Rack-based Ruby Application on IIS<\/a>: Jimmy Schementi<\/li>\n<li><a name=\"markaby\" href=\"http:\/\/markaby.rubyforge.org\/\">Markaby Doc<\/a>: Documentation<\/li>\n<li><a href=\"http:\/\/judofyr.net\/\">Magnus Holm a.k.a. judofyr<\/a>: Camping project \"Coordinator\"<\/li>\n<li><a href=\"http:\/\/www.iunknown.com\/\">John Lam<\/a>: Blog<\/li>\n<li><a href=\"http:\/\/reststop.rubyforge.org\/\">RestStop<\/a>: REST extension for Camping<\/li>\n<li><a href=\"http:\/\/code.google.com\/p\/camping-picnic\/wiki\/PicnicTutorial\">Camping-Picning<\/a>Set of extensions<\/li>\n<\/ol>\n<h5>Videos:<\/h5>\n<ol>\n<li><a name=\"whycampingmattersvideo\" href=\"http:\/\/rubyconf2007.confreaks.com\/d1t1p3_why_camping_matters.html\">Why Camping Matters<\/a>: Presentation by <a href=\"http:\/\/blog.talbott.ws\/\">Nathaniel Talbott<\/a><\/li>\n<li><a href=\"http:\/\/podcast.rubyonrails.org\/programs\/1\/episodes\/the_camping_episode\">RailsCast Camping Episode 1 of 2<\/a> <\/li>\n<li><a href=\"http:\/\/podcast.rubyonrails.org\/programs\/1\/episodes\/the_camping_episode_ii\">RailsCast Camping Episode 2 of 2<\/a> <\/li>\n<\/ol>\n<h5>My Other Related Posts:<\/h5>\n<ol>\n<li><a name=\"myotherrelatedposts\" href=\"https:\/\/blog.monnet-usa.com\/?p=20\">From Ruby Silverlight to Ruby Web Service (with a touch of spice)<\/a><\/li>\n<\/ol>\n","protected":false},"excerpt":{"rendered":"<p>Homage to _why Camping, a Micro-Framework Camping Examples IronRuby Camp Site Need A Few More Poles For The Tent! Raising The Camp Flag Running In The IronRuby Console So What? References and Resources Homage to _why Six or seven years ago, when I read about Ruby, I quickly came across Why&#8217;s Poignant Guide To Ruby. [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[23,22,56,24],"class_list":["post-166","post","type-post","status-publish","format-standard","hentry","category-ruby","tag-camping","tag-ironruby","tag-ruby","tag-_why"],"_links":{"self":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=166"}],"version-history":[{"count":15,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/166\/revisions"}],"predecessor-version":[{"id":183,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=\/wp\/v2\/posts\/166\/revisions\/183"}],"wp:attachment":[{"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.monnet-usa.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}