What are we here for? To see the power of Ruby on Rails! Without further ado:
- You must be at a Terminal on the machine where you have Rails installed
- 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:~$
- Change into the folder of your new Rails app:
prawnhead@ubuntu:~$ cd app prawnhead@ubuntu:~/app$
- 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
- Create the backing database:
prawnhead@ubuntu:~/app$ rake db:create Created database 'db/development.sqlite3' Created database 'db/test.sqlite3' prawnhead@ubuntu:~/app$
- 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$
- 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
- Open a web browser to http://localhost:3000/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:
- REpresentational State Transfer (REST)
- JavaScript Object Notation (JSON)
- The Ruby programming language
- Ruby on Rails
That is all.
This comment has been removed by the author.
ReplyDelete