Best ruby-on-rails questions in March 2012

API Versioning for Rails Routes

51 votes

I'm trying to version my API like Stripe has. Below is given the latest API version is 2.

/api/users returns a 301 to /api/v2/users

/api/v1/users returns a 200 of users index at version 1

/api/v3/users returns a 301 to /api/v2/users

/api/asdf/users returns a 301 to /api/v2/users

So that basically anything that doesn't specify the version links to the latest unless the specified version exists then redirect to it.

This is what I have so far:

scope 'api', :format => :json do
  scope 'v:api_version', :api_version => /[12]/ do
    resources :users
  end

  match '/*path', :to => redirect { |params| "/api/v2/#{params[:path]}" }
end

The original form of this answer is wildly different, and can be found here. Just proof that there's more than one way to skin a cat.

I've updated the answer since to use namespaces and to use 301 redirects -- rather than the default of 302. Thanks to pixeltrix and Bo Jeanes for the prompting on those things.


You might want to wear a really strong helmet because this is going to blow your mind.

The Rails 3 routing API is super wicked. To write the routes for your API, as per your requirements above, you need just this:

namespace :api do
  namespace :v1 do
    resources :users
  end

  namespace :v2 do
    resources :users
  end
  match 'v:api/*path', :to => redirect("/api/v2/%{path}")
  match '*path', :to => redirect("/api/v2/%{path}")
end

If your mind is still intact after this point, let me explain.

First, we call namespace which is super handy for when you want a bunch of routes scoped to a specific path and module that are similarly named. In this case, we want all routes inside the block for our namespace to be scoped to controllers within the Api module and all requests to paths inside this route will be prefixed with api. Requests such as /api/v2/users, ya know?

Inside the namespace, we define two more namespaces (woah!). This time we're defining the "v1" namespace, so all routes for the controllers here will be inside the V1 module inside the Api module: Api::V1. By defining resources :users inside this route, the controller will be located at Api::V1::UsersController. This is version 1, and you get there by making requests like /api/v1/users.

Version 2 is only a tiny bit different. Instead of the controller serving it being at Api::V1::UsersController, it's now at Api::V2::UsersController. You get there by making requests like /api/v2/users.

Next, a match is used. This will match all API routes that go to things like /api/v3/users.

This is the part I had to look up. The :to => option allows you to specify that a specific request should be redirected somewhere else -- I knew that much -- but I didn't know how to get it to redirect to somewhere else and pass in a piece of the original request along with it.

To do this, we call the redirect method and pass it a string with a special-interpolated %{path} parameter. When a request comes in that matches this final match, it will interpolate the path parameter into the location of %{path} inside the string and redirect the user to where they need to go.

Finally, we use another match to route all remaining paths prefixed with /api and redirect them to /api/v2/%{path}. This means requests like /api/users will go to /api/v2/users.

I couldn't figure out how to get /api/asdf/users to match, because how do you determine if that is supposed to be a request to /api/<resource>/<identifier> or /api/<version>/<resource>?

Anyway, this was fun to research and I hope it helps you!

Find unused code in a Rails app

20 votes

How do I find what code is and isn't being run in production ?

The app is well-tested, but there's a lot of tests that test unused code. Hence they get coverage when running tests... I'd like to refactor and clean up this mess, it keeps wasting my time. I have a lot of background jobs, this is why I'd like the production env to guide me. Running at heroku I can spin up dynos to compensate any performance impacts from the profiler.

Related question How can I find unused methods in a Ruby app? not helpful.

Bonus: metrics to show how often a line of code is run. Don't know why I want it, but I do! :)

Under normal circumstances the approach would be to use your test data for code coverage, but as you say you have parts of your code that are tested but are not used on the production app, you could do something slightly different.

Just for clarity first: Don't trust automatic tools. They will only show you results for things you actively test, nothing more.

With the disclaimer behind us, I propose you use a code coverage tool (like rcov or simplecov for Ruby 1.9) on your production app and measure the code paths that are actually used by your users. While these tools were originally designed for measuring test coverage, you could also use them for production coverage

Under the assumption that during the test time-frame all relevant code paths are visited, you can remove the rest. Unfortunately, this assumption will most probably not fully hold. So you will still have to apply your knowledge of the app and its inner workings when removing parts. This is even more important when removing declarative parts (like model references) as those are often not directly run but only used for configuring other parts of the system.

Another approach which could be combined with the above is to try to refactor your app into distinguished features that you can turn on and off. Then you can turn features that are suspected to be unused off and check if nobody complains :)

And as a final note: you won't find a magic tool to do your full analysis. That's because no tool can know whether a certain piece of code is used by actual users or not. The only thing that tools can do is create (more or less) static reachability graphs, telling you if your code is somehow called from a certain point. With a dynamic language like Ruby even this is rather hard to achieve, as static analysis doesn't bring much insight in the face of meta-programming or dynamic calls that are heavily used in a rails context. So some tools actually run your code or try to get insight from test coverage. But there is definitely no magic spell.

So given the high internal (mostly hidden) complexity of a rails application, you will not get around to do most of the analysis by hand. The best advice would probably be to try to modularize your app and turn off certain modules to test f they are not used. This can be supported by proper integration tests.

Why couldn't twitter scale by adding servers the way sites like facebook have?

9 votes

I have been looking for an explanation for why twitter had to migrate part of its middle ware from Rails to Scala. What prevented them from scaling the way facebook has, by adding servers as its user base expanded. More specifically what about the Ruby/Rails technology prevented the twitter team from taking this approach?

It's not that Rails doesn't scale, but rather, requests for "live" data in Ruby (or any interpreted language) do not scale, as they are comparatively far more expensive both in terms of CPU & memory utilization than their compiled language counterparts.

Now, were Twitter a different type of service, one that had the same enormous user base, but served data that changed less frequently, Rails could be a viable option via caching; i.e. avoiding live requests to the Rails stack entirely and offloading to front end server and/or in-memory DB cache. An excellent article on this topic:

How Basecamp Next got to be so damn fast

However, Twitter did not ditch Rails for scaling issues alone, they made the switch because Scala, as a language, provides certain built-in guarantees about the state of your application that interpreted languages cannot provide: if it compiles, time wasting bugs such as fat-fingered typos, incorrect method calls, incorrect type declarations, etc. simply cannot exist.

For Twitter TDD was not enough. A quote from Dikstra in Programming in Scala illustrates this point: "testing can only prove the presence of errors, never their absence". As their application grew, they ran into more and more hard to track down bugs. The magical mystery tour was becoming a hindrance beyond performance, so they made the switch. By all accounts an overwhelming success, Twitter is to Scala what Facebook is to PHP (although Facebook uses their own ultra fast C++ preprocessor so cheating a bit ;-))

To sum up, Twitter made the switch for both performance and reliability. Of course, Rails tends to be on the innovation forefront, so the 99% non-Twitter level trafficked applications of the world can get by just fine with an interpreted language (although, I'm now solidly on the compiled language side of the fence, Scala is just too good!)

Duplicating class in the object space object_id

7 votes

I having a strange issue where certain models in a rails engine I am using are getting duplicated in the object space.

(rdb:1) ObjectSpace.each_object(::Class).each { |klass| puts klass.to_s + ": " + klass.object_id.to_s if klass.to_s.eql?("DynamicFieldsets::Field") }
DynamicFieldsets::Field: 66866100
DynamicFieldsets::Field: 71836380
2479

When this happens, I cannot use is_a? or equality checks to test that an object is an instance of the Field class. The problem only happens in development and it looks like it may be caused by cache_classes being off. I think the object from the previous request is still in the object space but I am not sure how to remove it.

This is easy to reproduce with remove_const:

class X
  def self.foo
    "hello"
  end
end
first_x = X.new

Object.send :remove_const, :X
class X
  def self.foo
    "world"
  end
end
second_x = X.new

p first_x.class, first_x.class.object_id, second_x.class, second_x.class.object_id
  # => X, <an_id>, X, <another_id>
p first_x.class.foo, second_x.class.foo
  # => "hello", "world"

As you stated, you get this symptom only in development. When Rails reloads the classes, it simply calls remove_const on the defined classes, to force them to be reloaded (using autoload). Here's the code. Rails will actually call DynamicFieldsets::Field.before_remove_const if it is defined, as explained here, how nice :-)

These should be garbage collected and you can trigger the GC with GC.start, but if you have instances of the old classes lying around (like first_x in my example), or subclasses, the old classes can not be garbage collected.

Note that is_a? should work fine, in the sense that new instances will be kind_of? and is_a? of the new class. In my example:

first_x.is_a? X  # => false
second_x.is_a? X # => true

This is the right behavior, as X refers to the new class, not the old class.

Really slow testing with file uploads

7 votes

I just added validations for a carrierwave image to a model and now tests run really slow. How can I speed up this process? I feel like there must be a better way.


I've been running without validations and used to be able to run through my rspec tests in about 140 seconds, but since i now validate presence of :display_pic I've had to add real file uploads to my project factory. This has upped it to 240 seconds! 140 was already on the heavy side, this is just crazy.

This is how the carrierwave github page recommends setting up Factory Girl:

FactoryGirl.define do
  factory :project do
    display_pic { File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) }
  end
end

I made the above test.jpg just an empty text file, so its essentially as small a file as possible.

I also followed the carrierwave recommendation to setup testing:

CarrierWave.configure do |config|
  config.storage = :file
  config.enable_processing = false
end

Please help!

With validation happening now always that a instance is created the attribute display_pic is accessed and the code inside the brackets

{ File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) } 

will be executed (it is lazily executed). This is causing the difference in time.

An option to avoid this is to set to_create for the factory definition what i don't recommend:

FactoryGirl.define do
  factory :project do
    display_pic { File.open(File.join(Rails.root, 'spec', 'support', 'projects', 'display_pics', 'test.jpg')) }

    to_create do |instance|
      instance.save!(:validate => false)
    end 
  end
end

Not understanding Classes, Modules, and the class << self method

7 votes

I have the following code:

class MyClass  
  module MyModule
    class << self

      attr_accessor :first_name

      def myfunction
        MyModule.first_name = "Nathan"
      end

    end
  end
end

When I call the method myfunction like so, it works fine:

> me = MyClass::MyModule.myfunction
=> "Nathan"
> me
=> "Nathan"

But if I removed the class << self and add a self. prefix to myfunction, it doesn't work.

For example:

class MyClass  
  module MyModule

    attr_accessor :first_name

    def self.myfunction
      MyModule.first_name = "Nathan"
    end

  end
end


> me = MyClass::MyModule.myfunction
NoMethodError: undefined method `first_name=' for MyClass::MyModule:Module

I'm trying to understand the class << self method. I thought it was a way add the self. prefix to the all the methods inside of it, but if that was true, why doesn't it work if I remove it and prefix each method with self. manually?

Thanks in advance for your help.

This is because your attr_accessor :first_name is also wrapped by the class << self.

To do it the way you suggest, you can use mattr_accessor like so:

require 'active_support'

class MyClass  
  module MyModule

    mattr_accessor :first_name

    def self.myfunction
      MyModule.first_name = "Nathan"
    end

  end
end

Use one action for multiple models

6 votes

I have about 5 models that behave very similarly. In fact, I'd like them to share an action for displaying them. For example, for models Car, Truck, Van I want to have a definition like:

[Car, Truck, Van].each do |Model|
  action_for Model do #I made this up to show what I mean
    def index
      @model = Model.all
      @model_names = @model.map(&:name).join(', ')
    end
  end
end

How would I do this so I'm not defining the same action in multiple controllers? (Which isn't very DRY) Would it be in the application_controller? And if it's not too much to ask, how could I do this so they also share the view?

UPDATE

It would be preferred if this can be outside the individual controllers. If I can get this to work right, I'd like to not even have to generate the individual controllers.

Couldn't find something that met all my needs, so I wrote a custom RB script to populate my controllers like so:

["cars", "trucks", "vans"].each do |method|

code = <<END
class #{method.capitalize}Controller < ApplicationController
    def index 
        @models = #{method.capitalize.gsub(/s$/, '')}.all
        render 'shared/display'
    end
end
END

  `(echo "#{code}") > app/controllers/#{method}_controller.rb`
end

Then I put my view file in app/views/shared. I just re-run the script when I want to make global changes.

What is the real benefit of scopes

6 votes

I've looked at over 10 pages trying to find the benefit of a scope over any other ActiveRecord class method that returns an ActiveRecord::Relation.

In the following for example why are scopes better than the alternative below it which do the same thing:

  #scope :pat1,  lambda {{:conditions => ["name like ?", 'J%']}}    
  #scope :pat2,  lambda {{:conditions => ["id  > 5"]}}  

  def self.pat1
    where("name like ?", 'J%')
  end  

  def self.pat2 
    where("id  > 5")
  end  

  def patx 
    self.class.pat1.pat2.first
  end

The documentation over and over again says that scopes are beneficial because they can be chained...

"All scope methods will return an ActiveRecord::Relation object which will allow for further methods (such as other scopes) to be called on it." -guides.rubyonrails.org

"The main reason scopes are better than plain class methods is that they can be chained with other methods" http://ruby.railstutorial.org

...but the alternative above can also be chained producing the same results.

Just trying to figure out if there's an emperor's new clothes thing going on here. Even from a syntactic standpoint there appears to be no benefit. Are they faster- some sources vaguely suggest that.

ActiveRecord scopes are really just syntax sugar wrapped in a best practice, as noted already.

In the 2.x days of Rails, when they were called "named_scope", they mattered a bit more. They allowed easy chaining of conditions for generating a query. With the improvements in Rails 3.x with Arel, it is simple to create functions for query relations, as you noted. Scopes just provide a simple and elegant solutions for chainable, predefined queries. Having all the scopes at the top of a model improves the readability and helps shows how the model is used.

Ruby: file encryption/decryption with private/public keys

6 votes

I search for algorithm for file encryption/decryption which satisfies following requirements:

  • Algorithm must be reliable
  • Algorithm should be fast for rather big files
  • Private key can be generated by some parameter (for example, password)
  • Generated private key must be compatible with public key (public key is generated only once and stored in database)

Is there any ruby implementation of suggested algorythms? Any gem?

Note Well: As emboss mentions in the comments, this answer is a poor fit for an actual system. Firstly, file encryption should not be carried out using this method (The lib provides AES, for example.). Secondly, this answer does not address any of the wider issues that will also affect how you engineer your solution.

The original source also goes into more details.

Ruby can use openssl to do this:

#!/usr/bin/env ruby

# ENCRYPT

require 'openssl'
require 'base64'

public_key_file = 'public.pem';
string = 'Hello World!';

public_key = OpenSSL::PKey::RSA.new(File.read(public_key_file))
encrypted_string = Base64.encode64(public_key.public_encrypt(string))

And decrypt:

#!/usr/bin/env ruby

# DECRYPT

require 'openssl'
require 'base64'

private_key_file = 'private.pem';
password = 'boost facile'

encrypted_string = %Q{
...
}

private_key = OpenSSL::PKey::RSA.new(File.read(private_key_file),password)
string = private_key.private_decrypt(Base64.decode64(encrypted_string))

from here

How to use modules in Rails application

5 votes

I just created a module location.rb inside /lib folder with following contents:

module Location
  def self.my_zipcode()
    zip_code = "11215"
  end
end

And now in my controller i am trying to call "my_zipcode" method:

class DirectoryController < ApplicationController
  def search
    require 'location'
    zip_code = Location.my_zipcode()
  end
end

But it throws an error:

undefined method `my_zipcode' for Location:Module

You might have to restart the rails server for it to recognize stuff in the lib directory.

Issue with will_paginate page links

4 votes

I currently have a comment model that posts under a micropost and both are displayed on the same page. The issue is that both are displayed on the same page and both are paginated and I am trying to go for the facebook approach to microposting. Here is the issue below:

The links for both pagination turns into this href="/users/2?page=2" rather than href="/users/2/micropost?page=2" or href="/users/2/comment?page=2". I am unsure how to go about solving this problem. Here are some of my code. All suggestions are much appreciated!

Micropost Render HTML

<table class="microposts">
<% if microposts.any? %>
<%= render microposts %>
<%= will_paginate microposts, :page_links => false %>
<% else %>
<div class="EmptyContainer"><span class='Empty'>Add a thread!</span></div>
<% end %>
</table>

Comment Section HTML

<div id='CommentContainer-<%= micropost.id%>' class='CommentContainer Condensed2'>
<div class='Comment'>
<%= render :partial => "comments/form", :locals => { :micropost => micropost } %>
</div>
<div id='comments'>
  <% comments = micropost.comments.paginate(:per_page => 5, :page => params[:page]) %>
  <%= render comments %>
  <%= will_paginate comments, :class =>"pagination" %>
</div>
</div>

User Controller for the Show Page

  def show
    @user = User.find(params[:id])
    @comment = Comment.find(params[:id])
    @micropost = Micropost.new
    @comment = Comment.new
    @comment = @micropost.comments.build(params[:comment])
    @comments = @micropost.comments.paginate(:page => params[:page], :per_page => 5)
    @microposts = @user.microposts.order('created_at DESC').paginate(:per_page => 10, :page => params[:page])
      respond_to do |format|
      format.html
      format.js
     end
  end

Problem lies within will_paginate way of creating urls for each page (it doesn't have anything to do with jQuery).

By design, will_paginate try its best to guess what's the base url for the page user is on (internally it's using controller/action to do that). That base url is then combined with any extra params passed to will_paginate helper using :params and succesive page numbers.

For now (will_paginate 3.0.3), in order to overwrite this default behavior, you need to write your custom LinkRenderer class. Below there's example of such class - it makes use of new, extra option :base_link_url that can be passed to will_paginate view helper. Passed string is then used as a base when creating pagination links. If :base_link_url option is not passed, it will fallback to default behavior.

Put following class somewhere rails can find it on load (/lib for example, provided you've added /lib to your autoload paths in application.rb):

# custom_link_renderer.rb
class CustomLinkRenderer < WillPaginate::ActionView::LinkRenderer
  def prepare(collection, options, template)
    @base_link_url = options.delete :base_link_url
    @base_link_url_has_qs = @base_link_url.index('?') != nil if @base_link_url
    super
  end

  protected
  def url(page)
    if @base_link_url.blank?
      super
    else
      @base_url_params ||= begin
        merge_optional_params(default_url_params)
      end

      url_params = @base_url_params.dup
      add_current_page_param(url_params, page)

      query_s = []
      url_params.each_pair {|key,val| query_s.push("#{key}=#{val}")}

      if query_s.size > 0
        @base_link_url+(@base_link_url_has_qs ? '&' : '?')+query_s.join('&')
      else
        @base_link_url
      end
    end
  end
end

Usage:

# in your view
will_paginate collection, :renderer => CustomLinkRenderer, :base_link_url => '/anything/you/want'

And now back to your case. By this time you probably see the solution - you can have two will_paginate widgets on one page with different base urls by passing different :base_link_url options for those two.

Rails JS Response, render both status and template?

4 votes

In my current rails app I'm using an ajax uploader library that requires you respond with json {success:true} to indicate a file was uploaded successfully.

In my app the files are images, and after upload I would like to add this to the page. Normally I would do something like:

respond_to do |format|
  format.html { redirect_to @resource, :notice => 'Created.' }
  format.js
end

In the above example my JS response would render the appropriate template, such as create.js.erb which might say something like...

$('#resources').append('<%= escape_javascript( render '@resource' ) %>');

This is how I have done things like Ajax comments etc. in the past. Now, to get the uploader working my respond_to block currently does this:

format.js { render :json => { :success => true } }

This makes the uploader work but seems to preclude me adding rendered partials to the page like I normally would with an Ajax response.

My question is, is there any way to accomplish both? If not, how would you populate the page with rendered objects after they have been successfully created?

You can't do both directly from the controller or using view templates. It looks like ajax uploader is expecting a response that can be parsed as JSON, so adding any other response using a template (which would mean not doing format.js { render :json => { :success => true }}) will not send back JSON as the response body.

Looking at the source for the plugin, you can see that you can define a method to run onComplete which will give you the JSON response.

This is untested, but should get you close. So you if extend your JSON response like

format.js do
  partial = render_to_string @resource
  render :json => { :success => true, :partial => partial }
end

Then in your javascript on the page when you setup your ajax uploader, add the callback

var uploader = new qq.FileUploader({
  element: document.getElementById('file-uploader'),
  action: '/upload',
  onComplete: function(id, fileName, responseJSON) {
    $('#resources').append(responseJSON.partial);
  }
});