Survey
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Notes on Ruby on Rails Created 11/02/09 Updated 11/10/10, Updated 02/06/11, Updated 09/15/11, Updated 03/22/12, Updated 04/06/12, Updated 05/20/12 Updated 06/25/12, Updated 07/14/12, Updated 08/12/12, Updated 12/16/12, Updated 01/27/13, Updated 03/16/13 Updated 07/03/13, Updated 09/25/13, Updated 11/19/13, Updated 01/02/14, Updated 01/22/14, Updated 02/16/14 Updated 04/21/14, Updated 05/26/14, Updated 06/08/14, Updated 07/28/14, Updated 09/08/14, Updated 11/05/14 Updated 12/13/14, Updated 02/09/15 http://www.codebeerstartups.com/2013/04/must-have-gems-for-development-machine-in-ruby-on-rails/ Introduction ...............................................................................................................................................................2 Concepts ....................................................................................................................................................................3 Installating Rails ........................................................................................................................................................5 Background on the Ruby Language ...........................................................................................................................5 Starting a new Ruby on Rails Project ........................................................................................................................9 Development Tools.................................................................................................................................................. 11 Running the Application .......................................................................................................................................... 12 Creating Models, Views, and Controllers ................................................................................................................ 12 Using Rake .............................................................................................................................................................. 12 Using RVM ............................................................................................................................................................. 13 From Static Pages to Dynamic Pages ...................................................................................................................... 13 Using ActiveRecord for Persistence ........................................................................................................................ 13 Using the Rails Console........................................................................................................................................... 20 Layouts and Rendering in Rails ............................................................................................................................... 20 Asset Pipeline .......................................................................................................................................................... 22 Writing Controllers .................................................................................................................................................. 24 Routing .................................................................................................................................................................... 25 Common Code (i.e., Helpers) .................................................................................................................................. 27 Resources ................................................................................................................................................................. 27 Using Git with Rails ................................................................................................................................................ 29 Using Gravatars ....................................................................................................................................................... 29 Further details of implementing User Management ................................................................................................ 30 Session Management ............................................................................................................................................... 31 Caching (improved in Rails 4) ................................................................................................................................. 32 Testing within Rails ................................................................................................................................................. 32 Debugging ............................................................................................................................................................... 32 Pagination ................................................................................................................................................................ 33 Using Modules ......................................................................................................................................................... 33 Rack ......................................................................................................................................................................... 34 Performance Testing (improved in Rails 4) ............................................................................................................. 34 Using RSpec for Tests ............................................................................................................................................. 34 Caching (new in 4.0) ............................................................................................................................................... 34 Turbolinks (new in 4.0) ........................................................................................................................................... 34 Declarative eTags (new in 4.0) ................................................................................................................................ 35 Engines .................................................................................................................................................................... 35 Observations and Open Issues ................................................................................................................................. 35 Appendix A: Chronology ....................................................................................................................................... 36 Appendix C: Sample Programs............................................................................................................................... 37 Appendix C: Gems of Interest to Try out ............................................................................................................... 38 Appendix D: Devise Gem ....................................................................................................................................... 38 Appendix E: CanCan Gem ..................................................................................................................................... 40 Appendix F: Paperclip Gem ................................................................................................................................... 44 Appendix G: Squeel Gem ....................................................................................................................................... 45 Appendix H: Rails 12factor Gem ........................................................................................................................... 48 Appendix I: Fog Gem ............................................................................................................................................. 48 Appendix J: Tagging Gem ...................................................................................................................................... 49 Appendix K: TheRubyRacer Gem .......................................................................................................................... 49 Page 1 Introduction Ruby on Rails, often shortened to Rails or RoR, is an open source full-stack web application framework for the Ruby programming language. Rails is a full-stack framework, meaning that it gives the Web developer the full ability to gather information from the web server, talking to or querying the database, and template rendering out of the box. As a result, Rails features a routing system that is independent of the Web server. The current version is 4.2.0, released December 2014. The 4.2.x versions started in December 2014. A prior current version was 4.1.8, released November 17, 2014. The 4.1.x versions started in April 2014. A prior current version was 4.0.6, released in June 2014. The 4.0.x versions started in mid-summer 2013. A prior current version was 3.2.14, released July 23, 2013. The 3.2.x versions started early 2012. Early versions of Rails go back to 2004. Reception In 2011, Gartner Research noted that despite criticisms and comparisons to Java, many high-profile consumer web firms are using Ruby on Rails to build agile, scalable web applications. Some of the largest sites running Ruby on Rails include GitHub, Scribd, Groupon, and Basecamp. As of February 2012, it was estimated that over 235,000 web sites were running Ruby on Rails. Resources The development and distribution site for Rails is http://rubyonrails.org. The most useful startup guide has been at http://guides.rubyonrails.org/getting_started.html Here is a reference to Ruby Syntax: http://zenspider.com/Languages/Ruby/QuickRef.html The RailsCasts series has been very useful: http://railscasts.com/ “Ruby on Rails 4 Tutorial: Learn Rails by Example” by Michael Hartl. Now in pre-press. The text is available online at http://ruby.railstutorial.org/ruby-on-rails-tutorial-book. “Crafting Rails 4 Applications: Expert Practices for Everyday Rails Development Paperback” by Jose Valim. Pragmatic Programmer’s Press, November 2013, 200 pages. List price $36.00. Not yet released or reviewed. “Agile Web Development with Rails 4” by Sam Ruby, Dave Thomas, and David Heinemeier Hansson. Pragmatic Programmer’s Press, October 2013, 500 pages. List price $43.95, Amazon price $23.97. Rated 4.5 stars on Amazon.com (from 2 reviews). We have bought this as an eBook. “Ruby on Rails 3 Tutorial (second edition): Learn Rails by Example” by Michael Hartl. Addison-Wesley, August 2012, 600 pages. Amazon price $29.98. Rated 4.5 stars on Amazon.com. Update to a well-regarded book, now being replaced by the Rails 4 version (above). The text is also available at http://ruby.railstutorial.org/ruby-on-railstutorial-book. “Ruby and MongoDB Web Development” by Gautham Rege. Packt Publishing, July 2012, 332 pages. List price $44.99, Amazon price $40.49, used from $27.00. Rated 3 stars on Amazon.com. A discussion of how to develop using frameworks on top of Ruby (i.e., Rails and Sinatra), to write an application whose state is maintained in MongoDB. Comments indicated that it wasn’t clearly-written or well-organized, however, “Learning Rails 3” by Simon St. Laurent, Edd Dumbill, Eric J Gruber. O’Reilly Press, July 2012, 414 pages. Rated 4.5 stars on Amazon.com (from 8 reviews). The description says that this book covers Rails from the web site view back toward the programming model. This is unique, because it allows you to get web sites and their visual design started and understood first, then starts covering concepts of Model/View/Controller. “Rails 3 in Action” by Ryan Bigg and Yehuda Katz. Manning Press, September 2011, 592 pages. List price $49.99, Amazon price $31.67, used from $25.62. Rated 3.5 stars on Amazon.com. Covers Rails 3.1.x. Reviews indicated Page 2 that it had more errors than usual for a Manning title, however, the content seemed to be well organized and had more material on testing than some of the other books. Available on City of Palo Alto library site. “Agile Web Development with Rails, fourth edition” by Sam Ruby, Dave Thomas, and David Heinemeier Hansson. Pragmatic Programmer’s Press, March 2011, 480 pages. List price $43.95, Amazon price $25.93, used from $23.47. Rated 3 stars on Amazon.com. Earlier editions of this book provided the first introductions on Ruby on Rails, going back to about 2005. Check which edition you are looking at before buying. “Ruby on Rails 3 Way (Second Edition)” by Obie Fernandez. Addison-Wesley, December 2010, 768 pages. List price $49.99, Amazon price $28.56, used from $21.89. Rated 3.5 stars on Amazon.com. It literally covers the “way” to do almost everything with Rails. Quite a large book, organized like a “how-to” book. Not quite a reference manual, nor is it a tutorial. Installing Rails 4 on a Mac OSX computer https://www.evernote.com/shard/s442/sh/f0463d3b-6c17-46e2-a0af-5992c4f2db66/b330815cc2b70615a559c07d29ba0205 Concepts Like many web frameworks, Ruby on Rails uses a variant of the Model/View/Controller (MVC) architecture pattern to organize application programming. In a default configuration, a model in a Ruby on Rails framework maps to a table in a database. By convention, a model named User will map to the database table users, and the model will have a filename user.rb within app/models. While developers can choose to use whatever model name and database table name they wish, this is not a common practice and it's usually discouraged because Rails philosophy is to use convention over configuration. A controller is the component of Rails that responds to external request from the web server to the application, and respond to the external request by determining which view file to render. The controller may also have to query the model directly for information and pass these onto the view. A controller may contain one or more actions. In Ruby on Rails, action is typically a basic unit which describes a single rule on how to respond to a specific external webbrowser request. Also note that, if a controller/action is not mapped to the Rails router, the controller/action will be directly inaccessible to external web requests. By convention, Rails encourage developers to use a RESTful route which contain actions named create, new, edit, update, destroy, and index, as these are routed automatically by convention in the routes file if specified. A view typically is a erb file in the default configuration of Rails. It is typically converted to output html at run-time, although in theory any format can be used as a view. Ruby on Rails includes tools that make common development tasks easier "out of the box", such as scaffolding that can automatically construct some of the models and views needed for a basic website. Also included are WEBrick, a simple Ruby web server that is distributed with Ruby, and Rake, a build system, distributed as a gem. Together with Ruby on Rails these tools provide a basic development environment. Page 3 Ruby on Rails relies on a web server to run it. WEBrick is most common at the time of writing, but it can also be run by Lighttpd, Apache, Cherokee, Hiawatha, nginx (either as a module - Passenger for example - or via CGI, FastCGI or mod_ruby), and many others. From 2008 onwards, the Passenger web server replaced Mongrel as the most-used web server for Ruby on Rails. Ruby on Rails is also noteworthy for its extensive use of JavaScript such as jQuey. Ruby on Rails initially utilized lightweight SOAP for web services; this was later replaced by RESTful web services. Since version 2.0, Ruby on Rails by default offers both HTML and XML as output formats. The latter is the facility for RESTful web services. The server uses embedded ruby in the HTML views with files having an html.erb extension. Other templating methods are available such as HAML which removes much of the normal page 'clutter'. For instance it uses a format in which indenting indicates the DOM nesting, and many of the syntactic elements of HTML are reduced. Ruby on Rails 3.2 has been designed to work with Ruby 1.9.3, 1.9.2, and 1.8.7. Rails 3.2 deprecates plugins (Rails 4.0 will remove them completely) so find Ruby gems to replace any plugins you may still be using. Ruby on Rails 4.0 and later has been designed to work with Ruby 2.0.0 or 1.9.3. Framework structure Ruby on Rails is separated into various packages, namely ActiveRecord (an object-relational mapping system for database access), ActiveModel (the data representation system extended by ActiveRecord), ActiveResource (provides web services), ActionPack, ActiveSupport and ActionMailer. Deployment Ruby on Rails is often installed using RubyGems, a package manager which is included with current versions of Ruby. Many Linux distributions also support installation of Ruby on Rails and its dependencies through their native package management system. Ruby on Rails is typically deployed with a database server such as MySQL or PostgreSQL, and a web server such as Apache running the Phusion Passenger module. There are many Ruby on Rails hosting services such as Heroku, Engine Yard, and Rails Playground. See our document “Notes on Heroku”. New Features in Rails 4.2 Active Job asynchronous emails Adequate Record Web Console foreign keys New Features in Rails 4.1 Spring application preloader - Spring is a Rails application preloader. It speeds up development by keeping your application running in the background so you don't need to boot it every time you run a test, rake task or migration config/secrets.yml - Rails 4.1 generates a new secrets.yml file in the config folder. By default, this file contains the application's secret_key_base, but it could also be used to store other secrets such as access keys for external APIs. The secrets added to this file are accessible via Rails.application.secrets. Action Pack variants - We often want to render different HTML/JSON/XML templates for phones, tablets, and desktop browsers. Variants make it easy. The request variant is a specialization of the request format, like :tablet, :phone, or :desktop. Page 4 Action Mailer previews - Action Mailer previews provide a way to visually see how emails look by visiting a special URL that renders them. Active Record enums - declare an enum attribute where the values map to integers in the database, but can be queried by name. New Features in Rails 4.0 It's an amazing new version packed with new goodies and farewells to old features past their expiration date. A big focus has been on making it dead simple to build modern web applications that are screaming fast without needing to go the client-side JS/JSON server route. Much of this work was pioneered for Rails in the new version of Basecamp and focuses on three aspects: See http://railscasts.com/episodes/400-what-s-new-in-rails-4?view=asciicast. A good guide for upgrading is located at http://edgeguides.rubyonrails.org/upgrading_ruby_on_rails.html Specific changes: Strong parameters ActiveModel::Model Russian Doll caching TurboLinks Queue API Installation of Rails See http://railsinstaller.org/ We used this to generate our system. It provided Ruby 1.9.3 and Rails 3.2.14, which we have now updated to Rails 4.1.8. Background on the Ruby Language Syntax Statements don’t need semicolons. Comments begin with # Page 5 Block comments: =begin Every body mentioned this way to have multiline comments. It has to be at the beginning of a line or it will be a syntax error. =end Variables These don’t need to be declared, or have any special syntax marker (this is an improvement over PHP where they had to begin with $). Variables are not typed. Flow of control There is an if/then/else facility There is a loop construct: item_list.each do |item| puts "current_index: #{index} end Notice that the do/end pair is defining a code block. Another form of this loop allows the block to get the loop index (0…n): itemList.each_with_index do |item, index| puts "current_index: #{index} end Objects and Message Passing Everything in Ruby, including strings and even nil, is an object. It’s easier to describe what objects do, which is respond to messages. An object like a string, for example, can respond to the message length, which returns the number of characters in the string: >> "foobar".length # Passing the "length" message to a string => 6 Typically, the messages that get passed to objects are methods, which are functions defined on those objects. Method Definitions The console allows us to define methods the same way we did with the home action (defining methods in the console is a bit cumbersome, and ordinarily you would use a file, but it’s convenient for demonstration purposes). For example, let’s define a function string_message that takes a single argument and returns a message based on whether the argument is empty or not: Page 6 >> def string_message(string) >> if string.empty? >> "It's an empty string!" >> else >> "The string is nonempty." >> end >> end => nil >> puts string_message("") It's an empty string! >> puts string_message("foobar") The string is nonempty. Note that Ruby functions have an implicit return, meaning they return the last statement evaluated—in this case, one of the two message strings, depending on whether the method’s argument string is empty or not. Ruby also has an explicit return option; the following function is equivalent to the one above: >> def string_message(string) >> return "It's an empty string!" if string.empty? >> return "The string is nonempty." >> end The alert reader might notice at this point that the second return here is actually unnecessary—being the last expression in the function, the string "The string is nonempty." will be returned regardless of the return keyword, but using return in both places has a pleasing symmetry to it. Arrays and Ranges An array is just a list of elements in a particular order. Understanding them gives a good foundation for understanding hashes and for aspects of Rails data modeling (such as the has_many association which can be defined in a model file). So far we’ve spent a lot of time understanding strings, and there’s a natural way to get from strings to arrays using the split method: >> "foo bar baz".split # Split a string into a three-element array => ["foo", "bar", "baz"] The result of this operation is an array of three strings. Ranges are subparts of an array. Arrays can be created by setting a variable to [] and built using the array.append() method. Blocks Both arrays and ranges respond to a host of methods that accept blocks (similar to closures in other languages), which are simultaneously one of Ruby’s most powerful and most confusing features: Page 7 >> (1..5).each { |i| puts 2 * i } 2 4 6 8 10 => 1..5 This code calls the each method on the range (1..5) and passes it the block { |i| puts 2 * i }. The vertical bars around the variable name in |i| are Ruby syntax for a block variable, and it’s up to the method to know what to do with the block; in this case, the range’s each method can handle a block with a single local variable, which we’ve called i, and it just executes the block for each value in the range. Modules (“packages” in Java) Modules have many powerful features, and one of them is that they are namespaces. This means that names defined inside a module are hidden from outside the module. To see what I mean, you need to know that one of the things you can do inside a module is define another module. So, for example: module Animals module Dog def self.bark "bow wow" end end end Kernel.puts(Dog.bark) #=> NameError: uninitialized constant Dog From where we are in the last line, when we say Dog.bark, the name Dog is not visible. That’s because the name Dog is defined inside the Animals module. However, we are at the same level as the place where Animals was defined, so the name Animals is visible. Now, we can reach the name Dog, if we want to; we just can’t do it directly, because it isn’t directly visible. Instead, we have to do it by way of the name Animals, which is directly visible. The “by way of” operator is two colons (::). Ruby Classes We’ve said before that everything in Ruby is an object, and in this section we’ll finally get to define some of our own. Ruby, like many object-oriented languages, uses classes to organize methods; these classes are then instantiated to create objects. If you’re new to object-oriented programming, this may sound like gibberish, so let’s look at some concrete examples. We’ve seen lots of examples of using classes to instantiate objects, but we have yet to do so explicitly. For example, we instantiated a string using the double quote characters, which is a literal constructor for strings. A variable whose name begins with '@' is an instance variable of self. An instance variable belongs to the object itself. Uninitialized instance variables have a value of nil. Conventions A number of naming conventions within class methods, such as using a ? at the end of a name for a method that returns a Boolean. Page 8 Modifying Built-In Classes While inheritance is a powerful idea, in the case of palindromes it might be even more natural to add the palindrome? method to the String class itself, so that (among other things) we can call palindrome? on a string literal, which we currently can’t do. Concerns Concerns are also a helpful way of extracting a slice of model that doesn’t seem part of its essence (what is and isn’t in the essence of a model is a fuzzy line and a longer discussion) without going full-bore Single Responsibility Principle and running the risk of ballooning your object inventory. Starting a new Ruby on Rails Project Use the “rails new <name>” command (or the “rails new <name> --database=postgresql” command). The resulting structure is shown below: Page 9 The default Gemfile is: source 'https://rubygems.org' # Use sqlite3 as the database for Active Record gem 'sqlite3' # Use SCSS for stylesheets gem 'sass-rails', '~> 4.0.0' # Use Uglifier as compressor for JavaScript assets gem 'uglifier', '>= 1.3.0' # Use CoffeeScript for .js.coffee assets and views gem 'coffee-rails', '~> 4.0.0' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails' # Turbolinks makes following links in your web application faster. # Read more: https://github.com/rails/turbolinks gem 'turbolinks' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder gem 'jbuilder', '~> 1.0.1' group :doc do # bundle exec rake doc:rails generates the API under doc/api. gem 'sdoc', require: false end # Use ActiveModel has_secure_password # gem 'bcrypt-ruby', '~> 3.0.0' # Use unicorn as the app server # gem 'unicorn' # Use Capistrano for deployment # gem 'capistrano', group: :development # Use debugger # gem 'debugger', group: [:development, :test] Using Bundler After creating a new Rails application, the next step is to use Bundler to install and include the gems needed by the app. This involves opening the Gemfile with your favorite text editor, then running the command: bundle install A similar command “bundle update” will update the versions of existing gems. Gems are located in the Ruby193/lib/ruby/gems/1.9.3/gems directory. This is managed by the bundle system. Version specification in the Gemfile Most of the version specifiers, like >= 1.0, are self-explanatory. The specifier ~> has a special meaning, best shown by example: ~> 2.0.3 is identical to >= 2.0.3 and < 2.1. ~> 2.1 is identical to >= 2.1 and < 3.0. ~> 2.2.beta will match prerelease versions like 2.2.beta.12. Page 10 Other suggestions on project creation The Hartl book suggests renaming README.rdoc to README.md. If you use the file extension .md then GitHub will automatically format it nicely for you. However, it appears that as of 2013, GitHub can handle both types. Before creating the Git repo, if you are using JetBrains, update the IDE’s list of files to ignore (this is on the project settings), and the .gitignore file itself. Write the Git commit messages in the present tense (i.e, “Improve the README file”. Git models commits as a series of patches, and in this context it makes sense to describe what each commit does, rather than what it did. Moreover, this usage matches up with the commit messages generated by Git commands themselves. See the GitHub post Shiny new commit styles for more information. Development Tools Now that we have dealt with simple changes at the file-system level, let’s load up a viewing/editing environment for multiple files. The Ruby on Rails 4 Tutorial book names two IDEs: RadRails and RubyMine. Both are crossplatform. Another IDE is Komodo, which gets good reviews. See http://www.activestate.com/komodo-ide There is Komodo Edit for free, and Professional for $295 (discounted to $245). Many Rails developers don’t use a full IDE, they just need a multi-file text editing tool, such as TextMate or Sublime Text 2. The latter can organize multiple text files into a project, and integrate with source control. The Ruby plugin for Eclipse You can also use Eclipse: Download and install Eclipse if you don’t already have it. Launch Eclipse, then locate and install the Ruby Development Tools plug-in using the Eclipse ‘Help/Install New Software’ menu option. At the Available Software dialog box: 1. Select –All Available Sites– for the ‘Work with’ drop down list. 2. Expand the Programming Languages section and select ‘Dynamic Languages Toolkit – Ruby Development Tools’. 3. Press the Next button, review the options, then select the Finish button. Use the IDE ‘Eclipse/Preferences’ menu option to pull up the application preferences dialog, then select ‘Ruby’ on the far right of the dialog. 1. Expand the options under ‘Ruby’ and select ‘Interpreters’. If none are displayed, select the ‘Search…’ button at the right side of the screen to locate any installed interpreters. Apple ships a ruby interpreter – in OSX 10.5.x I found them in /usr/bin/ruby. Now use the IDE ‘File/New/Ruby Project’ to create a new Ruby project. Name it to any convention you follow (type of project, class, etc.). NOTE: You want to use the Ruby Perspective when writing Ruby code, which is available using the IDE ‘Window/Open Perspective/Other’ option and selecting ‘Ruby from the list. Select ‘File/New/Ruby Class’ to create your first Ruby program. Name it hello, then enter this simple Hello World program to confirm you can create and run a Ruby program: class hello def hello puts “Hello Ruby World” end end Save and execute the file and verify you see the text string in the Eclipse console. Congratulations. You just created your first Ruby program using Eclipse. Page 11 Running the Application rails server - The default Rails web server is WEBrick, a pure-Ruby server that isn’t suitable for production use but is fine in development. This command is often abbreviated “rails s” The suggested web server for production is Unicorn. Creating Models, Views, and Controllers To get started with this Rails application, you generate a scaffold. Scaffolds in Rails provide a lot of basic functionality but are generally not used for full-scale development because you may want something more customized, in which case you’d build it yourself. But for this example of what Rails can do, let’s generate a scaffold by running this command: rails generate scaffold purchase name:string cost:float When you used the rails command earlier, it generated an entire Rails application. You can use this command inside of an application to generate a specific part of the application by passing the generate argument to the rails command, followed by what it is you want to generate. The scaffold command generates a model, a controller, and views based on the name passed after scaffold in this command. These are the three important parts needed for your purchase tracking. The model provides a way to interact with a database. The controller interacts with the model to retrieve and format its information and defines different actions to perform on this data. The views display the information from the controller in a neat format. Everything after the name for the scaffold are the fields for the database table and the attributes for the objects of this scaffold. Here you tell Rails that the table for your purchase scaffold will contain name and cost fields, which are a string and a float. To create this table, the scaffold generator generates what’s known as a migration. rails generate scaffold User name:string email:string To create an association (to another table/entity) use the data type “reference”. Then create an index to indicate what is being referred to. You can also generate just a model file, or just a controller (which will take a list of method names, generate a static view for each of them). rails generate model rails generate controller StaticPages home help_ Using Rake Rake is Ruby make, a make-like language written in Ruby. Rails uses Rake extensively, especially for the innumerable little administrative tasks necessary when developing database-backed web applications. The rake db:migrate command is probably the most common, but there are many others; you can see a list of database tasks using -T db: $ rake -T db To see all the Rake tasks available, run $ rake -T The most common command is rake db:migrate, others are rake db:seed, rake db:create, rake db:drop Page 12 Using RVM RVM is a command-line tool which allows you to easily install, manage, and work with multiple Ruby environments from interpreters to sets of gems. See http://rvm.io/ Using Gemsets One of the most powerful, useful and often overlooked features of RVM is the gemset. A gemset is a separate gem directory, so the gems in one gemset don't conflict with the gems in another gemset. A common usage would be to install Rails 2 in one gemset, and Rails 3 in another. Since they're in different gemsets, there's no possibility of them interfering with each other. From Static Pages to Dynamic Pages Rails has two main ways of making static web pages. First, Rails can handle truly static pages consisting of raw HTML files. Second, Rails allows us to define views containing raw HTML, which Rails can render so that the web server can send it to the browser. Every Rails application comes with a minimal working application thanks to the rails script, with a default welcome page at the address http://localhost:3000 To learn where this page comes from, take a look at the file public/index.html Because the file contains its own stylesheet information, it’s a little messy, but it gets the job done: by default, Rails serves any files in the public directory directly to the browser. In the case of the special index.html file, you don’t even have to indicate the file in the URL, as index.html is the default. As an introduction to MVC, we will change this simple app to use a controller to render the static page. The StaticPages controller generation automatically updates the routes file, called config/routes.rb, which Rails uses to find the correspondence between URIs and web pages. This is our first encounter with the config directory which is where Rails collects files needed for the application configuration—hence the name. To pass dynamic content to the view, assign value to variables with “@” in front. These will be known to the view. Using ActiveRecord for Persistence The ActiveRecord pattern is defined as one where objects carry data & behavior, in this case the ability to persist themselves. ActiveRecord is an ORM mapping layer, similar to Hibernate. It allows you to create queries and updates without writing the actual SQL. It also handles caching and transaction management. It is probably more limited than Hibernate, but this makes it easier to learn and with less unexpected behavior. Models See http://guides.rubyonrails.org/association_basics.html for this information. These define the data objects and their relationships. You can add validations to your model to ensure that the data conforms to certain rules or that data for a certain field must be present or that a number you enter must be above a certain other number. Expressing Data Model Relationships These are defined by the following elements of the model files: has_many for a 1 to N relationship belongs_to Expressing Validation Conditions Page 13 Here is an example: validates :name, :presence => true, :length => { :minimum => 5 } The Query Interface See http://guides.rubyonrails.org/active_record_querying.html for this information There are a number of access methods available on a model class, including: find - by primary key first, last, all find in batches find multiple with ordering selecting only specific fields. For a query, you can chain together a number of where() clauses, or use findAll(). In fact you can conditionally chain the where clauses, as shown below: @challenges = Challenge.all @challenges = @challenges.where('name like ?', "%" + cfp[:name] + "%") if cfp[:name] && !cfp[:name].empty? @challenges = @challenges.where('challenge_type = ?', cfp[:challenge_type]) if cfp[:challenge_type] && !cfp[:challenge_type].empty? Though there is a syntactic facility called “dynamic finders” which is similar to Grails, it is being deprecated and will be removed in Rails 4.1. For example, instead of find_by_first_name(‘jack’) you should use find_by(first_name: ‘jack’) The locking options include optimistic (using a version field), and pessimistic (locking at the DB level) To carry out joins, you might specify Client.joins(“left outer join addresses on addresses.client_id = clients.id”) however this simply makes fields in addresses available to the where clause, it doesn’t add new fields to the Client objects returned. You can use eager loading of related objects (related in the sense of associations), by using the :includes(:address) specification on the query. What this will do is carry out a main query, and then a supporting query for the specified related object class, instead of carrying out 1+N queries (where N is the number of records returned in the first query). find_by_sql has a close relative called connection#select_all. select_all will retrieve objects from the database using custom SQL just like find_by_sql but will not instantiate them. Instead, you will get an array of hashes where each hash indicates a record. Client.connection.select_all("SELECT * FROM clients WHERE id = '1'") This facility is the closest within Rails to the ‘projections’ capability in Grails. Essentially this operation is grabbing the connection, in order to carry out ad-hoc operations on it. Finally, there is an ‘explain’ facility that will show the query plan for any given query interface call. Preload, Eagerload, Includes and Joins These examples from http://blog.bigbinary.com/2013/07/01/preload-vs-eager-load-vs-joins-vs-includes.html Rails provides four different ways to load association data. In this blog we are going to look at each of those. Preload: loads the association data in a separate query. Page 14 1 User.preload(:posts).to_a 2 3 # => 4 SELECT "users".* FROM "users" 5 SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (1) This is how includes loads data in the default case. Since preload always generates two sql we can’t use posts table in where condition. Following query will result in error. 1 User.preload(:posts).where("posts.desc='ruby is awesome'") 2 3 # => 4 SQLite3::SQLException: no such column: posts.desc: 5 SELECT "users".* FROM "users" WHERE (posts.desc='ruby is awesome') where clauses on users table will work fine. 1 User.preload(:posts).where("users.name='Neeraj'") 2 3 # => 4 SELECT "users".* FROM "users" WHERE (users.name='Neeraj') 5 SELECT "posts".* FROM "posts" WHERE "posts"."user_id" IN (3) Includes: loads the association data in a separate query in the default case just like preload. However it is smarter than preload. Above we saw that preload failed for query User.preload(:posts).where(“posts.desc=’ruby is awesome’”). Let’s try same with includes. 1 User.includes(:posts).where('posts.desc = "ruby is awesome"').to_a 2 3 # => 4 SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, 5 "posts"."title" AS t1_r1, 6 "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 7 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" 8 WHERE (posts.desc = "ruby is awesome") As you can see includes switches from using two separate queries to creating a single LEFT OUTER JOIN to get the data. And it also applies the supplied condition. Page 15 So includes changes from two queries to a single query on some cases. By default for a simple case it will use two queries. Let’s say that for some reason you want to force a simple includes case to use a single query instead of two. Use references to achieve that. 1 User.includes(:posts).references(:posts).to_a 2 3 # => 4 SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, 5 "posts"."title" AS t1_r1, 6 "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 7 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" In the above case a single query was done. Eager load: loads all association in a single query using `LEFT OUTER JOIN’. 1 User.eager_load(:posts).to_a 2 3 # => 4 SELECT "users"."id" AS t0_r0, "users"."name" AS t0_r1, "posts"."id" AS t1_r0, 5 "posts"."title" AS t1_r1, "posts"."user_id" AS t1_r2, "posts"."desc" AS t1_r3 6 FROM "users" LEFT OUTER JOIN "posts" ON "posts"."user_id" = "users"."id" This is exactly what includes does when it is forced to make a single query when where or order clause is using an attribute from posts table. Joins: brings association data using inner join. 1 User.joins(:posts) 2 3 # => 4 SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" In the above case no posts data is selected. Above query can also produce duplicate result. To see it let’s create some sample data. 1 def self.setup 2 User.delete_all 3 Post.delete_all 4 5 u = User.create name: 'Neeraj' 6 u.posts.create! title: 'ruby', desc: 'ruby is awesome' Page 16 7 u.posts.create! title: 'rails', desc: 'rails is awesome' 8 u.posts.create! title: 'JavaScript', desc: 'JavaScript is awesome' 9 10 u = User.create name: 'Neil' 11 u.posts.create! title: 'JavaScript', desc: 'Javascript is awesome' 12 13 u = User.create name: 'Trisha' 14 end With the above sample data if we execute User.joins(:posts) then this is the result we get 1 #<User id: 9, name: "Neeraj"> 2 #<User id: 9, name: "Neeraj"> 3 #<User id: 9, name: "Neeraj"> 4 #<User id: 10, name: "Neil"> We can avoid the duplication by using distinct . 1 User.joins(:posts).select('distinct users.*').to_a Also if we want to make use of attributes from posts table then we need to select them. 1 records = User.joins(:posts).select('distinct users.*, posts.title as posts_title').to_a 2 records.each do |user| 3 puts user.name 4 puts user.posts_title 5 end Note that using joins means if you use user.posts then another query will be performed. Setting up Database Connections While most Rails programs use SQLite by default, we have been using the ‘mysql2’ adapter or the ‘pg’ adapter, for the MySQL and PostgreSQL database respectively. The parameters for the database configuration go into database.yml, and are very standard fields for setting up a MySQL connection. Migrations Migrations are a convenient way for you to alter your database in a structured and organized manner. You could edit fragments of SQL by hand but you would then be responsible for telling other developers that they need to go and run them. You’d also have to keep track of which changes need to be run against the production machines next time you deploy. Active Record tracks which migrations have already been run so all you have to do is update your source and run rake db:migrate. Active Record will work out which migrations should be run. It will also update your db/schema.rb file to match the structure of your database. Page 17 Migrations also allow you to describe these transformations using Ruby. The great thing about this is that (like most of Active Record’s functionality) it is database independent: you don’t need to worry about the precise syntax of CREATE TABLE any more than you worry about variations on SELECT * (you can drop down to raw SQL for database specific features). For example you could use SQLite3 in development, but MySQL in production. Here is an example: class CreateProducts < ActiveRecord::Migration def up create_table :products do |t| t.string :name t.text :description t.timestamps end end def down drop_table :products end end This creates the table and defines the fields. The t.timestamps creates the date_created and last_updated fields. Active Record supports the following database column types: :binary :boolean :date :datetime :decimal :float :integer :primary_key :string :text :time :timestamp These will be mapped onto an appropriate underlying database type. For example, with MySQL the type :string is mapped to VARCHAR(255). You can create columns of types not supported by Active Record, but these will be database-specific. To create a migration file, use the command “rails generate migration <migrationName>”. An example name would be “ChangeDataTypeForStartsAt”. This will create a migration class ChangeDataTypeForStartsAt. Then edit the class to define the “up” (at leasts) and the “down” (optional) method. Here is an example: class ChangeDataTypeForStartsAt < ActiveRecord::Migration def self.up change_table :challenges do |t| t.change :startsAt, :datetime end end def self.down change_table :challenges do |t| t.change :startsAt, :date end end end Apply the migration using the command “rake db:migrate”. Page 18 Occasionally you will make a mistake when writing a migration. If you have already run the migration then you cannot just edit the migration and run the migration again: Rails thinks it has already run the migration and so will do nothing when you run rake db:migrate. You must rollback the migration (for example with rake db:rollback), edit your migration and then run rake db:migrate to run the corrected version. In general editing existing migrations is not a good idea: you will be creating extra work for yourself and your coworkers and cause major headaches if the existing version of the migration has already been run on production machines. Instead, you should write a new migration that performs the changes you require. Editing a freshly generated migration that has not yet been committed to source control (or, more generally, which has not been propagated beyond your development machine) is relatively harmless. Defining Indexes in the Schema class CreateRelationships < ActiveRecord::Migration def change create_table :relationships do |t| t.integer :follower_id t.integer :followed_id t.timestamps end add_index :relationships, :follower_id add_index :relationships, :followed_id add_index :relationships, [:follower_id, :followed_id], unique: true end end Non-Database Models In Rails 3 and later the non-database functionality of Active Record is extracted out into Active Model. This allows you to cleanly add validations and other features to tableless models. See http://railscasts.com/episodes/219-activemodel. Here is an example: class ChallengeFilter extend ActiveModel::Naming include ActiveModel::AttributeMethods include ActiveModel::Conversion attr_accessor :name attr_accessor :challenge_type def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end def persisted? false end end Virtual Attributes Keep your controllers clean and forms flexible by adding virtual attributes to your model. This very powerful technique allows you to create form fields which may not directly relate to the database. We can create this by defining a virtual attribute for full_name. class User < ActiveRecord::Base # Getter def full_name [first_name, last_name].join(' ') end Page 19 # Setter def full_name=(name) split = name.split(' ', 2) self.first_name = split.first self.last_name = split.last end end The getter and setter methods defined in the User model. The getter method gets the first_name and last_name values for the user and returns them, joined by a space. The setter method splits the passed value at the first space and sets the first_name and last_name to be the first and second parts of the split string. Using virtual properties means that the user interface doesn’t have to have a field to map to each field in the corresponding database table. This is especially useful when you’re using Rails to connect to a legacy database and can’t alter the fields in its tables. Using the Rails Console Since Ruby is interpreted, you can interactively use it through a console. Type “rails c” to start the console. Layouts and Rendering in Rails The view rendering facilities within Rails support the following: Having a master layout that all views use Create layouts with multiple content sections Use partials to DRY up your views Use nested layouts (sub-templates) A view is an ERB file, which allows you to mix HTML and Ruby code to generate dynamic pages. The beginning of an ERB tag indicates that the result of the code inside the tag will be output to the page. If you want the code to be evaluated but not output, you use the <% tag, like this: <% some_variable = "foo" %> If you were to use <%= some_variable = "foo" %> here, the some_variable variable would be set and the value output to the screen. By using <% %> instead, the Ruby code is evaluated but not output. The render method, when passed a string as in this example, renders a partial. A partial is a separate template file that you can include in other templates to repeat similar code. By convention these begin with an underscore.. The link_to method generates a link with the text of the first argument (Back) and with an href attribute specified by the second argument. There are several ways to specify the path: Use a controller name and an action name. The benefit here is flexibility: you can reference any controller and action. Use a defined constant such as edit_purchases_path which is generated by the routing file processor. The benefit here is that this constant can be checked at compile time. Expressions commonly used inside Views Here are the link generators: <%= link_to "Home",'#' %> <%= button_to "Home",'#' %> The most comprehensive and advanced set are called “FormHelpers”. These include: Page 20 <%= form_tag :url => ‘update’ %> <contents> <%= f.submit %> <% end %> <%= form_for(@treasure) do |f| %> <contents> <%= f.submit %> <% end %> Here are the form field editor generators: <%= <%= <%= <%= <%= <%= <%= f.text_field :email %> f.text_area :description %> f.number_field :score_points %> f.check_box :locked %> f.collection_select :location_id, Hex.order(:id), :id, :get_label %> f.date_select :starts_at %> f.datetime_select :bought_at %> Within any view, you can also have if/then/else, and looping constructs which use regular Ruby syntax, since an ERB file is really a Ruby script. The selection generation FormHelpers are worth describing in detail: select_tag(name, option_tags = nil, options = {}) Creates a dropdown selection box, or if the :multiple option is set to true, a multiple choice selection box. The second argument is typically a call to one of the “options_for…” functions described below. The third argument is a set of options from the following: :multiple - If set to true the selection will allow multiple choices. :disabled - If set to true, the user will not be able to use this input. :include_blank - If set to true, an empty option will be created. :prompt - Create a prompt option with blank value and the text asking user to select something Any other key creates standard HTML attributes for the tag. One of the following methods is typically used to create the options that are selectable: options_for_select(container, selected = nil) public Returns a string of option tags that have been compiled from a container (hash, array, enumerable, your type). Given a container where the elements respond to first and last (such as a two-element array), the “lasts” serve as option values and the “firsts” as option text. Hashes are turned into this form automatically, so the keys become “firsts” and values become lasts. If selected is specified, the matching “last” or element will get the selected option-tag. selected may also be an array of values to be selected when using a multiple select. options_from_collection_for_select(collection, value_method, text_method, selected = nil) public Returns a string of option tags that have been compiled by iterating over the collection and assigning the result of a call to the value_method as the option value and the text_method as the option text. Within a form_for, you can use: <%= f.select :challenge_type, ChallengeType.options, :prompt => "Type:" %> Page 21 Which performs all of the above functions, including emitting correct element name, generating the options, adding a prompt option, and indicating current value from specified field (:challengeType) of the given model (f). Partials Partial views are a way to have common fragments of multiple views shared. Partials are named files, which typically start with an underscore. To incorporate a partial, use a single call to a Rails helper called render: <%= render 'layouts/shim' %> The effect of this line is to look for a file called app/views/layouts/_shim.html.erb, evaluate its contents, and insert the results into the view. Asset Pipeline One of the most notable differences between Rails 3.0 and more recent versions is the asset pipeline, which significantly improves the production and management of static assets such as CSS, JavaScript, and images. This section gives a high-level overview of the asset pipeline and then shows how to use a remarkable tool for making CSS called Sass, now included by default as part of the asset pipeline. In current Rails, there are three canonical directories for static assets, each with its own purpose: app/assets: assets specific to the present application lib/assets: assets for libraries written by your dev team vendor/assets: assets from third-party vendors Manifest Files: Once you’ve placed your assets in their logical locations, you can use manifest files to tell Rails (via the Sprockets gem) how to combine them to form single files (this applies to CSS and JavaScript but not to images). This provides a way to reduce the download times. After you’ve assembled your assets, Rails prepares them for the site template by running them through several preprocessing engines and using the manifest files to combine them for delivery to the browser. Examples of the pre-processing includes CoffeeScript and Sass. Note: for deployments on Heroku, either add in the 12factor gem, or make the following change in config/environments/production.rb: # Enable Rails's static asset server (this is needed on Heroku) config.serve_static_assets = true About the Asset Pipeline The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages and pre-processors such as CoffeeScript, Sass and ERB. The asset pipeline is technically no longer a core feature of Rails 4, it has been extracted out of the framework into the sprockets-rails gem. The asset pipeline is enabled by default. You can disable the asset pipeline while creating a new application by passing the --skip-sprockets option. rails new appname --skip-sprockets Rails 4 automatically adds the sass-rails, coffee-rails and uglifier gems to your Gemfile, which are used by Sprockets for asset compression: gem 'sass-rails' gem 'uglifier' Page 22 gem 'coffee-rails' Using the --skip-sprockets option will prevent Rails 4 from adding sass-rails and uglifier to Gemfile, so if you later want to enable the asset pipeline you will have to add those gems to your Gemfile. Also, creating an application with the --skip-sprockets option will generate a slightly different config/application.rb file, with a require statement for the sprockets railtie that is commented-out. You will have to remove the comment operator on that line to later enable the asset pipeline: # require "sprockets/railtie" To set asset compression methods, set the appropriate configuration options in production.rb config.assets.css_compressor for your CSS and config.assets.js_compressor for your Javascript: config.assets.css_compressor = :yui config.assets.js_compressor = :uglify The sass-rails gem is automatically used for CSS compression if included in Gemfile and no config.assets.css_compressor option is set. Asset Tag Helpers Asset tag helpers provide methods for generating HTML that link views to feeds, JavaScript, stylesheets, images, videos and audios. There are six asset tag helpers available in Rails: auto_discovery_link_tag javascript_include_tag stylesheet_link_tag image_tag video_tag audio_tag You can use these tags in layouts or other views, although the auto_discovery_link_tag, javascript_include_tag, and stylesheet_link_tag, are most commonly used in the <head> section of a layout. The asset pipeline is compiled by default. You refer to assets using %= image_tag 'logo.png', :style => "border: 0px solid white" %> And background-image: image-url('portalPage.jpg'); Improvements to the Sprockets Gem in 4.0 Rails 4 automatically adds the sass-rails, coffee-rails and uglifier gems to your Gemfile, which are used by Sprockets for asset compression. If you are upgrading from Rails 3, please take into account that assets under lib/assets or vendor/assets are available for inclusion via the application manifests but no longer part of the precompile array. See Precompiling Assets for guidance. The config.assets.compress initialization option is no longer used in Rails 4 to enable either CSS or JavaScript compression. Setting it will have no effect on the application. Instead, setting config.assets.css_compressor and config.assets.js_compressor will control compression of CSS and JavaScript assets. Page 23 Rails 4 no longer sets default config values for Sprockets in test.rb, so test.rb now requies Sprockets configuration. The old defaults in the test environment are: config.assets.compile = true, config.assets.compress = false, config.assets.debug = false and config.assets.digest = false. Specifying ‘require’ in files processed by Sprockets One of the facilities provided by Sprockets is the ‘require’ directive, which is placed into a special comment syntax as follows: /* *= require_self *= require_tree . */ This will allow inclusion of other files. For instance “require_tree .” brings in all of the other css or scss files in the directory. There is a similar syntax for inclusion used in JavaScript files. In earlier versions of the Twitter Bootstrap 3 Gem, we would specify “require bootstrap”. However, the documentation of the gem recommended against this, in favor of writing an @import statement. The description for this is located at https://github.com/twbs/bootstrap-sass/issues/79#issuecomment-4428595 Sprockets compiles each file individually, so mixins or variables to set in one file do not work in another; hence, all the mixins and variables available via Bootstrap are not within the scope of your own files, so you can't use them. Import basically imports the files together and then they are compiled, allowing you to override and use Bootstrap. Starting with release 3.2.x of the gem, the @import approach must be used. Writing Controllers These handle the requests. New features in Rails 4.0: Mass assignment protection in Active Record models (GitHub, Pull Request) ActiveRecord::SessionStore (GitHub, Pull Request) Active Record Observers (GitHub, Commit) Active Resource (GitHub, Pull Request, Blog) Strong Parameters In the model classes, we used to have class Post < ActiveRecord::Base attr_accessible :title, :body validates :title, :presence => true, :length => { :minimum => 5 } has_many :comments, :dependent => :destroy end Now I have had to comment out the “attr_accessible” line. What these lines had been doing was creating something almost like getters and setters for the fields. In posts_controller, change: @post = Post.new(params[:post]) to: @post = Post.new(params.require(:post).permit(:title, :body)) The same is needed in the “update” method. What the new code does is call the equivalent of the setter methods for title and body. Apparently this improves the strictness of access and setting of model fields, perhaps for validation reasons. Page 24 Routing The Rails router recognizes URLs and dispatches them to a controller's action. It can also generate paths and URLs, avoiding the need to hardcode strings in your views. When your Rails application receives an incoming request for: GET /patients/17 it asks the router to match it to a controller action. If the first matching route is: get '/patients/:id', to: 'patients#show' the request is dispatched to the patients controller's show action with { id: '17' } in params. You can also generate paths and URLs. If the route above is modified to be: get '/patients/:id', to: 'patients#show', as: 'patient' and your application contains this code in the controller: @patient = Patient.find(17) and this in the corresponding view: <%= link_to 'Patient Record', patient_path(@patient) %> then the router will generate the path /patients/17. This reduces the brittleness of your view and makes your code easier to understand. Note that the id does not need to be specified in the route helper. The routes are specified in config.routes.rb of each Rails application. Prior to Rails 3, these were specified by adding to the map.routes variable, but in Rails 3 and later there is a more declarative syntax used, which in turn provides a way to create multiple related mappings quickly, such as those for a resource. Resource-oriented routing declarations RR016::Application.routes.draw do resources :purchases end Inside the block for the draw method is the resources method. Collections of similar objects in Rails are referred to as resources. This method defines the routes and routing helpers (such as the edit_purchase_path method) to your purchases resources. Table 1.1. Routing helpers and their routes Helper Route purchases_path /purchases new_purchase_path /purchases/new edit_purchase_path /purchases/:id/edit purchase_path /purchases/:id In this table, :id can be substituted for the ID of a record. Each routing helper has an alternative version that will give you the full URL to the resource. Simply use the _url extension rather than _path, and you’ll get a URL such as http://localhost:3000/purchases for purchases_url. Page 25 From this table, two of these routes will act differently depending on how they’re requested. The first route, /purchases, takes you to the index action of PurchasesController if you do a GET request. GET requests are the standard type of requests for web browsers, and this is the first request you did to this application. If you do a POST request to this route, it will go to the create action of the controller. This is the case when you submit the form from the new view. Let’s go to http://localhost:3000/purchases/new now and look at the source of the page. You should see the beginning tag for your form looking like the following listing. <form action="/purchases" class="new_purchase" id="new_purchase" method="post"> The two attributes to note here are the action and method attributes. The action dictates the route to where this form goes, and the method tells the form what kind of HTTP request to make. How’d this tag get rendered in the first place? Well, as you saw before, the app/views/purchases/new.html.erb template uses the form partial from app/views/purchases/_form.html.erb, which contains this as the first line: <%= form_for(@purchase) do |f| %> This one simple line generates that form tag. When we look at the edit action shortly, you’ll see that the output of this tag is different, and you’ll see why. The other route that responds differently is the /purchases/{id} route, which acts in one of three ways. You already saw the first way: it’s the show action to which you’re redirected (a GET request) after you create a purchase. Ad-Hoc Routing Declarations You can specify a name for any route using the :as option: get 'exit', to: 'sessions#destroy', as: :logout This will create logout_path and logout_url as named helpers in your application. Calling logout_path will return /exit You can also use this to override routing methods defined by resources, like this: get ':username', to: 'users#show', as: :user This will define a user_path method that will be available in controllers, helpers and views that will go to a route such as /bob. Inside the show action of UsersController, params[:username] will contain the username for the user. Change :username in the route definition if you do not want your parameter name to be :username. In general, you should use the get, post, put and delete methods to constrain a route to a particular verb. You can use the match method with the :via option to match multiple verbs at once: match 'photos', to: 'photos#show', via: [:get, :post] You can match all verbs to a particular route using via: :all: match 'photos', to: 'photos#show', via: :all Routing both GET and POST requests to a single action has security implications. In general, you should avoid routing all verbs to an action unless you have a good reason to. You can also have wildcards in the match expression. Page 26 Listing Existing Routes To get a complete list of the available routes in your application, visit http://localhost:3000/rails/info/routes in your browser while your server is running in the development environment. You can also execute the rake routes command in your terminal to produce the same output. Both methods will list all of your routes, in the same order that they appear in routes.rb. For each route, you'll see: The route name (if any) The HTTP verb used (if the route doesn't respond to all verbs) The URL pattern to match The routing parameters for the route Common Code (i.e., Helpers) This is what Grails called “services”. Resources A “resource” represents a data model and the infrastructure around it for fetching, editing, creating etc. For instance, a User is a resource. Working outwards from the User resource, we can now use all of the key concepts introduced so far and put them together. For instance, at the back end, we are also starting to think of the operations supported by the back end in terms of REST operations. On the front end, there are several tools that will help create HTML form screens for the CRUD operations. But first, a diversion to show how to display debug information. Showing Debug Information There is a built-in debug method and params variable. Here we added it to the main layout: <!DOCTYPE html> <html> . . . <body> <%= render 'layouts/header' %> <div class="container"> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> </body> </html> Use CSS such as: .debug_dump { clear: both; float: left; width: 100%; margin-top: 45px; } Rails environments Rails comes equipped with three environments: test, development, and production. The default environment for the Rails console is development: $ rails console Page 27 Loading development environment >> Rails.env => "development" >> This facility is used in the debug(params) if Rails.env.development? statement. Resource specifications in Routes This is a shortcut for specifiying all of the CRUD operations on a given entity. SampleApp::Application.routes.draw do resources :users root to: 'static_pages#home' match '/signup', . . . End to: 'users#new' Using Factories In order to make the necessary User model object, we could use Active Record to create a user with User.create, but experience shows that user factories are a more convenient way to define user objects and insert them in the database. We’ll be using the factories generated by the Factory Girl gem. As with RSpec, Factory Girl defines a domain-specific language in Ruby, in this case specialized for defining Active Record objects. The syntax is simple, relying on Ruby blocks and custom methods to define the attributes of the desired object. For cases such as the one in this chapter, the advantage over Active Record may not be obvious, but we’ll use more advanced features of factories below. As with other Ruby gems, we can install Factory Girl by adding a line to the Gemfile (since Factory Girl is only needed in the tests, we’ve put it in the :test group). group :test do gem 'factory_girl_rails', '1.4.0' end Then install as usual: $ bundle install We’ll put all our Factory Girl factories in the file spec/factories.rb, which automatically gets loaded by RSpec. The code needed to make a User factory appears below: FactoryGirl.define do factory :user do name "Michael Hartl" email "[email protected]" password "foobar" password_confirmation "foobar" end end By passing the symbol :user to the factory command, we tell Factory Girl that the subsequent definition is for a User model object. Page 28 Using Git with Rails Git is not specific to Rails, however, its use is very common for Rails developers. In turn, Git is used to provide a way to communicate code and test cases to the continuous integration server as part of the Agile development model. The Hartl book suggests using the following .gitignore file: .bundle db/*.sqlite3* log/*.log *.log tmp/**/* tmp/* doc/api doc/app *.swp *~ .DS_Store This will have the result of skipping temp files, log files, doc files, sqlite db files, the .DS_Store files created by MacOS utilities, etc. What is maintained is: app – contains models, controllers, views, etc. config – contains all config information. db – contains migrations lib – contains supporting DLL’s, library files, etc. (I think) public – contains the static html files (but we should skip the outputs of the asset precompiler) script – contains command-line scripts test – contains the test specs vendor – contains vendor-supplied files and assets, typically for third-party purchased components. Gemfile – describes the dependencies Using Gravatars Having defined a basic user page in the previous section, we’ll now flesh it out a little with a profile image for each user and the first cut of the user sidebar. When making views, we’ll focus on the visual appearance and not worry too much about the exact structure of the page, which means that (at least for now) we won’t be writing tests. We’ll start by adding a “globally recognized avatar”, or Gravatar, to the user profile. Originally created by Tom Preston-Werner (cofounder of GitHub) and later acquired by Automattic (the makers of WordPress), Gravatar is a free service that allows users to upload images and associate them with email addresses they control. Gravatars are a convenient way to include user profile images without going through the trouble of managing image upload, cropping, and storage; all we need to do is construct the proper Gravatar image URI using the user’s email address and the corresponding Gravatar image will automatically appear. Our plan is to define a gravatar_for helper function to return a Gravatar image for a given user, as shown below: app/views/users/show.html.erb <% provide(:title, @user.name) %> Page 29 <h1> <%= gravatar_for @user %> <%= @user.name %> </h1> Because the gravatar_for method is undefined, the user show view is currently broken. (Catching errors of this nature is perhaps the most useful aspect of view specs. This is why having some test of the view, even a minimalist one, is so important.) Using Helpers By default, methods defined in any helper file are automatically available in any view, but for convenience we’ll put the gravatar_for method in the file for helpers associated with the Users controller. app/helpers/users_helper.rb module UsersHelper # Returns the Gravatar (http://gravatar.com/) for the given user. def gravatar_for(user) gravatar_id = Digest::MD5::hexdigest(user.email.downcase) gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}" image_tag(gravatar_url, alt: user.name, class: "gravatar") end end Further details of implementing User Management Now that we have good failing tests for user signup, we’ll start getting them to pass by making a form for signing up users. We can accomplish this in Rails with the form_for helper method, which takes in an Active Record object and constructs a form using the object’s attributes. <% provide(:title, 'Sign up') %> <h1>Sign up</h1> <div class="row"> <div class="span6 offset3"> <%= form_for(@user) do |f| %> <%= f.label :name %> <%= f.text_field :name %> <%= f.label :email %> <%= f.text_field :email %> <%= f.label :password %> <%= f.password_field :password %> <%= f.label :password_confirmation, "Confirmation" %> <%= f.password_field :password_confirmation %> <%= f.submit "Create my account", class: "btn btn-large btn-primary" %> <% end %> </div> </div> Using Flash Before submitting a valid registration in a browser, we’re going to add a bit of polish common in web applications: a message that appears on the subsequent page (in this case, welcoming our new user to the application) and then disappears upon visiting a second page or on page reload. The Rails way to accomplish this is to use a special variable called the flash, which operates like flash memory in that it stores its data temporarily. The flash variable Page 30 is effectively a hash; you may even recall the console example in Section 4.3.3, where we saw how to iterate through a hash using a strategically named flash hash: $ rails console >> flash = { success: "It worked!", error: "It failed." } => {:success=>"It worked!", error: "It failed."} >> flash.each do |key, value| ?> puts "#{key}" ?> puts "#{value}" >> end success It worked! error It failed. We can arrange to display the contents of the flash site-wide by including it in our application layout, as shown below: <!DOCTYPE html> <html> . . . <body> <%= render 'layouts/header' %> <div class="container"> <% flash.each do |key, value| %> <div class="alert alert-<%= key %>"><%= value %></div> <% end %> <%= yield %> <%= render 'layouts/footer' %> <%= debug(params) if Rails.env.development? %> </div> . . . </body> </html> The code above arranges to insert a div tag for each element in the flash, with a CSS class indicating the type of message. For example, if flash[:success] = "Welcome to the Sample App!", then the code <% flash.each do |key, value| %> <div class="alert alert-<%= key %>"><%= value %></div> <% end %> Session Management To save data across multiple requests, you can use either the session or the flash hashes. A flash hash stores a value (normally text) until the next request, while a session stores data during the complete session. In your controller: session[:user] = @user flash[:message] = "Data was saved successfully" Page 31 In your view: <%= link_to "login", :action => 'login' unless session[:user] %> <% if flash[:message] %> <div> <%= h flash[:message] %> </div> <% end %> Caching (improved in Rails 4) Action Caching (GitHub, Pull Request) Page Caching (GitHub, Pull Request) Testing within Rails In the Ruby world a huge emphasis is placed on testing, specifically on test-driven development (TDD) and behavior-driven development (BDD). There are three commonly-used testing tools, Test::Unit, RSpec, and Cucumber. By learning good testing techniques now, you’ve got a solid way to make sure nothing is broken when you start to write your first real Rails application. If you didn’t test, there’s no telling what could go wrong in your code. TDD is a methodology consisting of writing a failing test case first (usually using a testing tool such as Test::Unit), then writing the code to make the test pass, and finally refactoring the code. This process is commonly called redgreen-refactor. The reasons for developing code this way are twofold. First, it makes you consider how the code should be running before it is used by anybody. Second, it gives you an automated test you can run as often as you like to ensure your code is still working as you intended. We’ll be using the Test::Unit tool for TDD. BDD is a methodology based on TDD. It is slightly higher-level, in that you write an automated test to check the interaction between the different parts of the codebase rather than testing that each part works independently. The two tools used for BDD are RSpec and Cucumber, both of which the Hartl book uses heavily. Testing The default test framework is called Test::Unit. However, in the Hartl book he focuses on RSpec, so when creating the Rails project, add the --skip-test-unit attribute to the command line. Add something like the following to the Gem file: group :development, :test do gem 'sqlite3', '1.3.5' gem 'rspec-rails', '2.10.0' end Next, we need to configure Rails to use RSpec in place of Test::Unit. This can be accomplished with rails generate rspec:install: $ rails generate rspec:install More information on testing is located below. Debugging Edit config/environments/development.rb, for instance to change the development env’s log level. This is recommended. Simply add the line config.log_level = :warn to set WARN level. This will greatly decrease the amount of logging performed. Then you can use logger.warn or a similar call. Note that not all objects can be converted into string for display. Page 32 Pagination We cover this here since it combines views, controllers, and models. The Kaminari gem, is a new breed of pagination gem written by Akira Matsuda, and is available for both Rails 3 and 4. After you install this gem, you’re given an interface on the models of your application, which allows you to make calls like this: @project.tickets.page(2).per(50) This call would ask for the second page of tickets, with each page containing 50 tickets. It’s a very clean API. Those familiar with will_paginate will be used to a syntax like this: @project.tickets.paginate(:per_page => 50, :page => 2) The syntax is a little longer, but it’s a little clearer what it’s doing to those who are familiar with it. You’ll use Kaminari here just for something different. In your views, you can use the same paginate method, which is made available by both gems: <%= paginate @tickets %> Using Modules A module is a reusable component of code. It is created/opened by simply coding: module MyModule def first_module_method end end Places all modules into the lib folder. If you want to organize your modules in the lib folder, you can put them into modules themselves. For example, if you wanted a subfolder super_modules your modules would be defined as follows: module SuperModules module MyModule def first_module_method end end end When including the module in a class you can simply call the modules methods as if they were defined within the class: class MyClass include MyModule def some_method first_module_method #calls module method end end When to use Modules? First, make sure that your module is really needed in every class of your application. If it isn't it makes sense to only include it where it is need so as not to bloat the classes that don't need it anyways. If you really want the module everywhere, include look at the class hierarchy of your classes in the app. Do you want the module in all models? You could open ActiveRecord::Base and add your module there. Page 33 Rack The term ‘Rack” refers to the middleware that is the interface between Rails as an application framework, and the facilities provided by the web server/application server. Rack provides a minimal, modular and adaptable interface for developing web applications in Ruby. By wrapping HTTP requests and responses in the simplest way possible, it unifies and distills the API for web servers, web frameworks, and software in between into a single method call. Rack was first developed in Rails 2.3, and moved into a gem of its own. It allows the substitution of different web servers. For instance, development may use WEBrick, but production may use Phusion Passenger or Unicorn. Rack-based Applications Rack is the underlying web server framework that powers the underlying request/response cycle found in Rails, but it isn’t a part of Rails itself. It’s completely separate, with Rails requiring the parts of Rack it needs. When your application is running, it’s running through a web server. When your web server receives a request, it will pass it off to Rack. It is possible to develop applications directly at the Rack level rather than Rails-level, by using lighter-weight frameworks such as Sinatra. Such a framework has no ORM, no configuration, not even a project structure, but can be light-weight for deployment of a supporting server, such as one with no screens but only REST-based interfaces, for instance. A Rack-based application is typically started using the command “rackup”. As of fall 2013, we haven’t worked directly with the Rack layer, though we briefly looked at Sinatra in Spring 2012. Performance Testing (improved in Rails 4) Performance tests (GitHub, Pull Request) Using RSpec for Tests Here we extend upon the initial information given regarding testing from the above sections. Caching (new in 4.0) Make it super easy to do Russian Doll-caching through key-based expiration with automatic dependency management of nested templates (explored first in the cache_digests plugin). Turbolinks (new in 4.0) There is a facility to speed up the client-side with Turbolinks, which essentially turns your app into a single-page JavaScript application in terms of speed, but with none of the developmental drawbacks (except, maybe, compatibility issues with some existing JavaScript packages). What is does is revise all of your <a> tags to run JavaScript that makes a request to the server using Ajax for the new page content, then changes the body of the current page to be that content. Hence, the amount of processing on the browser side (which generally includes processing the full request, including processing of the headers, including processing of the resources referenced in the headers) is greatly reduced. The compatibility issues stem from the idea that most JavaScript in your application is written with the assumption that it running as part of a fresh page load. Hence, reaction to and comments on this feature have been mixed in the developer community. In general, if you are dealing with the standard CRUD update screens as generated by the Rails generator you will probably be okay, and if you are working a page or app that was designed to use Ajax and be a single-page web app you won’t need Turbolinks, and if you are in between these cases, you need to review your JavaScript and libraries carefully for compliance. As of this point, we have skipped over Turbolinks, and focused on better understanding of JavaScript frameworks such as Backbone and Angular. Page 34 Declarative eTags (new in 4.0) Declarative etags makes it even easier to ensure you're taking advantage of HTTP freshness. Spork You may have noticed that the overhead involved in running a test suite can be considerable. This is because each time RSpec runs the tests it has to reload the entire Rails environment. The Spork test server (the name is based on the name of a combination spoon-fork) aims to solve this problem. Spork loads the environment once, and then maintains a pool of processes for running future tests. Spork is particularly useful when combined with Autotest. Configuring Spork and getting it to work can be difficult, and this is a rather advanced topic; in particular, as of this writing Spork doesn’t officially support Rails 3, so this section is really just a collection of hacks. Nevertheless, the performance boost due to Spork is considerable, so I recommend giving it a shot at some point. (You’ll have to rely on the Spork website and Google searches if these directions don’t work for you.) Engines Engines are a new feature for Rails 3. They are sub-applications that are packaged as gems and can run inside a standard Rails application. They give you most of the functionality of a standard rails app, including the controllers, views, helpers, configuration, etc… A Rails application is just a “supercharged” engine, with the Rails:Application class inheriting a lot of its behavior from Rails:Engine. Engines allow Rails programmers to share common code between applications in an extremely easy fashion. It’s entirely possible to use more than one engine in an application, and many people do. Engines are generally provided as gems, and so they are managed like every other gem your application uses: by using Bundler. With an Engine, all the code (this includes models, controllers, views, and assets) is kept separate from the application and must be explicitly overridden in the application if that’s what is needed. When a new version of an engine is released, it will only alter the code of the engine and not the application, making the upgrade process as easy as changing a version number in a Gemfile. The term is most similar to “plugin” within other frameworks, in fact the command to create an Engine is “rails plugin new”. Examples of Engines Devise Forem Spree RefineryCMS The structure of an Engine can be seen to be a subset of the structure of a full Application. Engines are stored in the same place as gems within the Ruby directory structure: Ruby193/lib/ruby/gems/1.9.3/gems directory We will not be discussing Engines directly in this document, but several of the gems mentioned in the Appendixes are in fact Engines (Devise is a good example). Modularity through Engines An Engine provides a useful coding abstraction to compartmentalize a “functional module” of an application. Each Engine contains its models, views, controllers, and assets. Apparently, some large Rails applications are built in this way. From a coding standpoint, the identifiers within each Engine are scoped to a different Ruby namespace to avoid collisions. Observations and Open Issues Controllers and Models are Ruby code, the Views are ‘erb’ code. This is in contrast to CodeIgniter where all are .php files. Page 35 The name of a file does not always match the name of its contained class. Ruby is quite flexible in this regard. The command-line tools are quite useful. Bundle is used manage gems, rake is used to manage the db, and rails is used to carry out the generator and the execution. Rails includes an application runner (WEBrick). Ruby is a more comprehensive language than PHP, and has better object-oriented support. Testing: the emphasis is on unit tests and integration tests, using RSpec, rather than functional testing. Hence, no discussion of Selenium or WebDriver appears here. Appendix A: Chronology 05/19/12 to 05/20/12 Installed Rails 3.2.3, with Ruby 1.9.3p194. Found files at RailsInstaller.org Loaded supporting gems including the DevKit (this appeared to be needed in order to run the JSON marshaller). A key piece was the MySQL adapter, since we were changing the programs to work with MySQL instead of SQLite. Wrote simple programs, working from http://guides.rubyonrails.org/getting_started.html Reviewed books. Ran example program of web application. Tested the scaffolding functions. 06/20/12 Re-ran the examples. Continued to use Eclipse with a “general project” for the development tools. Since this takes up a good bit of memory, something else might be better. 06/22/12 Looked at sample programs, and test cases for them. 06/24/12 Development meeting with Bucksprout, learned about a number of useful Gems. 06/30/12 By this point the RR01, RR02, and RR03 programs were in place, and the RR03 was starting to use the specified Gems. 07/01/12 Moved knowledge gained from RR03 into RR02. Updated all documents as a result. 07/13-14/12 Re-worked all of our Rails documentation. Also looked at IDE/editors such as RubyMine and Sublime Text. 08/10/12 Began to evaluate Rails-based CMS programs. 07/03/13 Updated to 3.2.11 (at the Human API hackathon) Late September 2013 to mid-October 2013. Re-ran all examples, updated to Rails 3.2.14 and then to Rails 4.0.0. Began to use the RubyMine plugin with IntelliJ instead of Eclipse. 10/31/13 to 11/03/13 Since the sample applications were now using all of the basic functions within the Rails framework, we began to review more advanced documentation, including material on Rack and Engines. Completed another literature search. Late 2013 Deployed main Squirrel Legend server using Rails 4.0 Page 36 08/01/14 At the Startup Weekend event, evaluated Rails 4.1, and switched to it. 11/02/14 Updated the sample programs RR02 and RR03 to Rails 4.1 and Twitter Bootstrap 3.2.0. Also found a large number of improvements to make in each one. 12/13/14 First event that integrated Rails and AngularJS. Appendix C: Sample Programs RR01 This is the example program from the on-line guide that is a blog with posts and comments. Changes made were limited to simple layout modifications and CSS modifications. Uses PostgreSQL so that it can be deployed to Heroku. RR02 Improvement to RR01 that has a data model of Challenges and Activities. Uses PostgreSQL so that it can be deployed to Heroku. Contains Squirrel Legend Games graphics. RR03 This is a modification of RR01, showing use of the most important gems for User management. [done] add User domain object [done] install Devise [done] add the ability for User to log in [done] install CanCan for authorization [not started] define admin page and separate operations between roles RR04 Puts all of the above together, including use of Amazon Web Services S3 for images. Uses PostgreSQL. A simple version of QbeFit, with the domain objects such as: ActivityType, Activity, ActivityEvent, and User. [done] set up the model files, including user, role, and userrole (rolify was reverse engineered to help on this) [done] install Devise [done] install CanCan for authorization [in progress] create a home page for a user [in progress] create a home page for an admin [in progress] change to use Paperclip for attachments (probably store these on the file system for now, later S3) [not started] create “add activity” screen for a user [not started] create track screen for a user RR05 Rails plus Angular example, derived from CodingHouse training session [done] basic setup, to match the end of the course [in progress] display set of answers to a given question RR06 This is a subset of the Squirrel Legend Games server, with multiple games, each having a list of episodes, and access rules/sequencing for the episodes. We also need to define admin screens for some analytic entities. [in progress] basic setup Page 37 Appendix C: Gems of Interest to Try out AB/Testing: Split Attachments: Paperclip – Paperclip is intended as an easy file attachment library for ActiveRecord. The intent behind it was to keep setup as easy as possible and to treat files as much like other attributes as possible. http://github.com/thoughtbot/paperclip/tree/master. See http://burm.net/2008/10/07/the-ruby-on-rails-paperclipplugin-tutorial-easy-image-attachments/ Authorization: CanCan. See http://railsapps.github.com/tutorial-rails-bootstrap-devise-cancan.html AWS S3 management: aws, fog Procedural query generation: Squeel User registration: Devise (an alternative is auth_logic) Appendix D: Devise Gem Devise is a gem that adds user management, including registration, log in, log out, etc. It is a very flexible package that includes email sending and validation. This gem is used for a large number of user-centric applications. The current release is 2.1.2 (June 2012). In this version, all deprecations from 2.0 have been removed, so we should focus on this version. Installation steps Add the gem to the gem file, then run bundle install && rails generate devise:install The installation step will give additional instructions, such as setting up the mail address, adding a flash and message area to your layout screen, and adding routing information. Next run, rails generate devise User, which will change the User model class to have additional devise fields. It is here that you turn on and off elements of the 8 or 9 different options. Also change the migrate file to have fields commented and out. Now run a db:migrate, and you can run the application. Manually go to the users/sign_up screen. Next, we changed the main application layout to have the following on the main screen: <% if user_signed_in? %> Signed in as <%= current_user.email %>. <%= link_to 'Sign out', destroy_user_session_path, :method => :delete %> <% else %> <%= link_to 'Register', new_user_registration_path %> or <%= link_to 'Sign in', new_user_session_path %> <% end %> From here, we typically focused on the user/role management and the CanCan gem. However, there is one more step which will allow customization of the views for user registration and more. Run rails generate devise:views. This will create about 12 views in the views/devise folder that implement the various operations. You can add and re-arrange fields on these views. For instance, we added firstname and lastname to the signup screen. In the above example, the controllers for handling operations such as register and sign_in are located in the Devise gem’s set of controllers, and pointed by the routing instruction “devise_for :users”. If you want to implement your own versions of these, you can either: Page 38 1. 2. Override the controller class names that are to be used for actions. For instance use devise_for :users, :controllers => {:registrations => "Nregistrations"} Typically the new classes will extend from the ones provided in Devise. Change the “devise_for” instruction as shown below and implement your own versions of all controllers. devise_for :users, :skip => :all This parameter tells Devise to not generate any route at all. You can check that by executing bundle exec rake routes. However, you may be wondering: why can’t we simply remove the devise_for call? If we remove the route, Devise wouldn’t actually know that you have added Devise configuration to the User model, as all models are lazy loaded. So we need the route to tell Devise it needs to setup the appropriate helpers for the user, like authenticate_user!. With Devise configured, we are ready to create the controllers and views on our own. In this blog post, we are going to create the SessionsController as an example allowing us to sign in and sign out. First, let’s add our routes: root :to => "sessions#new" post "/users/sign_in" delete "/users/sign_out" => "sessions#destroy" => "sessions#create" The controller implementation is quite straightforward. With the controller in hands, we just need to generate the view for the new action at app/views/sessions/new.html.erb: Page 39 The view shows a message if the user is signed in, otherwise it shows a sign in form. Now we are almost ready to check if it works. First, we need to run the migrations: bundle exec rake db:migrate Remove the index page: rm public/index.html And create a user in the database so we can sign in. We can do that in rails console: User.create!(:email => "[email protected]", :password => "123456") Now, start the server and you are ready to sign in and sign out. You can also block user access in any controller by calling authenticate_user! in a before filter. Just remember that, if you add the filter to your application controller, remember to skip the filter on the sessions controller, otherwise you won’t be able to sign in in the first place. You can now freely proceed to implement the other controllers and views in your application. Keep in mind that if you have devise :recoverable in your model, all the related methods like User.send_reset_password_instructions will already be available in your model, so you can use them straight away to implement your own reset password feature. Since Devise use all those methods internally, if you have any questions about implementing your own reset password feature, you can always take a look at Devise’s own controllers for some help (find them in the source code on GitHub). Appendix E: CanCan Gem This is a permission management system that determines what operations a user can carry out. CanCan is an authorization library for Ruby on Rails which restricts what resources a given user is allowed to access. All permissions are defined in a single location (the Ability class) and not duplicated across controllers, views, and database queries. The current release is 1.6.10 (May 2013) Using CanCan First, add the “cancan” gem. To use CanCan we need to create a new class called Ability, which we’ll place in our /app/models directory. The class needs to include the CanCan::Ability module and also an initialize method that takes a user object as a parameter and it’s in this method that we’ll define the abilities for each type of user. CanCan includes a Rails 3 generator for creating this class. rails g cancan:ability Here is an example: class Ability include CanCan::Ability def initialize(user) can :read, :all end end The abilities are specified with the method ‘can’ which is at the heart of CanCan. This method takes two parameters: the first is the action that we want to perform and the second is the model class that the action applies to. Alternatively, to apply an action to all models we can pass :all. In the view code for the article’s show action we can call the can? predicate to determine whether the current user is authorized to perform the action that each link links to. Like can, can? takes two parameters, an action and a model, in this case an Article. There is an inverse predicate called cannot?. We’ll use can? in the show view so that the edit and destroy links are hidden unless the current user has the appropriate ability. <% if can? :update, @article %> Page 40 <%= link_to "Edit", edit_article_path(@article) %> <% end %> <% if can? :destroy, @article %> <%= link_to 'Destroy', @article, method: :delete, data: {confirm: 'Are you sure?'} %> <% end %> This has removed the operations from the screen, however the smart user could still access the method by typing in the URL directly. Hence, we must also check in the controller class. def edit @article = Article.find(params[:id]) unauthorized! if cannot? :edit, @article end To stop the action being executed we call unauthorized! which will raise an exception. Obviously we only want this exception raised if the user does not have the appropriate authorization. To check this we can use can? as we did in the view or, as we have here, cannot? to check the authorization. If we try to access the edit action directly now we’ll be stopped from doing so and an error will be raised (rather crudely – with a long stack trace). Setting this for every action can be tedious, therefore the load_and_authorize_resource method is provided to automatically perform an authorization check on all actions in a RESTful style resource controller (i.e., CRUD/admin). It will use a before filter to load the resource into an instance variable and check the auth for every action. class ArticlesController < ApplicationController load_and_authorize_resource def show # @article is already loaded and authorized end end If the user authorization fails, a CanCan::AccessDenied exception will be raised. You can catch this and modify its behavior in the ApplicationController. class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied do |exception| redirect_to root_url, :alert => exception.message end end Note: in Rails 4 you must use: load_and_authorize_resource :except => [:create, :update] Adding Abilities Now that our application is secure we can start to define the abilities that each role will have. This is done back in the Ability class we created earlier. The abilities we define in the initialize method will be reflected through the rest of our application. We pass in the current user to initialize so we can change the abilities according to the currently logged-in user. We’ll start with the users in the administrator role who should be able to manage everything. The user passed to initialize can be an object of any type which means that the authentication is completely decoupled from the authorization. What defines a user as, say, an administrator entirely depends on the authentication system used. We might, for example, have an admin? boolean field in our User model. In our application a user can have many roles and we’ll have a role? method to tell us if a user is a member of a role. We’ll use that method to set the abilities: Page 41 class Ability include CanCan::Ability def initialize(user) if user.role? :admin can :manage, :all else can :read, :all end end end The actual code we are using in the User model (different from the RailsCast is) has_many :user_roles, :dependent => :destroy, :uniq => true has_many :roles, :through => :user_roles, :uniq => true # does this user have the specified role in its role list? def has_role?(role_sym) roles.any? { |role| role.name.underscore.to_sym == role_sym.downcase } end Here is a more complex case of authorization checking (where the user ||= User.new statement handles there not being a current user): class Ability include CanCan::Ability def initialize(user) user ||= User.new if user.role? :admin can :manage, :all else can :read, :all can :create, Comment can :update, Comment do |comment| comment.try(:user) == user end end end end Writing the code to enable guest users to create comments is straightforward but the update code is a little trickier as users should only be able to update comments they have written. To do this we pass can a block which will pass in the instance of the model we’re checking. The block should return true or false depending on whether the action should be allowed so in the block we’ll check that the comment’s user is the current user. There’s a possibility that the comment might be nil so we’ll use Rails’ try method to read the user attribute so that nil is returned if the comment is nil instead of an exception being raised. If we log in as a user who has no roles now we can add a comment and update it but not the comments made by anyone else. This leads to: class Ability include CanCan::Ability def initialize(user) user ||= User.new if user.role? :admin can :manage, :all else can :read, :all Page 42 can :create, Comment can :update, Comment do |comment| comment.try(:user) == user || user.role?(:moderator) end if user.role?(:author) can :create, Article can :update, Article do |article| article.try(:user) == user end end end end end Now we have all of our abilities defined for each user role. The nice thing about CanCan is that it allows us to define all of the abilities in one location and the rest of the application will reflect these changes. A prettier error page If a user calls an action that they don’t have access to they will see a rather ugly error page showing an AccessDenied exception. We can change this so that they see a better-looking custom error page instead. Rails provides a method called rescue_from that we can place in our ApplicationController. We pass it an exception and pass it either a method or a block. We’ll pass a block and inside it make the application show a flash error message and redirect to the home page. rescue_from CanCan::AccessDenied do |exception| flash[:error] = "Access denied!" redirect_to root_url end If a user without roles now tries to edit an article by typing the URL in directly they’ll be redirected to the home page and told that they can’t do that. Creating role selection checkboxes Note: The simplest rule of thumb when declaring many –to-many relationship is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database). It is often asked: how do I create a list of checkboxes for managing a HABTM association (e.g.: User to Roles)? In User/_form.html.erb add: <% for role in Role.all %> <div> <%= check_box_tag "user[role_ids][]", role.id, @user.roles.include?(role) %> <%=h role.name %> </div> <% end %> <%= hidden_field_tag "user[role_ids][]", "" %> If no checkboxes are checked, the params[:user][:role_ids] array is not getting set at all (it is nil). Instead, you want this to be an empty array. To fix this problem, add this to your update action in UserController.rb: def update params[:user][:role_ids] ||= [] … end Page 43 The ||= is a little strange, but it makes sense once you get used to it. It works just like an equal sign, but it only sets the value if the original is nil - this is very useful. Since we have separate model called User_Role, in order to assign new roles to user we need to define method in User.rb to do this: # For assignment of roles using checkboxes when creating or updating a user model. def role_ids=(role_ids) # It destroys all entries from user_roles which have not been selected using # checkboxes (i.e. those which have been removed) and then adds any new ones user_roles.each do |usr_role| usr_role.destroy unless role_ids.include? usr_role.role_id end role_ids.each do |role_id| self.user_roles.create(:role_id=>role_id) unless user_roles.any? {|r|r.role_id==role_id} end end To work with the strong parameters of Ruby 4.x, you will need to add role_ids to the permitted params, as shown below: def user_params params.require(:user).permit( :email, :facebook_id, :locked, :score_points, :location_id, :role_ids => [] ) end Appendix F: Paperclip Gem Paperclip is intended as an easy file attachment library for Active Record. The intent behind it was to keep setup as easy as possible and to treat files as much like other attributes as possible. This means they aren't saved to their final locations on disk, nor are they deleted if set to nil, until ActiveRecord::Base#save is called. It manages validations based on size and presence, if required. It can transform its assigned image into thumbnails if needed, and the prerequisites are as simple as installing ImageMagick (which, for most modern Unix-based systems, is as easy as installing the right packages). Attached files are saved to the filesystem and referenced in the browser by an easily understandable specification, which has sensible and useful defaults. The basics of Paperclip are quite simple: Declare that your model has an attachment with the has_attached_file method, and give it a name. Paperclip will wrap up four attributes regarding your image (all prefixed with that attachment's name, so you can have multiple attachments per model if you wish) and give them a friendly front end. These attributes are: <attachment>_file_name <attachment>_file_size <attachment>_content_type <attachment>_updated_at By default, only <attachment>_file_name is required for paperclip to operate. You'll need to add <attachment>_content_type in case you want to use content type validation. More information about the options to has_attached_file is available in the documentation of Paperclip::ClassMethods. For validations, Paperclip introduces several validators to validate your attachment: AttachmentContentTypeValidator AttachmentPresenceValidator AttachmentSizeValidator Example of Usage: Page 44 validates :avatar, :attachment_presence => true validates_with AttachmentPresenceValidator, :attributes => :avatar Validators can also be defined using the old helper style: validates_attachment_presence validates_attachment_content_type validates_attachment_size Example Usage: validates_attachment_presence :avatar Lastly, you can also define multiple validations on a single attachment using validates_attachment: validates_attachment :avatar, :presence => true, :content_type => { :content_type => "image/jpg" }, :size => { :in => 0..10.kilobytes } Storage System By default, the images will be stored in the local file system. You can configure the path using the “path” attribute of has_attached_file. Here is an example: :path => ":attachment/:id/:timestamp_:style.:extension", Each of the placeholders will be filled in. You can structure the storage hierarchy as desired. If you include the “fog” gem, you can write to external storage systems such as Amazon S3. Here is a configuration that writes to S3: Paperclip::Attachment.default_options.update( { :path => ":attachment/:id/:timestamp_:style.:extension", :storage => :fog, :fog_credentials => { :provider => 'AWS', :aws_access_key_id => '<API_KEY>', :aws_secret_access_key => '<SECRET_KEY>', :persistent => false }, :fog_directory => 'rr016', :fog_public => true } ) Appendix G: Squeel Gem Consider the case of a complex query, such as in a Product listing. The search code seems like a fairly simple thing to write but the query in the Product model is quite complicated. We need to search only the products that have been released, that aren’t discontinued, that are in stock and which have a name that matches the search term. Here is a typical implementation: def self.search(query) where("released_at <= ? and (discontinued_at is null or discontinued_at > ?) and stock >= ? and name like ?", Time.zone.now, Time.zone.now, 2, "%#{query}%") end A fairly long SQL query is being performed here and which has a number of variables appended at the end and these need to correctly line up with the parameters in the query. There are a variety of ways that we could improve this Page 45 query and the biggest improvement we could make would be to move parts of the query into named scopes and search using those. If we did this we’d still be using SQL so we’d still need to worry about database differences such as in the LIKE clause in our query. In most databases this will perform a case-insensitive query so if we were to switch the application’s database to PostgreSQL we’d need to change this to ILIKE. It would be nice if there was some consistency here and if we could abstract these database queries out. This is the problem handled by Squeel. Initial Examples We’ll start by experimenting with Squeel in the console. Squeel allows us to pass a block of code to a where call and we can use the Squeel DSL inside this block. We can call any column as a method inside this block, like this: Product.where{released_at <= 3.months.ago} Squeel uses more than ActiveRecord::Relation, though. It’s built on Arel and uses that to convert queries into SQL. We can see this if we look at the Squeel README which has a table showing a list of the operators that Arel supports along with the equivalent SQL operator. So, instead of using the < operator that Squeel provides we can use Arel’s lt method directly to make the same query. You can also use the & and | operators to form ‘and’ and ‘or’ conditions. Note that you will typically need to fill in the parenthesis in this case so that the operator parsing works as desired. Getting back to our Product query, translated into Squeel code it becomes this: def self.search(query) where do (released_at <= Time.zone.now) & ((discontinued_at == nil) | (discontinued_at > Time.zone.now)) & (stock >= 2) & (name =~ "%#{query}%") end end Note that for the LIKE clause we use =~, similar to the regular expression operator, and this will perform a caseinsensitive search no matter what database is being used. Also to compare against NULL we use == nil instead of is null. It’s arguable as to whether this code is cleaner than what we had before but one definite improvement is that the values are inline in the query instead of being tagged on at the end. Also as we’re now using Ruby code we can use a multiple-line block to spread the query out to make it easier to read. When we try the search field out in the browser now it brings back the same results as our SQL query did. Another example was first written for one of our test programs. This returns all of the challenges that are currently in effect: # GET /challenges/open def open @challenges = Challenge.where { (starts_at <= Time.now) & (ends_at >= Time.now) } end Keeping Things in Context It’s important to remember that Squeel uses instance_eval when it calls the block. This means that the current context in our block will not be the Product class but a Squeel DSL instance instead. If we want to call a class method inside the block we can’t call it directly as Squeel will interpret the method call as a column name. To get around this we need to call my and pass in a block. Anything inside the block will then be evaluated in the original context. To demonstrate this we’ll add a low_stock method to our class and use it in our search. Page 46 class Product < ActiveRecord::Base belongs_to :category def self.search(query) where do (released_at <= Time.zone.now) & ((discontinued_at == nil) | (discontinued_at > Time.zone.now)) & (stock >= my{low_stock}) & (name =~ "%#{query}%") end end def self.low_stock 2 end end This will perform the same search that we had previously. Extending Squeel If we want to customize some of Squeel’s behavior there’s a generator provided that will create an initializer for doing so. $ rails g squeel:initializer create config/initializers/squeel.rb This creates a config file which contains comments explaining the various settings we can change. For example uncommenting the line shown below will add methods to the Hash and Symbol classes to simulate the Metawhere functionality which is useful if you’re transitioning an application from Metawhere to Squeel. Composing Queries with Squeel This is what we really want to do. Suppose that we had the following classes: class User < ActiveRecord::Base has_many :microposts end class Micropost < ActiveRecord::Base belongs_to :user validates :user_id, presence: true validates :content, length: { maximum: 140 } end Example 1: Squeel condition composition inside block similar to Criteria. Write this inside one of your controllers: params = {content: "First Post%", id: [1,2,3,4,5]} @userWithInitialPost = current_user.microposts.where do conditions = {} conditions[id.in] = params[:id] if params[:id] conditions[content.matches] = params[:content] if params[:content] conditions end Example 2: Other complex queries involving joins. Write this include your domain class: def usersWithInitialPosts(firstPost) userList = User.joins(:microposts).where { { microposts => ( ((id.in ([1,2,3])) | content.matches(firstPost)))) } } end Page 47 Appendix H: Rails 12factor Gem Makes running your Rails app easier on Heroku. Based on the ideas behind 12factor.net What it does Rails gets a lot right when it comes to twelve-factor apps, but it could still be better. The two biggest areas right now are that in production logs should be directed to stdout and dev/prod parity while delivering assets. This gem enables serving assets in production and setting your logger to standard out, both of which are required for to run a Rails 4 application on a twelve-factor provider. The gem also makes the appropriate changes for Rails 3 apps. Appendix I: Fog Gem This is a cloud services interface layer. Whether you need compute, dns, storage, or a multitude of other services, fog provides an accessible entry point and facilitates cross service compatibility. The main site is at http://fog.io/ By coding with fog from the start you avoid vendor lock-in and give yourself more flexibility to provide value. Whether you are writing a library, designing a software as a service product or just hacking on the weekend this flexibility is a huge boon. Support for AWS and Rackspace, among others. For a summary from 2010, see http://www.rubyinside.com/fog-apowerful-cloud-services-gem-3375.html For a summary from 2012, see http://sevos.github.io/2012/12/31/basics-offog-and-aws-for-rails-apps.html Example of use for Storage: storage = Fog::Storage.new({ :local_root => '~/fog', :provider => 'Local' }) storage will now contain our storage object, configured to use the Local provider from our specified directory. Now that you have cleared the preliminaries you are ready to start storing data. Storage providers in fog segregate files into directories to make it easier to organize things. So lets create a directory so we can see that in action. directory = storage.directories.create( :key => 'data' ) To make sure it was created you can always check in your filesystem, but we can also check from inside fog. storage.directories Progress! Now it is time to actually create a file inside our new directory. file = directory.files.create( :body => 'Hello World!', :key => 'hello_world.txt' ) Mocking for tests As you might imagine, testing code using Fog can be slow and expensive, constantly turning on and and shutting down instances. Mocking allows skipping this overhead by providing an in memory representation resources as you make requests. Enabling mocking easy to use, before you run other commands, simply run: Fog.mock! Then proceed as usual, if you run into unimplemented mocks, fog will raise an error. Page 48 Appendix J: Tagging Gem We use acts-as-taggable-on See http://railscasts.com/episodes/382-tagging for more details Appendix K: TheRubyRacer Gem Integrates the JavaScript V8 engine into your Rails application. This could be useful in the ChallengeEngine backend, in which the behavior of earning an achievement is configured by the challenge designer using a JavaScript expression that is stored into the database and run at eval time using the V8 engine. Page 49