Best ruby-on-rails questions in January 2011

Best ruby / rails time-savers

12 votes

Just curious, as I've been digging deeper in Rails I've found there are often routine things that I originally learned to do in a verbose way, and as I begin to understand the framework and the ruby language better I find much nicer and/or shorter and/or faster ways to do the same things.

So, I thought it would be great to hear tips from the rest of you. What things have you learned that have really boosted your productivity using ruby in general or the rails framework specifically? If you could teach a new rails developer (like me) one time-saving habit or tool or coding convention you wish you'd learned sooner, what would it be?

RVM with per project Gemsets plus Bundler => Awesome

Just create a .rvmrc in your rails app.

rvm --create use  "ruby-version@you-project-name"

It is a very real time saver because combined with bundler you eliminate any of conflict between gems.

Implementing account scoping

8 votes

Currently in my application I have the concepts of projects and users. Now I'm wanting to implement an account scope for these so that both projects and users belong to an account rather than to nothing in particular. By doing this, I would like to scope my routes like this:

 scope ":account_id" do
   resources :projects
   ...
 end

However, by implementing a routing scope with a named parameter this changes how the routing helpers perform so that the project_path routing helper now expects two parameters, one for the account_id parameter and one for the id parameter, making it something like this:

  project_path(current_account, project)

This tiny scope change requires me to make massive changes across the application in the controllers and views where I use these path helpers.

Surely, surely, surely, there's a clean way to do this without having to change every single routing helper in the application?

Use the default_url_options hash to add a default value for :account_id:

class ApplicationController < ActionController::Base
  protect_from_forgery

  before_filter :set_default_account_id

  def set_default_account_id
    self.default_url_options[:account_id] = current_account
  end
end

You can then use the url helper with a single parameter:

project_path(project)

You can override it in a view by passing :account_id as a hash parameter to a route:

project_path(project, :account_id => other_account)

Note that this won't work in the console.

Is it a bad idea do divide the models into directories?

8 votes

I have a over 100 models in my rails application, and just for organization, I'm dividing them into folders, all still under the main model folder, just to make it simpler to navigate on the project and see files that are related.

Is this a bad idea? What is the rails way to do this?

No, it's not a bad idea. Many people do it and I couldn't live without it in large applications.

There are two ways of doing it:

The first is to just move your models. You will, however, have to tell Rails to load the wayward models (as it won't know where they are). Something like this should do the trick:

# In config/application.rb
module YourApp
  class Application < Rails::Application
    # Other config options

    config.autoload_paths << Dir["#{Rails.root}/app/models/*"]
  end
end

The first way is easy, but is not really the best way. The second way involves namespacing your models with groups they're in. This means that instead of having User and UserGroup and UserPermissions, you have User, User::Group and User::Permission.

To use this, generate a model like this: rails generate model User::Group. Rails will automatically create all of the folders for you. An added benefit is that with this approach, you won't have to spell out the full model name for associations within a namespace:

class User < ActiveRecord::Base
  belongs_to :group # Rails will detect User::Group as it's in the same namespace
end

class User::Group < ActiveRecord::Base
  has_many :users
end

You can specify however many levels of namespacing as you want, so User::Group::Permission would be possible.

Run a customized version of a Rails application

8 votes

Here's our basic requirements:

  • We have a base Rails application, which is being actively maintained.
  • We want to offer a customized version of this app, given that:
    • servers must reside in our customer's premise and run on a different domain.
    • there's a specific logging instrumentation for their own monitoring in the datacenter.

To do that, I can see several options to achieve that goal:

  • Git branch
  • Rails::Engine
  • Rails::Application

The most obvious answer would be Git branch, for full flexibility.

However I'm not sure if it's a good idea, because the code base is largely shared and the mainline has a lot more activities - catching up with rebase / merge could be just extra hassle.

We want to decouple the original and the customized versions as far as possible. In other words, we want to have as less often conflicts as possible between the original and the customized.

Rails::Engine or Rails::Application seemed like a close idea (I'm not familiar with Rails Engines), but I don't understand how to have OurApp::Application and OurCustomizedApp::Application in one place and switch between them globally and dynamically.

Probably it would be nice to have:

  • custom initializers, controllers and views in a separate directory to override (or patch) the original
  • ability to specify which app (the original or the customized) to boot by an environment variable like RAILS_APP
  • separate config files, like so: config/database.yml to be config/customer1/database.yml
  • ability to use the same deploy.rb for capistrano (probably with config/servers.yml and config/customer1/servers.yml to define roles and IPs?)

Is there practices / conventions for our requirements? Any advice?

Our apps run on Ruby 1.9.2 + Rails 3.0.3.

UPDATE

We started it as a Git branch. We created a rake task to generate a file at config/branch that includes text like "master" or "customized", and application.rb reads it upon bootstrap. Configs like database.yml or servers.yml now live in config/mainline/ or config/customized/, and application.rb handles them accordingly.

config.paths.config.database = "config/#{branch}/database.yml"

Not perfect, but good enough for now. I'll update when we find a better way to do this.

I know it's not the precise answer you're after, but I believe Git is going to be the least amount of work - and the easiest to manage, long-term - over customising the app and adding logic to handle the additional config files, modifying your deploy files and also managing the (potentially) new css/js/template files.

Using rebase & merge are going to be a lot less error-prone, and as long as you keep your branches synced on a regular basis, you shouldn't have any serious problems keeping them both up-to-date. After all, that's what Git is good at! ;)

Detect browser character support in javascript?

7 votes

I'm working on a music related website, and frequently use the HTML special characters for sharps (♯) and flats(♭) to keep things pretty, e.g.:

&#9839;
&#9837;

However, I've noticed that in some browsers (IE6, Safari for PC) those characters aren't supported. I've created a conditional javascript that serves up plain, supported characters in place of the special ones ( G# for G♯ and Bb for B♭ ). But I'm having a hard time figuring out how to detect which browsers lack those characters.

I know I could test for the browser (e.g. ie6), but I was hoping to do things right and test for character support itself.

Does anyone know of a good way to do this using either javascript, jQuery, or rails? (The page is served by a rails app, so the request object and any other Rails magic is on the the table.

If you create two SPANs, one containing the character you want, and the other containing an unprintable character U+FFFD (�) is a good one, then you can test whether they have the same width.

<div style="visibility:hidden">
  <span id="char-to-check">&#9839;</span>
  <span id="not-renderable">&#xfffd;</span>
</div>
<script>
  alert(document.getElementById('char-to-check').offsetWidth ===
        document.getElementById('not-renderable').offsetWidth
        ? 'not supported' : 'supported');
</script>

You should make sure that the DIV is not styled using a fixed font.

Ruby on Rails development on windows

7 votes

I've been planning on developing a rails project on windows. I've heard that the framework wasn't tested on windows (at least not the testing framework)

does anyone have any real experience with rails on windows? are there any known bugs when running on windows? does the testing framework work on windows?

I've been developing a Rails website on Windows & Mac (depending on where I am at the time) for a few months now and, in general, I haven't run into to many problems. Here's what I know:

The new Ruby 1.9.2 installer for Windows is nice because it comes with RubyGems (which a vague memory tells me was difficult before). That's what I'm using. I haven't been able to get the ruby-debug19 gem to install correctly on windows. So, I just comment that out in my Gemfile on my Windows computer. Other than that, I haven't had any issues.

With that said, however, I love developing on my Mac so much more than I do on Windows. I haven't found an editor that I love for Rails development on Windows (currently using Notepad++ with Explorer plugin), using git is not as nice on Windows, and I just really don't like the Windows Command Prompt (I know there are other options, but still).

Usage of exceptions in Ruby - best practices

7 votes

I am reading Agile Web Development with Rails (4th ed.) and I have found the following code

class ApplicationController < ActionController::Base
  protect_from_forgery

  private

  def current_cart
    Cart.find(session[:cart_id])
  rescue ActiveRecord::RecordNotFound
    cart = Cart.create
    session[:cart_id] = cart.id
    cart
  end
end

Since I am a Java developer, my understanding of that part of code is more or less the following:

private Cart currentCard(){
  try{
    return CartManager.get_cart_from_session(cartId)
  }catch(RecordNotFoundEx e){
    Cart c = CartManager.create_cart_and_add_to_session(new Cart())
    return c;    
  }
}

That what strikes me is that the exception handling is used to control normal application flow (lack of Cart is perfectly normal behaviour when user visits Depot application for the first time).

If one takes any Java book, they say that this is a very bad thing to do - and for a good reason: error handling shouldn't be used as a replacement for control statements, it's kind of misleading for those who read code.

Is there any good reason why such a practice is justified in Ruby (Rails)? Is this a common practice in Ruby?

Rails is in no way consistent in its use of exceptions. find will raise an exception if no object is found, but for saving you can choose what behaviour you want. The most common form is this:

if something.save
  # formulate a reply
else
  # formulate an error reply, or redirect back to a form, or whatever
end

i.e. save returns true or false. But there is also save! which raises an exception (adding an exclamation mark to the end of a method name is a Rubyism for showing that a method is "dangerous", or destructive, or simply that it has side-effects, the exact meaning depends on the context).

There is a valid reason for why find raises an exception, though: if a RecordNotFound exception bubbles up to the top level it will trigger the rendering of a 404 page. Since you usually don't catch these exceptions manually (it's rare that you see a rescue ActiveRecord::RecordNotFound in a Rails app), you get this feature for free. In some cases though, you want to do something when an object does not exist, and in those cases you have to catch the exception.

I don't think that the term "best practice" actually means anything, but it is my experience that exceptions aren't used for control of flow in Ruby anymore than in Java or any other language I have used. Given that Ruby doesn't have checked exceptions, you deal with exceptions much less in general.

In the end it's down to interpretation. Since the most common use case for find is retrieving an object to display it, and that the URL for that object will have been generated by the application, it may very well be an exceptional circumstance that the object cannot be found. It means that either the application is generating links to objects that don't exist, or that the user has manually edited the URL. It can also be the case that the object has been removed, but a link to it still exist in a cache, or via a search engine, I would say that that too is an exceptional circumstance.

That argument applies to find when used as in your example, i.e. with an ID. There are other forms of find (including the many find_by_* variants) that actually search, and those don't raise exceptions (and then there is where in Rails 3, which replaces many of the uses of find in Rails 2).

I don't mean to say that using exceptions as control of flow is a good thing to do, just that it's not necessarily wrong that find raises exceptions, and that your particular use case is not the common case.

How does Ruby on Rails use yield for layouts?

7 votes

I simply don't understand. yield is used to call a block. How does this work in Rails where yield is used for layouts?

-# application.html.haml
%body= yield

Does it use blocks somewhere or is the method simply overridden?

Technically, yield is calling a block in this context as well. However, the block is the view your controller action was told to render.

For example, let's say you have a StaticContentController that has an index action on it that represented your home page. With routes configured correctly, you visit your home page. Rails will load the layout file in views/layouts that is appropriate for that controller (application.html.haml, unless you overrode this with a layout for your controller). When it reaches the yield command, it inserts the view at views/static_content/index.html.haml at the location where yield is inside your layout. Then, it loads the rest of your layout file.

Extending Devise SessionsController to authenticate using JSON

7 votes

I am trying to build a rails API for an iphone app. Devise works fine for logins through the web interface but I need to be able to create and destroy sessions using REST API and I want to use JSON instead of having to do a POST on the sessions controller and having to parse the HTML and deal with a redirect.

I thought I could do something like this:

class Api::V1::SessionsController < Devise::SessionsController  
  def create
    super
  end  
  def destroy
    super
  end  
end

and it config/routes.rb I added:

namespace :api do
  namespace :v1 do
    resources :sessions, :only => [:create, :destroy]
  end
end

rake routes shows the routes are setup properly:

   api_v1_sessions POST   /api/v1/sessions(.:format)     {:action=>"create", :controller=>"api/v1/sessions"}
    api_v1_session DELETE /api/v1/sessions/:id(.:format) {:action=>"destroy", :controller=>"api/v1/sessions"}

When I POST to /user/sessions everything works fine. I get some HTML and a 302.

Now if I POST to /api/v1/sessions I get:

Unknown action AbstractController::ActionNotFound

curl -v -H 'Content-Type: application/json' -H 'Accept: application/json'   -X POST http://localhost:3000/api/v1/sessions   -d "{'user' : { 'login' : 'test', 'password' : 'foobar'}}"

This is what finally worked.

class Api::V1::SessionsController < Devise::SessionsController  
  def create  
    respond_to do |format|  
      format.html { super }  
      format.json {  
        warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")  
        render :status => 200, :json => { :error => "Success" }  
      }  
    end  
  end  
  def destroy  
    super  
  end  
end  

Also change routes.rb, remember the order is important.

devise_for :users, :controllers => { :sessions => "api/v1/sessions" }
devise_scope :user do
  namespace :api do
    namespace :v1 do
      resources :sessions, :only => [:create, :destroy]
    end
  end
end

resources :users

OmniAuth: Guarding against multiple accounts for the same user

6 votes

I have a couple of Rails apps I'm looking to integrate with OmniAuth, but there is a conceptual problem I'm having with it that I'd like to figure out first. Consider the following scenario:

  1. Your application, Foo, supports OmniAuth logins via Twitter and Facebook.
  2. Joe comes to your site and logs in via his Twitter account. This creates a new user on Foo and associates it with this new Twitter authorization.
  3. Joe logs out of Foo, and forgets about the site for six months.
  4. Joe returns to Foo, not remembering that he has previously logged in with Twitter.
  5. Joe logs in with Facebook. Since he is not already logged in via his original Twitter authorization, there is no way to detect that he is, in fact, the same Joe, and a new account is created.
  6. Joe discovers his old account, and is now frustrated that his older content is tied to this old account, and that he can't log in with Twitter and Facebook interchangeably.

Since Twitter does not supply Foo with an email address, there is no universal identifier to use for detecting that the two Joes are the same Joe. You could decide to support only providers who give you the user's email address, but this doesn't help if the user has registered with different email addresses on different providers.

The only other solution I can think of is to give the user some way of merging two existing accounts. That's a big headache compared to the relative ease of everything else when using OmniAuth. If this is the only solution, has anyone come across a guide/tutorial showing an example of how this might be done? I'm surprised this issue hasn't gotten more attention given the popularity of OmniAuth.

Thanks!

Your gut feeling is correct. You'll have to provide a merge tool for your user... or you can ignore the problem.

Release date for Ruby on Rails 3.1

6 votes

Does anyone know when Ruby on Rails 3.1 is going to be released?

Rails will be released when it works well, and not when an externally imposed deadline says it should be working well. It will be released when it is ready, and when its dependencies are ready.

Looking back in recent history, Rails 3 was incompatible with Ruby 1.9.1 and so the Rails 3.0 release was forced to wait for the Ruby 1.9.2 release. Rails 3 also introduced dependencies on some new projects. In particular, the Rails 3.0 release was forced to wait for the Bundler 1.0 and Arel 1.0 releases. The Bundler 1.0 release was also forced to wait for the Ruby 1.9.2 release. These new projects, including Bundler and Arel and including other new projects such as Mail, made Rails 3 well worth the wait.

Ultimately, for any project, the choice has to be made between (1) freezing all feature development well in advance of a deadline in order to be sure to meet the deadline and (2) actually making the project better, rapidly, and releasing it early and often whenever it is ready to be released.

What is a "resource" in Rails?

6 votes

Dumb question but I have some lingering confusion of what, exactly, a "resource" is in Rails. The term is used everywhere but I get a funny feeling it might be being used rather loosely. It's referenced in the model, the controller and, quite literally, in routes.rb.

Is it the specific route? For example, map.resources maps the 7 RESTful "resources". So an example of one resource would be the call to, say, the index action of a particular class's controller?!?

Is it a reference to the whole page/object being retrieved? or perhaps, more narrowly, a database table? or the row being retreived?

Is it something else?

Anyway, hopefully someone can set me straight...

Any object that you want users to be able to access via URI and perform CRUD (or some subset thereof) operations on can be thought of as a resource. In the Rails sense, it is generally a database table which is represented by a model, and acted on through a controller.

For example, you might have a User resource (with a users table in your DB). This is represented by a User model, is mapped to users_controller with map.resources :users (which then generates routes like /users (a collection of User resources) and /users/1 (a specific User resource).

You act upon those resources by using the appropriate HTTP method when making calls to those resources. POST to the resource collection (/users) creates a new record; GET retrieves a list of resources (/users) or a specific user (/users/1). PUT updates a specific user (/users/1/), and DELETE destroys that user. The URLs are the same, but the result (and controller action) may be different based on the HTTP verb. The idea, though is that /users/1 always means "I'm interacting with the User that has ID #1", regardless of the action.

Good practices of Rails

6 votes

I'm looking for people's examples of good* practices when using Rails.

I've got a couple such as:

  • before_filter calls go underneath the controller class name declaration, nowhere else.
  • Base controllers for a namespace are named BaseController, not ApplicationController. There is, and should forever be, only one ApplicationController.
  • attr_* methods are defined at the top of their respective classes.
  • Model callbacks go after attr_* methods, or at the top of the model.
  • Validations go at the top of the model, with custom validation methods being defined as private methods at the bottom of the file.

Now I'm not looking for Ruby's good practices, but more of a list of ones specifically in Rails. The ones listed above are just an example, not gospel.

* I didn't want to use the term "best practices", as best implies an ultimate, and in all things code people may disagree.

One practice I've found to be pretty consistent is when parentheses are appropriate. DSL class macros like validations and associations seem natural without them, whereas methods with an explicit receiver and argument(s) seem better with them.

has_many :users

User.find_all_by_field(my_var)

vs

has_many(:users)

User.find_all_by_field my_var

What if any source code of a rails project should be obscured even for an open source project?

6 votes

This was a hard one to search for. If I have an open source rails web application project whose source code is publicly hosted, like on GitHub, what information should be obscured or swapped if that application is to be run in production at a public website? My assumption is that things like config/initilizers/secret_token.rb, any authentication salting stuff, and the database login information should not be the same in production as in development. What other precautions should be taken to ensure that the production site is not vulnerable to people fiddling with the sessions or anything else I am not considering?

Rails-specific Sources of Sensitive Information

Scrub sensitive information out of:

  • config/environments/*.rb
  • config/initializers/cookie_verification_secret.rb
  • config/initializers/secret_token.rb
  • config/initializers/session_store.rb
  • any files added to support third-party libraries, such as config/memcached.yml
  • config/database.yml
  • db/seeds.rb
  • any rake tasks in lib/tasks.
  • test/fixtures/*

General Changes

Including this just because I think it's a good list of things to keep in mind for releasing open-source software that you also have in production.

  • Remove sensitive information:
    • password salts
    • default user credentials populated by code or seeds
    • authentication information to any external server or service
      • databases
      • third-party APIs
      • eCommerse solutions
    • any seeded data that would potentially publicize trade secrets
  • Test code throughly for exploits. If they are in your code and your code is available to the public, people will find them and will know how to compromise your site.
  • Clean up the code. The code is a form of publicity for your site; it's is one of the many things that will represent your site/company. Make sure you change variable/function names/error messages/seeded data/etc that were written out of humor or frustration but that would look bad to the public.
  • Actively contribute your enhancements and bug fixes to the project and respond to external requests for fixes/enhancement or even pull requests for those who have solved a problem themselves. This keeps the project active and also helps with the publicity angle.
  • Make sure you give credit where credit is due. Now that your code is public, people will know if you've utilized third-party code/libraries. If such code came with attribution clauses in their license agreements, make sure your project complies with those agreements.

6 votes

I'm looking for some examples of bad practices in Ruby on Rails, for a presentation on to what not to do.

My biggest on is to use update_attribute on a model after_save hook.

Object.update_attribute(:only_one_field, "Some Value")  

As this is a very open ended question, I will wait a week or 2 and select as answer the most voted answer.

Have fun!

  1. too much mass-assignment without using attr_protected

  2. use of too many plugins - There are so many gems and Rails has sooo many plugins available for use in your applications. However, when you use a gem or a plugin, you rarely understand how your code is operating (unless you actually look at the source, which most people never do). This is a HUGE problem. You don't know how to debug code properly, plugins and gems clash with one another, security becomes a major concern, etc. For that reason, I always recommend writing all your own code. Sure, Devise is nice for authentication, but can you tell me exactly how it works and what queries are run? Do you have control over optimization? (I'm not picking on Devise, just showing a clear example that many RoR developers are familiar with)/

  3. keeping unwanted pages/actions - so many Rails developers use scaffolding (because it's nice), but then they don't bother to remove unwanted actions. It's as simple as adding :only => [] or :except => [] in your routes file, but most people never do! I don't know how many Rails sites that have been hacked or damaged because people didn't restrict the delete action

  4. trying to go against Ruby - developers who come from another language often have difficulties with the "Ruby-way." One of the most notable examples is having non-incrementing or non-integer primary keys.

  5. too much controller, not enough model - Rails had a "Fat Model, Skinny Controller" principle that all too many developers break.

  6. violations of MVC - accessing params in Models, trying to hack things into controllers, etc.

  7. not changing the default Rails unique session token (which is not actually random)

  8. writing sloppy code - Ruby has this great way of making code look readable. If you come from Java or PHP or even Python, you're code is just plain ugly until you learn Ruby

  9. saying that Rails "is a language" or "I code in Rails" - absolutely, positively jerks me the wrong way when I hear someone say "I code in Rails" or "Rails is my favorite language", etc. RAILS IS NOT A LANGUAGE. Rails is a framework built on Ruby. This isn't related to security or the like, but you'll really irritate a LOT of RUBY developers if you start saying that RAILS is a language. It's a framework.

  10. comparing PHP and Rails - don't do it. Again, PHP is a language, Rails is a framework. Comparing them is unfair. (You can compare Ruby and PHP OR Rails and CodeIgnitor or CakePHP, etc)

  11. not properly catching errors - if it can go wrong, someone, assume it will, and plan ahead

  12. failing to optimize queries - this absolutely kills me. Rails doesn't force you to know SQL like PHP did (before ORM's like Doctrine), so Rails apps tend to be SLLLLOOOOWWW unless the developer is actually aware that you can optimize a query (joins say what??)

  13. using too many generators - you should be able to create a class (controller, model, test, view) without the use of a generator.

  14. using Rails for a large-scale system - yeah, most of you aren't going to like this, but ask Twitter and GitHub what happens if you build your front and backend in Rails... Let's just say Twitter uses a custom Java backend now...

  15. have a freaking clue - I get so annoyed because people don't actually know how a has_many relationship works (just one of 21914232 examples of dumb Rails developers)!

  16. not commenting code

  17. relying on Rails instead of SQL or DOM (javascript/html) to perform functions