First Ruby on Rails App

What are we here for? To see the power of Ruby on Rails! Without further ado:

  1. You must be at a Terminal on the machine where you have Rails installed

  2. Create a new Rails application with the following command (I'll explain later):
     prawnhead@ubuntu:~$ rails new app  
        create   
        create README.md  
        create Rakefile  
        create config.ru  
        create .gitignore  
        create Gemfile  
        create app  
        create app/assets/config/manifest.js  
        create app/assets/javascripts/application.js  
        create app/assets/javascripts/cable.js  
        create app/assets/stylesheets/application.css  
        create app/channels/application_cable/channel.rb  
        create app/channels/application_cable/connection.rb  
        create app/controllers/application_controller.rb  
        create app/helpers/application_helper.rb  
        create app/jobs/application_job.rb  
        create app/mailers/application_mailer.rb  
        create app/models/application_record.rb  
        create app/views/layouts/application.html.erb  
        create app/views/layouts/mailer.html.erb  
        create app/views/layouts/mailer.text.erb  
        create app/assets/images/.keep  
        create app/assets/javascripts/channels  
        create app/assets/javascripts/channels/.keep  
        create app/controllers/concerns/.keep  
        create app/models/concerns/.keep  
        create bin  
        create bin/bundle  
        create bin/rails  
        create bin/rake  
        create bin/setup  
        create bin/update  
        create config  
        create config/routes.rb  
        create config/application.rb  
        create config/environment.rb  
        create config/secrets.yml  
        create config/cable.yml  
        create config/puma.rb  
        create config/spring.rb  
        create config/environments  
        create config/environments/development.rb  
        create config/environments/production.rb  
        create config/environments/test.rb  
        create config/initializers  
        create config/initializers/application_controller_renderer.rb  
        create config/initializers/assets.rb  
        create config/initializers/backtrace_silencers.rb  
        create config/initializers/cookies_serializer.rb  
        create config/initializers/cors.rb  
        create config/initializers/filter_parameter_logging.rb  
        create config/initializers/inflections.rb  
        create config/initializers/mime_types.rb  
        create config/initializers/new_framework_defaults.rb  
        create config/initializers/session_store.rb  
        create config/initializers/wrap_parameters.rb  
        create config/locales  
        create config/locales/en.yml  
        create config/boot.rb  
        create config/database.yml  
        create db  
        create db/seeds.rb  
        create lib  
        create lib/tasks  
        create lib/tasks/.keep  
        create lib/assets  
        create lib/assets/.keep  
        create log  
        create log/.keep  
        create public  
        create public/404.html  
        create public/422.html  
        create public/500.html  
        create public/apple-touch-icon-precomposed.png  
        create public/apple-touch-icon.png  
        create public/favicon.ico  
        create public/robots.txt  
        create test/fixtures  
        create test/fixtures/.keep  
        create test/fixtures/files  
        create test/fixtures/files/.keep  
        create test/controllers  
        create test/controllers/.keep  
        create test/mailers  
        create test/mailers/.keep  
        create test/models  
        create test/models/.keep  
        create test/helpers  
        create test/helpers/.keep  
        create test/integration  
        create test/integration/.keep  
        create test/test_helper.rb  
        create tmp  
        create tmp/.keep  
        create tmp/cache  
        create tmp/cache/assets  
        create vendor/assets/javascripts  
        create vendor/assets/javascripts/.keep  
        create vendor/assets/stylesheets  
        create vendor/assets/stylesheets/.keep  
        remove config/initializers/cors.rb  
          run bundle install  
     The dependency tzinfo-data (>= 0) will be unused by any of the platforms Bundler is installing for. Bundler is installing for ruby but the dependency is only for x86-mingw32, x86-mswin32, x64-mingw32, java. To add those platforms to the bundle, run `bundle lock --add-platform x86-mingw32 x86-mswin32 x64-mingw32 java`.  
     Fetching gem metadata from https://rubygems.org/..........  
     Fetching version metadata from https://rubygems.org/..  
     Fetching dependency metadata from https://rubygems.org/.  
     Resolving dependencies...  
     Using rake 12.0.0  
     Using concurrent-ruby 1.0.5  
     Using i18n 0.8.1  
     Using minitest 5.10.1  
     Using thread_safe 0.3.6  
     Using builder 3.2.3  
     Using erubis 2.7.0  
     Using mini_portile2 2.1.0  
     Using rack 2.0.1  
     Using nio4r 2.0.0  
     Using websocket-extensions 0.1.2  
     Using mime-types-data 3.2016.0521  
     Using arel 7.1.4  
     Using bundler 1.14.5  
     Using byebug 9.0.6  
     Using coffee-script-source 1.12.2  
     Using execjs 2.7.0  
     Using method_source 0.8.2  
     Using thor 0.19.4  
     Using debug_inspector 0.0.2  
     Using ffi 1.9.18  
     Using multi_json 1.12.1  
     Using rb-fsevent 0.9.8  
     Using puma 3.7.1  
     Using sass 3.4.23  
     Using tilt 2.0.6  
     Using sqlite3 1.3.13  
     Using turbolinks-source 5.0.0  
     Using tzinfo 1.2.2  
     Using nokogiri 1.7.0.1  
     Using rack-test 0.6.3  
     Using sprockets 3.7.1  
     Using websocket-driver 0.6.5  
     Using mime-types 3.1  
     Using coffee-script 2.4.1  
     Using uglifier 3.1.4  
     Using rb-inotify 0.9.8  
     Using turbolinks 5.0.1  
     Using activesupport 5.0.2  
     Using loofah 2.0.3  
     Using mail 2.6.4  
     Using listen 3.0.8  
     Using rails-dom-testing 2.0.2  
     Using globalid 0.3.7  
     Using activemodel 5.0.2  
     Using jbuilder 2.6.3  
     Using spring 2.0.1  
     Using rails-html-sanitizer 1.0.3  
     Using activejob 5.0.2  
     Using activerecord 5.0.2  
     Using spring-watcher-listen 2.0.1  
     Using actionview 5.0.2  
     Using actionpack 5.0.2  
     Using actioncable 5.0.2  
     Using actionmailer 5.0.2  
     Using railties 5.0.2  
     Using sprockets-rails 3.2.0  
     Using coffee-rails 4.2.1  
     Using jquery-rails 4.2.2  
     Using web-console 3.4.0  
     Using rails 5.0.2  
     Using sass-rails 5.0.6  
     Bundle complete! 15 Gemfile dependencies, 62 gems now installed.  
     Use `bundle show [gemname]` to see where a bundled gem is installed.  
          run bundle exec spring binstub --all  
     * bin/rake: spring inserted  
     * bin/rails: spring inserted  
     prawnhead@ubuntu:~$  
    

  3. Change into the folder of your new Rails app:
     prawnhead@ubuntu:~$ cd app  
     prawnhead@ubuntu:~/app$  
    

  4. Create a scaffold:
     prawnhead@ubuntu:~/app$ rails generate scaffold Item name:string order:integer  
     Running via Spring preloader in process 8914  
        invoke active_record  
        create  db/migrate/20170306223702_create_items.rb  
        create  app/models/item.rb  
        invoke  test_unit  
        create   test/models/item_test.rb  
        create   test/fixtures/items.yml  
        invoke resource_route  
         route  resources :items  
        invoke scaffold_controller  
        create  app/controllers/items_controller.rb  
        invoke  erb  
        create   app/views/items  
        create   app/views/items/index.html.erb  
        create   app/views/items/edit.html.erb  
        create   app/views/items/show.html.erb  
        create   app/views/items/new.html.erb  
        create   app/views/items/_form.html.erb  
        invoke  test_unit  
        create   test/controllers/items_controller_test.rb  
        invoke  helper  
        create   app/helpers/items_helper.rb  
        invoke   test_unit  
        invoke  jbuilder  
        create   app/views/items/index.json.jbuilder  
        create   app/views/items/show.json.jbuilder  
        create   app/views/items/_item.json.jbuilder  
        invoke assets  
        invoke  coffee  
        create   app/assets/javascripts/items.coffee  
        invoke  scss  
        create   app/assets/stylesheets/items.scss  
        invoke scss  
        create  app/assets/stylesheets/scaffolds.scss  
    

  5. Create the backing database:
     prawnhead@ubuntu:~/app$ rake db:create  
     Created database 'db/development.sqlite3'  
     Created database 'db/test.sqlite3'  
     prawnhead@ubuntu:~/app$  
    

  6. Push the model for the new scaffold into the new database:
     prawnhead@ubuntu:~/app$ rake db:migrate  
     == 20170306223702 CreateItems: migrating ======================================  
     -- create_table(:items)  
       -> 0.0013s  
     == 20170306223702 CreateItems: migrated (0.0014s) =============================  
     prawnhead@ubuntu:~/app$   
    

  7. Start the server:
     prawnhead@ubuntu:~/app$ rails server  
     => Booting Puma  
     => Rails 5.0.2 application starting in development on http://localhost:3000  
     => Run `rails server -h` for more startup options  
     Puma starting in single mode...  
     * Version 3.7.1 (ruby 2.4.0-p0), codename: Snowy Sagebrush  
     * Min threads: 5, max threads: 5  
     * Environment: development  
     * Listening on tcp://localhost:3000  
     Use Ctrl-C to stop  
    

  8. Open a web browser to http://localhost:3000/items
OK. Have a play with this. You now have a database for storing Items. Each Item is currently described by a Name (string) and an Index (integer). Using the web pages created for you by the Item scaffold, you can CRUD (create, read, update, delete) Items. Go on, CRUD some Items.

But you wouldn't expect a microcontroller project to deal with web pages, would you? Hint: no, you wouldn't. How would your Arduino, Raspberry Pi or whatever project interact with this service? Using JSON, of course! JSON is JavaScript Object Notation and a way to communicate objects between systems using plain old text. If you create a few Items using the web pages and then jump into a new Terminal window, you can do this (it also works in a web browser of course):
 prawnhead@ubuntu:~/app$ curl http://localhost:3000/items.json  
 [{"id":1,"name":"One","order":1,"created_at":"2017-03-06T23:26:44.588Z","updated_at":"2017-03-06T23:26:44.588Z","url":"http://localhost:3000/items/1.json"},{"id":2,"name":"Two","order":2,"created_at":"2017-03-06T23:28:11.817Z","updated_at":"2017-03-06T23:28:11.817Z","url":"http://localhost:3000/items/2.json"},{"id":3,"name":"Three","order":3,"created_at":"2017-03-06T23:28:20.001Z","updated_at":"2017-03-06T23:28:20.001Z","url":"http://localhost:3000/items/3.json"}]  
 prawnhead@ubuntu:~/app$   

And if you want to get just one object you can do this:
 prawnhead@ubuntu:~/app$ curl http://localhost:3000/items/1.json  
 {"id":1,"name":"One","order":1,"created_at":"2017-03-06T23:26:44.588Z","updated_at":"2017-03-06T23:26:44.588Z","url":"http://localhost:3000/items/1.json"}prawnhead@ubuntu:~/app$   

What if your microcontroller code wants to create an Item? It just needs to POST some JSON back to the server. But we need to correctly configure a security feature to make this work. Find the file ~/app/app/controllers/application_controller.rb and edit it. Add a line to it so it looks like this, then save and close the file:
 class ApplicationController < ActionController::Base  
  protect_from_forgery with: :exception  
  protect_from_forgery unless: -> { request.format.json? }  
 end  

Now at the command prompt, try this command:
 prawnhead@ubuntu:~/app$ curl -v -H "Accept: application/json" -H "Content-type: application/json" -X POST -d '{"name":"test", "order":99}'  http://localhost:3000/items
Note: Unnecessary use of -X or --request, POST is already inferred.  
 *  Trying 127.0.0.1...  
 * Connected to localhost (127.0.0.1) port 3000 (#0)  
 > POST /items HTTP/1.1  
 > Host: localhost:3000  
 > User-Agent: curl/7.47.0  
 > Accept: application/json  
 > Content-type: application/json  
 > Content-Length: 27  
 >   
 * upload completely sent off: 27 out of 27 bytes  
 < HTTP/1.1 201 Created  
 < X-Frame-Options: SAMEORIGIN  
 < X-XSS-Protection: 1; mode=block  
 < X-Content-Type-Options: nosniff  
 < Content-Type: application/json; charset=utf-8  
 < Location: http://localhost:3000/items/6  
 < ETag: W/"b410b62608b84277f1d9cc69f755ea53"  
 < Cache-Control: max-age=0, private, must-revalidate  
 < X-Request-Id: efe98ce0-b59b-45b3-8fe1-da9ada75fa93  
 < X-Runtime: 0.014341  
 < Transfer-Encoding: chunked  
 <   
 * Connection #0 to host localhost left intact  
 {"id":6,"name":"test","order":99,"created_at":"2017-03-08T02:22:22.456Z","updated_at":"2017-03-08T02:22:22.456Z","url":"http://localhost:3000/items/6.json"}  
 prawnhead@ubuntu:~/app$   

If everything worked as planned, you now have a new item in your database and it's visible on the items web page:
Awesome. So now you have a web server that can interact directly with your microcontroller projects. It's standards-based using the REST interface and uses JSON as an object exchange protocol. We'll see in future posts that adding code to a microcontroller project to communicate with this server isn't hard. And how much coding did we do? One line. We literally added only one line to application_controller.rb to make it all work. This is what Rails calls "configuration over code". We only configured the app, the Rails framework created all the code for us.

Aside: modern programming is, at least to me, usually a case of finding a framework that does most of what you want as easily as possible. The programmer's job is not so much to learn the language but to learn the framework and master only so much of the language as is required to successfully copy and paste snippets of code from Stack Overflow. This is anathema to me.

Think about that.

If I would much rather build a database server and a web server from scratch, why am I using Ruby on Rails? After all, this is the very reason I studied Computer Science; so I could "build computers from sand". Seriously, if you can manufacture silicon from sand, then dope it to make transistors, then increase the density of transistors to create integrated circuits, then architect a microprocessor, then design and write an operating system, yaddah, yaddah, yaddah. I don't know all there is to know, but I know enough about the fundamentals to see how it all works. And that's my fascination with computers. Not the building of web sites using the latest fashionable framework, but the writing of code, the mastery of a language, and the direct interaction with the real world. Hey, it's almost a mission statement for working with microcontrollers.

So why Rails? Well, for precisely the reason that I hate it. It's a framework I barely know that sits on a language that's new to me and it completely abstracts away major components like the web server and database. I need to learn in this kind of environment. My want is to tear it all apart and see how it works inside. But really I just need the functionality. I'm training myself not to look behind the curtain. To keep it all as "magic".

If you want to see behind the curtain of Ruby on Rails I suggest you learn about:

That is all.

Comments