Ruby Tutorial, Ruby on Rails Tutorial 2016-04-25T20:59:11+02:00 ActionCable Devise Authentication 2015-08-29T00:00:00+02:00 /actioncable-devise-authentication <p>ActionCable is a new framework for real-time communication over websockets and it will be part of Rails 5. I am not going to get into too much detail about it, you can read the very detailed readme of the project on this link: <a href="https://github.com/rails/actioncable" target="_blank">ActionCable</a>.</p> <p>The websockets server is running in a separate process from the main Rails application which means you need to authenticate your users there too. In the <a href="https://github.com/rails/actioncable-examples">example app</a>, David used a simple cookie based authentication in the app itself and re-validated the cookie at the websocket connection. This is good for demonstration, but many of the Rails based apps are using Devise for authentication so I want to share, how I solved the authentication with Devise.</p> <p>The websocket server doesn't have a session, but it can read the same cookies as the main app, so I figured, I will just set a cookie with the user id and verify that at the socket connection. To do this, I used a Warden hook:</p> ```ruby # app/config/initializers/warden_hooks.rb Warden::Manager.after_set_user do |user,auth,opts| scope = opts[:scope] auth.cookies.signed["#{scope}.id"] = user.id end ``` ```ruby # app/channels/application_cable/connection.rb module ApplicationCable class Connection < ActionCable::Connection::Base identified_by :current_user def connect self.current_user = find_verified_user logger.add_tags 'ActionCable', current_user.name end protected def find_verified_user if verified_user = User.find_by(id: cookies.signed['user.id']) verified_user else reject_unauthorized_connection end end end end ``` This is nice and simple, but I needed some sort of a timeout to expire the session, so I set an expiry time too in the cookies: ```ruby # app/config/initializers/warden_hooks.rb Warden::Manager.after_set_user do |user,auth,opts| scope = opts[:scope] auth.cookies.signed["#{scope}.id"] = user.id auth.cookies.signed["#{scope}.expires_at"] = 30.minutes.from_now end ``` ```ruby # app/channels/application_cable/connection.rb ... protected def find_verified_user verified_user = User.find_by(id: cookies.signed['user.id']) if verified_user && cookies.signed['user.expires_at'] > Time.now verified_user else reject_unauthorized_connection end end .... ``` One thing left, is to invalidate the cookie on sign out, which can be done in another Warden hook: ```ruby # app/config/initializers/warden_hooks.rb ... Warden::Manager.before_logout do |user, auth, opts| scope = opts[:scope] auth.cookies.signed["#{scope}.id"] = nil auth.cookies.signed["#{scope}.expires_at"] = nil end ... ``` That's it, now I can share the Devise authentication with my websocket server. If you want to see this in an example, you can check my fork of the <a href="https://github.com/gregmolnar/actioncable-examples" target="_blank">actioncable-example</a>. Rails Fixtures 2014-06-12T00:00:00+02:00 /rails-fixtures Fixtures are the officially supported way to handle test data and lately they are getting some attention finally. I guess I am not the only developer who started with FactoryGirl because there was more tutorials about it that time, and it seemed a really great way to handle test data as a junior ruby developer. As time went by, the projects I was working on grew and I realized that my test suite's speed issues are mostly related to my factories. So I decided to give a shot to Fixtures. It was a quite positive experience and fixtures made a huge impact on the speed of test runs. But enough of the story about me let's see how can you use fixtures with Rails. Fixtures are stored in a yaml file but they are also processed with ERB before sent to the yaml parser, so we can use Ruby to make them dinamic. Let's use the good old blog example: ```yaml # posts.yml post_one: id: 1 title: First Post published_at: <%= 1.day.ago %> post_two: id: 1 title: Second Post published_at: <%= Time.now %> ``` You would also want to handle relations too with your test data and fortunately that's not an issue with fixtures. You can simply use the "label" to reference another fixture: ```yaml # users.yml greg: name: Greg email: ... # posts.yml post_one: title: First Post author: greg published_at: <%= 1.day.ago %> ``` As you see, there is a simple reference to the user model in the `posts.yml` which will setup the relation. You can also implement somewhat of an inheritence in your fixtures by using the `DEFAULTS` label: ```yaml # posts.yml DEFAULTS: &DEFAULTS active: true tags: "foo, bar" post_one: title: First Post author: greg <<: *DEFAULTS post_two: title: Second Post author: greg <<: *DEFAULTS ``` This can be handy if your fixtures shares multiple attributes with the same values. Brandon Hilkert also shared his 7 reason of switching to fixtures and Minitest, which is a great reading: [7 REASONS WHY I'M STICKING WITH MINITEST AND FIXTURES IN RAILS](http://brandonhilkert.com/blog/7-reasons-why-im-sticking-with-minitest-and-fixtures-in-rails/) I hope this little write up will make you to give a try to fixtures if you are not using them now. Rails Caching Again 2014-05-04T00:00:00+02:00 /rails-caching-again In the <a href="http://www.rubytutorial.io/rails-caching/">previous post</a> I covered how can you use Rails' Russian Doll caching to make you app super fast. I didn't cover though how to cache search result pages and paginated results, so here comes the second part of that article. I made a sample application where I have a product listing page with pagination and a search form: [https://github.com/gregmolnar/rails-caching](https://github.com/gregmolnar/rails-caching) <!--more--> Caching of the individual products is simple: ```ruby # app/views/products/index.html.erb <% cache(product) do %> <tr> <td><%= product.name %></td> <td><%= product.price %></td> <td><%= link_to 'Edit', edit_product_path(product) %></td> <td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> ``` But we want to cache the full list too so we need to generate a cache key by ourself. My solution to this problem is to pluck the ids, join them and add the max updated_at value to the end of the string: ```ruby # app/helpers/products_helper.rb module ProductsHelper def cache_key_for_products(products) ids = products.pluck(:id).join('-') max_updated_at = products.pluck(:updated_at).max "products/#{ids}-#{max_updated_at.to_i}" end end ``` Now we can cache a bigger fragment in the view: ```ruby # app/views/products/index.html.erb <%= cache(cache_key_for_products(@products)) do %> <table> <thead> <tr> <th>Name</th> <th>Price</th> <th colspan="2"></th> </tr> </thead> <tbody> <% @products.each do |product| %> <% cache(product) do %> <tr> <td><%= product.name %></td> <td><%= product.price %></td> <td><%= link_to 'Edit', edit_product_path(product) %></td> <td><%= link_to 'Destroy', product, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> <% end %> </tbody> </table> <% end %> ``` This methods works for pagination and sorting too, since it relies on the order of the ids. If there is a search functionality too, all we have to do is to pass a suffix to the helper method: ```ruby # app/helpers/products_helper.rb module ProductsHelper def cache_key_for_products(products, suffix = '') ids = products.pluck(:id).join('-') max_updated_at = products.pluck(:updated_at).max "products/#{ids}-#{max_updated_at.to_i}#{suffix}" end end ``` I would pass the attribute name and the value so if someone is searching for a product name 'Jewel': ```ruby cache_key_for_products(@products, "jewel=#{@search.jewel_eq}") ``` That's it for now. I hope you enjoyed the article. ## Update David Patrick([dponrails](https://twitter.com/dponrails)) did some benchmarks and it turned out `pluck` is pretty slow so here is a better performing alternative would be the usage of map: ```ruby # app/helpers/products_helper.rb module ProductsHelper def cache_key_for_products(products, suffix = '') ids = products.map(&:id).join('-') max_updated_at = products.map(&id).max "products/#{ids}-#{max_updated_at.to_i}#{suffix}" end end ``` Thanks David for the heads up! Rails Caching 2014-04-18T00:00:00+02:00 /rails-caching I've built a Kanban board as a side project and I want to share how much performance boost I was able to give it by implementing a Russian Doll Caching.<br/> The app itself is a typical kanban board where you can setup different stages for the tasks and they are in the column of the appropriate stage. I have a project where I have 4 stages: To-do, In progress, Waiting for review, Done. On this board I have 138 tasks in the moment. According to Rack Metrics this page takes between 1485 and 1678 ms to get ready on the server side. This is way too slow in my opinion so I will dig in a bit more. I randomly picked one of the request to this route and I saw there the following: ``` Duration: 1598.14ms View runtime: 1485.152174ms DB runtime: 78.776503ms Render calls: 140 ``` There are 140 render calls because I use partials and every task on the board triggers a render call. This is a great place to utilise some caching. <br /> <!--more--> First, I need to make sure caching is enabled and set to `mem_cache_store` by putting this line into config/production.rb: ```ruby config.action_controller.perform_caching = true config.cache_store = :mem_cache_store ``` I use memcached for storage because I will have a lot of cache entries, and storing them on the filesystem would cause a lot of IO and it would not give me too much performance win. Another reason to use memcached is it's ability to invalidate the oldest keys first when it runs out of space. Next I need to go to my view and wrap the render calls into a caching block: This: ```ruby <% status.tasks.each do |task| %> <%= render task %> <% end %> ``` Will become this: ```ruby <% status.tasks.each do |task| %> <% cache(task) do %> <%= render task %> <% end %> <% end %> ``` As you see I pass an object to the cache call, which calls the `cache_key` method and that will generate a key based on the `updated_at` field of the object, so our cache will be invalidated automatically when it needs to. Let's see how much improvement this caused. After hitting refresh a few times my response time is between 129 and 159ms. It is around 10% of what it was before which is a huge improvement. If a task is updated only that one will be rendered again so the overall speed of this section is dramatically got better. One important thing to do is to change my relation so when I subtask or comment is added to the task it's cache will be invalidated: ```ruby class Comment < ActiveRecord::Base belongs_to :commentable, polymorphic: true, touch: true end ``` With `touch` true every time a comment is created or updated it will update the task's updated_at field so the cache will be invalidated.<br /> I already made my app perform better but there is one more thing I can do to make it even faster. I could cache the whole view in a fragment and bust the cache when any of the tasks has changed. To make this work I created a helper method to generate a cache_key based on the number of tasks and the updated task: ```ruby module ProjectsHelper def cache_key_for_tasks count = Task.count max_updated_at = Task.maximum(:updated_at).try(:utc).try(:to_s, :number) "tasks/all-#{count}-#{max_updated_at}" end end ``` In the view: ```ruby <% cache(cache_key_for_tasks) do %> ... # rest of the file <% end %> ``` With this in place I made around 30ms improvement.<br /> As you see with clever caching your Rails app can be a lot faster and use less resources.<br /> I hope you enjoyed the article. UPDATE: A second part of this acrticle available at: [http://www.rubytutorial.io/rails-caching-again/](http://www.rubytutorial.io/rails-caching-again/) Rails 4.1 - My favourite changes 2014-03-07T00:00:00+01:00 /rails-4-1 Rails 4.1 is coming out soon and I'd like to summarize which new features I like the most. ### Spring Application Preloader Spring preloads your application which makes your test runs a lot faster. Rails 4.1 generates the binstubs with spring so when you call `rails` or `rake` spring will handle the call. If you want to run a command without spring you can do so by using `bundle rails` or `bundle rake`. Another change I'd like to mention here is two new rake task to run the tests: ``` rake test:all rake test:all:db ``` The first command merges your different types of test(model, controller, integration, mailer) and runs them. It also does not reset your db so your tests should run faster. The second one does the same except it resets the db between the test. ### config/secrets.yml You may have used [figaro](https://github.com/laserlemon/figaro) before to keep the sensitive data out of your repo but from now on Rails provides a built-in solution to this problem. Rails 4.1 generates a secret.yml in the config folder and you can store api keys, etc in it. The syntax as follows: ```yaml development: secret_key_base: 3b7cd727ee24e8444053437c36cc66c3 some_api_key: SOMEKEY ``` If you want to access these in your application you can do so by calling `Rails.application.secrets.some_api_key`. ### Action Pack Variants Mobiles and Tablets are widely used these days and from Rails 4.1 we can easily separate the templates for the different clients. You need to set the variant in your controller: ```ruby case request.user_agent when /iPad/ request.variant = :tablet when /iPhone|Android/ # far from complete. just for the sake of example request.variant = :phone end ``` And in your `respond_to` block you can render a separate view: ```ruby respond_to do |format| format.html do |html| html.tablet # renders app/views/projects/show.html+tablet.erb html.phone { extra_setup_if_needs; render ... } end end ``` ### Active Record enums With this extension to ActiceRecord you can declare an enum field in your model and Rails will handle the mapping of integer values in the database column to human friendly values in your code. ```ruby class Conversation < ActiveRecord::Base enum status: [ :active, :archived ] end ``` It also defines handy utility methods: ```ruby conversation.active? # to see if the record is active conversation.active! # to set the record active ``` It also makes easy to implement a simple state machine. I am already using this feature in production and it works like a charm. ### CSRF protection from remote `script` tags I use RJS to update parts of the applications via ajax and as [homakov](https://twitter.com/homakov) pointed out in the past this was vulnerable to XSS. To fix the security issue Rails will use CSRF protection if the request is not xhr. ### Added Numeric#in_milliseconds In the past I always wrote `x * 1000` to convert timestamps I used on the client-side but now this is handled by this syntax sugar. These are the changes I like the most but you can find an exhaustive list of changes in the [changelog](http://edgeguides.rubyonrails.org/4_1_release_notes.html), thanks to [Yves](https://github.com/senny) and [Godfrey](https://github.com/chancancode). How I test Rails validations? 2014-02-25T00:00:00+01:00 /testing-rails-validations I often get the question from new rubyist "How should I test my model validations in Rails?". A simple example is when we have a `Post` model with two required fields: ```ruby # app/models/post.rb class Post < ActiveRecord::Base validates_presence_of :title, :body end ``` You could write a test like this: ```ruby # test/models/post_test.rb require 'test_helper' class PostTest < ActiveSupport::TestCase test "should not save post without title or body" do post = Post.new assert_not post.save post.title = 'Test' assert_not post.save post.body = 'Test body' assert post.save end end ``` But this test is already too long and what if you have a lot more required fields? To get around this it is better to test if the validation errors are correct: ```ruby # test/models/post_test.rb test "should have the necessary required validators" do post = Post.new assert_not post.valid? assert_equal [:title, :body], post.errors.keys end ``` Now we cover the existence of the validators with a lot shorter and simpler test. We can use the same approach to test ther validation rules like `numericality`: ```ruby # app/models/post.rb class Post < ActiveRecord::Base validates_presence_of :title, :body validates :score, numericality: true, allow_blank: true end ``` ```ruby # test/models/post_test.rb test "should have numeric score" do post = Post.new(title: 'test', body: 'test body', score: 'test') assert_not post.valid? assert_equal ["is not a number"], post.errors.messages[:score] end ``` And so on. As some of the readers pointed out if you need a full blown solution with support of `on` and etcetera thoughbot's [shoulda-matchers](https://github.com/thoughtbot/shoulda-matchers) is has all that. How to test your gem against multiple version of Rails? 2014-02-19T00:00:00+01:00 /how-to-test-your-gem-against-multiple-rails If you develop a gem which integrates with Rails you may want to test against multiple versions of it. My solution to this to use one page Rails applications with the appraisals gem and load the applications based on the actual gem version. Let me show you an example: ```bash bundle gem example create example/Gemfile create example/Rakefile create example/LICENSE.txt create example/README.md create example/.gitignore create example/example.gemspec create example/lib/example.rb create example/lib/example/version.rb Initializing git repo in ~/Github/example ``` We generated a gem skeleton. Let's prepare the gem for testing. We need to add some dependencies to the gemspec: ```ruby # example.gemspec ... spec.add_development_dependency 'minitest', '>= 3' spec.add_development_dependency 'appraisal' ... ``` <!--more--> Create the test folder with the test helper: ```ruby # test/test_helper.rb require 'bundler/setup' require 'minitest/autorun' require 'example' ENV["RAILS_ENV"] = "test" ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:' require "apps/rails4" ``` Setup rake to run the tests: ```ruby require "bundler/gem_tasks" require 'rake/testtask' require 'appraisal' Rake::TestTask.new do |t| t.libs = ["test"] t.pattern = "test/**/*_test.rb" end task :default => :test ``` Create an `Appraisal` file: ```ruby # Appraisals appraise "rails-4" do gem "rails", "4.0.0" gem "sqlite3" end ``` Than run `bundle install` and `rake appraisal:install` to generate the gemfile. The official Rails guide advises to use a `dummy` Rails app for testing but in their guide it is a structured setup and I think it is better to keep everything in one file in this scenario so we will create a one file Rails application: ```ruby # test/apps/rails4.rb require "rails" require 'rails/all' require 'action_view/testing/resolvers' require 'rails/test_help' require 'example' # our gem module Rails4 class Application < Rails::Application config.root = File.expand_path("../../..", __FILE__) config.cache_classes = true config.eager_load = false config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" config.consider_all_requests_local = true config.action_controller.perform_caching = false config.action_dispatch.show_exceptions = false config.action_controller.allow_forgery_protection = false config.active_support.deprecation = :stderr config.middleware.delete "Rack::Lock" config.middleware.delete "ActionDispatch::Flash" config.middleware.delete "ActionDispatch::BestStandardsSupport" config.secret_key_base = '49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk' routes.append do get "/" => "welcome#index" end end end class WelcomeController < ActionController::Base include Rails.application.routes.url_helpers layout 'application' self.view_paths = [ActionView::FixtureResolver.new( "layouts/application.html.erb" => '<%= yield %>', "welcome/index.html.erb"=> 'Hello from index.html.erb', )] def index end end Rails4::Application.initialize! ``` Than write an integration test which uses this controller: ```ruby # test/integration/welcome_controller_test.rb require 'test_helper' class WelcomeControllerTest < ActionController::TestCase test "should get index and our gem should do it's job" do get :index assert_response :success # do your gem specific assertion end end ``` If we run `rake appraisal` now it will run this test and it will pass. Next step is to create a Rails 3 test app. First we need to setup the appraisal for that: ```ruby # Appraisals ... appraise "rails-3_2" do gem "rails", "3.2.14" gem "sqlite3" gem "test-unit" end ``` Run `rake appraisal:install`. We need to change our test helper to switch beetween the Rails apps based on the Rails version: ```ruby # test/test_helper.rb require 'bundler/setup' require 'minitest/autorun' require 'example' ENV["RAILS_ENV"] = "test" ENV['DATABASE_URL'] = 'sqlite3://localhost/:memory:' require "rails" case Rails.version when '3.2.14' require 'test/unit' require "apps/rails3_2" when '4.0.0' require "apps/rails4" end ``` Than we need to create the test application: ```ruby # test/apps/rails3_2.rb require 'rails/all' require 'action_view/testing/resolvers' require 'rails/test_help' require 'example' # our gem class Rails3_2 < Rails::Application config.root = File.expand_path("../../..", __FILE__) config.cache_classes = true config.eager_load = false config.serve_static_assets = true config.static_cache_control = "public, max-age=3600" config.consider_all_requests_local = true config.action_controller.perform_caching = false config.action_dispatch.show_exceptions = false config.action_controller.allow_forgery_protection = false config.active_support.deprecation = :stderr config.middleware.delete "Rack::Lock" config.middleware.delete "ActionDispatch::Flash" config.middleware.delete "ActionDispatch::BestStandardsSupport" config.secret_token = "49837489qkuweoiuoqwehisuakshdjksadhaisdy78o34y138974xyqp9rmye8yrpiokeuioqwzyoiuxftoyqiuxrhm3iou1hrzmjk" routes.append do get "/" => "welcome#index" end end class WelcomeController < ActionController::Base include Rails.application.routes.url_helpers layout 'application' self.view_paths = [ActionView::FixtureResolver.new( "layouts/application.html.erb" => '<%= yield %>', "welcome/index.html.erb"=> 'Hello from index.html.erb', )] def index end end Rails3_2.initialize! ``` Now if you ru `rake appraisal` your test suite will be run against both versions of Rails'. The repo for the source of this example: [multiple-rails-test-example](https://github.com/gregmolnar/multiple-rails-test-example) How to test an autocomplete with Rails and Minitest? 2013-10-29T00:00:00+01:00 /how-to-test-an-autocomplete-with-rails An autocomplete is a nice example for an ajax driven feature and I will demonstrate how to test such a features with Rails 4 and Minitest. First we will create a sample app and setup Minitest and Capybara for integration testing. ```ruby rails new rails-autocomplete-test ``` We need to add `capybara` and the `poltergeist` driver to our Gemfile: ```ruby group :development, :test do gem "capybara" gem 'poltergeist' end ``` After we ran bundle we need make some changes to the `test_helper.rb`: ```ruby require "capybara/rails" class ActionDispatch::IntegrationTest include Capybara::DSL require 'capybara/poltergeist' Capybara.javascript_driver = :poltergeist def teardown Capybara.current_driver = nil end end class ActiveRecord::Base mattr_accessor :shared_connection @@shared_connection = nil def self.connection @@shared_connection || retrieve_connection end end ActiveRecord::Base.shared_connection = ActiveRecord::Base.connection ``` First we require the `capybara/rails` module than set the javascript driver to `poltergeist`. I am also setting the current\_driver of capybara to `nil` after each test because I want to use poltergeist only when we are testing javascript features so the rest of the test suite can run faster. Than we make `ActiveRecord` to share the same connection between threads because capybara starts the browser in a different thread from the one our application uses and it wouldn't access to the same data in these threads. If you want to know more why we need do do this you can read Jose Valim's explanation on <a href="http://blog.plataformatec.com.br/2011/12/three-tips-to-improve-the-performance-of-your-test-suite" target="_blank">this link</a>. Now we have a setup to test our javascript features so let's write a test for an autocomplete form field.<!--more--> We will expect a form with a search field on the root path: ```ruby require 'test_helper' class AutocompleteTest < ActionDispatch::IntegrationTest test "autocomplete" do Capybara.current_driver = Capybara.javascript_driver visit "/" fill_in('search_keyword', with: 'Test') end end ``` If we run the test it will fail of course: ``` 1) Error: AutocompleteTest#test_autocomplete: Capybara::ElementNotFound: Unable to find field "search_keyword" test/integration/autocomplete_test.rb:8:in `block in <class:AutocompleteTest>' 1 tests, 0 assertions, 0 failures, 1 errors, 0 skips ``` Let's fix this and generate a controller with an action and add the missing form to the view. We also need to set the root of our application to that action: ```ruby # config/routes.rb root 'welcome#index' ``` ```bash rails g controller Welcome index create app/controllers/welcome_controller.rb route get "welcome/index" invoke erb create app/views/welcome create app/views/welcome/index.html.erb invoke test_unit create test/controllers/welcome_controller_test.rb invoke helper create app/helpers/welcome_helper.rb invoke test_unit create test/helpers/welcome_helper_test.rb invoke assets invoke coffee create app/assets/javascripts/welcome.js.coffee invoke scss create app/assets/stylesheets/welcome.css.scss ``` ``` # app/views/welcome/index.html.erb <%= form_for :search do |f| %> <%= f.label :keyword %> <%= f.text_field :keyword %> <% end %> ``` Now our test is passing. Next step is to extend our test to fill the form with something and see the autocomplete with some suggestion. Before we go any further we need to decide which javascript library to use as we need to know the markup it will use. I chose <a href="https://github.com/twitter/typeahead.js" target="_blank">twitter's typeahead</a> for this tutorial. We can copy the typeahead.js to the vendor/assets/javascripts folder and than we just need to require it in the application.js file: ``` app/assets/javascripts/application.js ... //= require typeahead ... ``` This library uses the following markup: ```html <span class="tt-dropdown-menu"> <div class="tt-dataset-"> } <span class="tt-suggestions"> <div class="tt-suggestion">}</div> </span> } </div> </span> ``` It means we need to look for a div.tt-suggestion element after we entered the text to the field and compare the text of the element with the desired string: ```ruby require 'test_helper' class AutocompleteTest < ActionDispatch::IntegrationTest test "autocomplete" do Capybara.current_driver = Capybara.javascript_driver visit "/" field = 'search_keyword' fill_in('search_keyword', with: 'Test') page.execute_script %Q{ $('##{field}').trigger("focus") } suggestion = page.find('div.tt-suggestion') assert_equal "Test", suggestion.text end end ``` After we filled in the field we need to trigger the `focus` event. Of course this test fails in the moment so let's make it pass. First step is to setup a route and an action for the source of the suggestions: ```ruby # config/routes.rb match 'suggestions' => 'welcome#suggestions', via: :get ``` ```ruby # app/controller/welcome_controller.rb def suggestions render json: [{name: 'Test'}] end ``` We just render a hash as json for the sake of simplicity. Than we add some coffee to initialize typeahead: ```coffee $ -> $('#search_keyword').typeahead [ { name: 'name' remote: { url: '/suggestions.json?q=%QUERY' } valueKey: 'name' } ] ``` Now if we run the test it will pass. I hope you learned how to test your javascript features with capybara and minitest. You can view the code of the sample application on this <a href="https://github.com/gregmolnar/rails-autocomplete-test">link</a>. Page caching with Rails 4 2013-10-20T00:00:00+02:00 /page-caching-with-rails4 The built-in page caching has been extracted to a separate gem in Rails 4 and here is a guide how to use it. First we need to add the gem to our Gemfile: ```ruby gem 'actionpack-page_caching' ``` Than in the application controller we need to specify the folder to store our cache files: ```ruby class ApplicationController < ActionController::Base include ActionController::Caching::Pages self.page_cache_directory = "#{Rails.root.to_s}/public/page_cache" end ``` Let's say we have an article controller and we want to cache the index and the show action: ```ruby class ArticleController < ApplicationController caches_page :index, :show # Rest of the file omitted. end ``` <!--more--> Now if we have the `config.action_controller.perform_caching` set to `true` Rails will generated the HTML output of the pages in the cache folder. One more thing we need to do is to tell the webserver to use the cached version if there is one so the request won't even hit our app. With nginx we can achieve this with a configuration like this: ``` upstream puma_server_domain_tld { server unix:/path/to/the/puma/socket; } server { listen 80; server_name domain.tld; root /path/to/the/app; location / { proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_set_header Host $http_host; proxy_redirect off; # try the $uri, than the uri inside the cache folder, than the puma socket try_files $uri /page_cache/$uri /page_cache/$uri.html @puma; } location @puma{ proxy_pass http://puma_server_domain_tld; break; } # set the expire date to max for assets location ~ "^/assets/(.*/)*.*-[0-9a-f]{32}.*" { gzip_static on; expires max; add_header Cache-Control public; } } ``` Now we managed to serve the cached version of our pages but we need a way to flush the cache when it needs. To achieve this we will user a Sweeper. In Rails 4 the cache sweepers are also moved to a separate gem with the observers so we need to add the it to the Gemfile: ```ruby gem 'rails-observers' ``` Than we need to create a file in the app/sweepers folder called article_sweeper.rb and call the `expire_page` method with a reference to the page we want to flush the cache for. Let's say we use a user friendly url field for the articles and the cache files will be named after that field. In this case to expire those we will need to pass the raw url string to the method: ```ruby class ArticleSweeper < ActionController::Caching::Sweeper observe Article def after_save(record) expire_page(articles_path) expire_page("/#{record.url}") end end ``` That's all, we achieved to setup the page caching for our Rails 4 app. ## Resources <a href="https://github.com/rails/actionpack-page_caching" target="_blank">actionpack-page_caching</a> <a href="https://github.com/rails/rails-observers" target="_blank">rails-observers</a> Rails' rescue_from 2013-10-02T00:00:00+02:00 /rails-rescue_from rescue_from is a very useful method in Rails. It lets us to catch exceptions and pass them to a callback or a block. A typical usecase is to handle `ActiveRecord::RecordNotFound` errors like in this example: ```ruby FooController < ActionController::Base rescue_from ActiveRecord::RecordNotFound, with: not_found private def notfound message = "Foo with ID #{params[:id]} not found." logger.error message redirect_to not_found_url, info: message end end ``` In the example above whenever an `ActiveRecord::RecordNotFound` raised in the scope of the `FooController` it will be caught and the notfound method will log the event than redirect to the notfound page with a message to display in the browser. Since `rescue_from` works with a block too we can refactor the above as follows: ```ruby FooController < ActionController::Base rescue_from ActiveRecord::RecordNotFound do |exception| message = "Foo with ID #{params[:id]} not found." logger.error message redirect_to not_found_url, info: message end end ``` Another case when rescue_from comes handy is when we use cancan for authorization and we want to handle the authorization errors. We can do so by add the following to the application controller: ```ruby rescue_from CanCan::AccessDenied do |exception| redirect_to root_url, :alert => exception.message end ``` As you see in this case we display the exception message set by cancan. If you want to use rescue_from from in a class which does not inherit from `ActionController::Base` you just need to mixin the `ActiveSupport::Rescuable`: ```ruby class Foo include ActiveSupport::Rescuable end ``` Travis CI Multi Database Rails 2013-09-27T00:00:00+02:00 /travis-ci-multiple-database I started to use <a href="http://travis-ci.org">Travis CI</a> for continous integration. They have a free plan for open source which makes a good opportunity to try out the service. Travis supports various runtimes and databases and I was going to test my app against ruby 1.9 and ruby 2 and 3 different databases: sqlite, mysql, postgres. To setup these runtimes I created a travis.yml config file like this: ```yaml language: ruby rvm: - 2.0.0 - 1.9.3 env: - DB=sqlite - DB=mysql - DB=postgresql ``` We need a bit more work to make this work though as we need to specify the database config for each runtime. In my Gemfile I decide which database gem to bundle from the database config but on travis that won't exists so the bundle command will fail.<!--more--> Travis sets a `DB` environment variable which we can use to determine which gem to bundle: ```ruby require 'yaml' env = ENV["RAILS_ENV"] || 'development' dbconfig = File.expand_path("../config/database.yml", __FILE__) raise "You need to configure config/database.yml first" unless File.exists?(dbconfig) require 'erb' config = YAML.load(ERB.new(File.read(dbconfig)).result) environment = config[env] adapter = environment['adapter'] if environment raise "Please set an adapter in database.yml for #{env} environment" if adapter.nil? case adapter when 'sqlite3' gem 'sqlite3' when 'postgresql' gem 'pg' when 'mysql2' gem 'mysql2' else raise "Not supported database adapter: #{adapter}" end ``` Note that we process the database.yml with ERB first. It needs because we use the `ENV['DB']` variable in the yaml file. I don't have a database.yml in source control just an example for the different database options and I thought the best way would be to keep all the travis related db config in a separate file so I created a database.travis.yml file: ```yaml sqlite: &sqlite adapter: sqlite3 database: db/<%= ENV['RAILS_ENV'] %>.sqlite3 mysql: &mysql adapter: mysql2 username: root password: database: invoicer_<%= ENV['RAILS_ENV'] %> postgresql: &postgresql adapter: postgresql username: postgres password: database: invoicer_<%= ENV['RAILS_ENV'] %> min_messages: ERROR defaults: &defaults pool: 5 timeout: 5000 host: localhost <<: *<%= ENV['DB'] || "sqlite" %> development: <<: *defaults test: <<: *defaults production: <<: *defaults ``` With this in place we just need to extend the travis config to set the rails environment and run a before script: ```yaml language: ruby rvm: - 2.0.0 - 1.9.3 env: - DB=sqlite - DB=mysql - DB=postgresql before_install: cp config/database.travis.yml config/database.yml script: - export RAILS_ENV=test - bundle exec rake db:create db:migrate - bundle exec rake db:test:prepare ``` Now we just need to push a commit and wait for the build result.