Best ruby-on-rails questions in January 2012

Ruby expression evaluation: whitespace matters?

13 votes

Imagine it's Jan 19. This will not be hard if you look at this question today.

Date.today
 => Thu, 19 Jan 2012    # as expected

Date.today + 1
 => Fri, 20 Jan 2012    # as expected

Date.today+1
 => Fri, 20 Jan 2012    # as expected

Date.today +1
 => Thu, 19 Jan 2012    # ?!

What am I missing here?

The difference is that:

Date.today + 1 

is an addition of two numerical values and

Date.today +1 

is a call to the method today with the parameter sg(day of calendar reform) with value +1

The best way to examine this is to monkey patch the original method with debug output included. See this script as example:

require 'date'

class Date

  def self.today(sg=ITALY)
     puts "ITALY default("+sg.to_s+")" if sg==ITALY
     puts sg unless sg==ITALY
     jd = civil_to_jd(*(Time.now.to_a[3..5].reverse << sg))
     new0(jd_to_ajd(jd, 0, 0), 0, sg)
  end

end

puts "- Addition:"
Date.today + 1
puts "- Parameter:"
Date.today +1

This will print the following console output:

- Addition:
ITALY default(2299161)
- Parameter:
1

Scope vs Class Method in Rails 3

9 votes

Based on the Rails 3 API, the difference between a scope and a class method is almost non-existent.

class Shipment < ActiveRecord::Base
  def self.unshipped
    where(:shipped => false)
  end
end

is the same as

scope :unshipped, where(:shipped => false)

However, I'm finding that I'm sometimes getting different results using them.

While they both generate the same, correct SQL query, the scope doesn't always seem to return the correct values when called. It looks like this problem only occurs when its called the same way twice, albeit on a different shipment, in the method. The second time it's called, when using scope it returns the same thing it did the first time. Whereas if I use the class method it works correctly.

Is there some sort of query caching that occurs when using scope?

Edit:

order.line_items.unshipped

The line above is how the scope is being called. Orders have many line_items.

The generate_multiple_shipments method is being called twice because the test creates an order and generates the shipments to see how many there are. It then makes a change to the order and regenerates the shipments. However, group_by_ship_date returns the same results it did from the first iteration of the order.

def generate_multiple_shipments(order)
  line_items_by_date = group_by_ship_date(order.line_items.unshipped)

  line_items_by_date.keys.sort.map do |date|
    shipment = clone_from_order(order)
    shipment.ship_date = date
    line_items_by_date[date].each { |line_item| shipment.line_items << line_item }
    shipment
  end
end

def group_by_ship_date(line_items)    
  hash = {}
  line_items.each do |line_item|
    hash[line_item.ship_date] ||= []
    hash[line_item.ship_date] << line_item
  end
  hash
end

I think your invocation is incorrect. You should add so-called query method to execute the scope, such as all, first, last, i.e.:

order.line_items.unshipped.all

I've observed some inconsistencies, especially in rspec, that are avoided by adding the query method.

You didn't post your test code, so it's hard to say precisely, but my exeprience has been that after you modify associated records, you have to force a reload, as the query cache isn't always smart enough to detect a change. By passing true to the association, you can force the association to reload and the query to re-run:

order.line_items(true).unshipped.all

How to write negative loop in ruby like for(i=index; i >= 0; i --)

8 votes

I am new with ruby. wish to know how to write following loop in ruby.

var index=25; 

for (i = index; i >= 0; i--) { 
    print i;
}

Thanks in advance

There are many ways to perform a decrementing loop in Ruby:

First way:

for i in (10).downto(0)
   puts i 
end

Second way:

index = 10 # any value
index.downto(0) do |i|
  puts i
end

Third way:

i = index
until i > 0
  i -= 1
  puts i
end

8 votes

I have a question about handling my models. I get all confused. When I load the page I get a JSON string from rails containing "events" these events have in turn one user, multiple participants, multiple payments and multiple comments, these comments, have in turn one user, and the payments have multiple users and a single user. The comments and payments also have an event pointing back att the parent.

Events
  User
  Participants(users)
  Payments
    User
    Users
    Event
  Comments
    User
    Event

Okey, so the question is: Should I load everything as a tree, with full attributes everywhere:

"events": {
  "id": "event_1",
  "user": {
    "id": "user_1", "name":"name"
  }, "participants": [
    {"id": "user_1", "name":"name"},
    {"id": "user_2", "name":"name"}
  ], "payments": [{
      "id":"payment_1",
      "user": {
        "id": "user_1", "name":"name"
      },"users": [
        {"id": "user_1", "name":"name"},
        {"id": "user_2", "name":"name"}
      ], "event": {root event object}  
    }], "comments": [{
      "id": "comment_1",
      "user": {
        "id": "user_1", "name":"name"
      }, "event": {root event object}  
    }]
  }
}

And then have the events model, to create new comments, payments and users, and assign it to it's own event, or is it a better idea to load every event, user payment and comment into separate variables, and then use the variable to get the models. It is quite hard to explain, so feel free to ask if I need to clarify something.

Conclusion: Should I let the event model handle the creation of all the nested objects, or is there some better way to handle this, and have access to the models more globaly?

Architecture is subjective, but here is how I would go about it -

3 base models

User = Backbone.Model.extend({
    defaults : {
        name : ""
    }
})
Payment = Backbone.Model.extend({
    defaults : {
        user : new User(),
        users : new UserList(),
        event : ""
    }
})
Comment = Backbone.Model.extend({
    defaults : {
        user : new User(),
        event : ""
    }
})

3 Collections

UserList = Backbone.Collection.extend({
    model : User
})
PaymentList = Backbone.Collection.extend({
    model : Payment
})
CommentList = Backbone.Collection.extend({
    model : Comment
})

One Event model

Event = Backbone.Model.extend({
    defaults : {
        user : new User(),
        participants : new UserList(),
        payments : new PaymentList(),
        comments : new CommentList()
    }
})

If you initialize your event object with the JSON in the above example, it should just work. In the future if you want to separate out your users to a different API, this code should support that too. If you do want to make the component objects available globally later on, you can just get it from the user object, i.e., window.User = myEvent.get("user")

Hope this helps :)

Converting String "2½" (two and a half) into 2.5

8 votes

Right now I'm building a small app that imports data from a spreadsheet and due to the nature of the original entry, there is a string being read in that has values such as 8½, 2½, etc.

My goal with a simple function is to convert 2½ into float 2.5, for example.

I've tried the .to_f method but that has left me with a weird value of 2.02½.

Any insight or suggestions here would be very much appreciated!

Unicode only supports a small number of vulgar fractions so a simple lookup table will do the trick:

# You might want to double check this mapping
vulgar_to_float = {
    "\u00BC" => 1.0 / 4.0,
    "\u00BD" => 1.0 / 2.0,
    "\u00BE" => 3.0 / 4.0,
    "\u2150" => 1.0 / 7.0,
    "\u2151" => 1.0 / 9.0,
    "\u2152" => 1.0 /10.0,
    "\u2153" => 1.0 / 3.0,
    "\u2154" => 2.0 / 3.0,
    "\u2155" => 1.0 / 5.0,
    "\u2156" => 2.0 / 5.0,
    "\u2157" => 3.0 / 5.0,
    "\u2158" => 4.0 / 5.0,
    "\u2159" => 1.0 / 6.0,
    "\u215A" => 5.0 / 6.0,
    "\u215B" => 1.0 / 8.0,
    "\u215C" => 3.0 / 8.0,
    "\u215D" => 5.0 / 8.0,
    "\u215E" => 7.0 / 8.0,
    "\u2189" => 0.0 / 3.0,
}

Then, a little bit of regex wrangling to pull your "number" apart:

s = "2½"
_, int_part, vulgar_part = *s.match(/(\d+)?(\D+)?/)

And finally, put them together taking care to properly deal with possible nils from the regex:

float_version = int_part.to_i + vulgar_to_float[vulgar_part].to_f

Remember that nil.to_i is 0 and nil.to_f is 0.0.

How to handle translations for an ActiveModel?

6 votes

I am using Rails 3.1.1 and I would like to properly translate error messages for an ActiveModel. I don't know if overwriting the i18n_scope is the right way to solve my problem (or if there are other ways), but the official documentation says:

i18n_scope()

Returns the i18n_scope for the class. Overwrite if you want custom lookup.

... how should I overwtite the i18n_scope?

At this time I am getting a the following "alert":

# Note the 'activemodel' part
translation missing: de.activemodel.errors.models.my_class.attributes.message.blank

# I would like to "map" translations to 'de.activerecord.errors.messages.blank'
# as made for all other ActiveRecord classes in my application

My ActiveModel class is like the following:

class MyClass
  include ActiveModel::Conversion
  include ActiveModel::Validations
  include ActiveModel::Dirty
  extend  ActiveModel::Naming
  extend  ActiveModel::Translation

  validates :name, :presence => true

  ...
end

It should be a class method, by analogy with AR code:

class MyClass
  include ActiveModel ...
  class << self
    def i18n_scope
      :activerecord
    end
  end
end

eCommerce application design: how to manage shopping carts on a used goods website?

6 votes

I'm making an eCommerce marketplace for used goods using Ruby on Rails with the PayPal API for completing purchases. So essentially the deal is that people can buy and sell their stuff - like eBay or Half.com. All items are used. Here's my question: used goods means that every item is unique - at least in the sense that sellers typically only carry 1 of the used good they are selling. That brings me my question, for any developers who have run into this kind of problem before: how should I handle the situation in which:

  • Seller lists his sweatshirt
  • Person #1 adds the sweatshirt to his cart
  • Person #2 adds the same sweatshirt to his cart
  • Person #1 checks out via PayPal

Now, what do I do with Person #2? Clearly, he can't check out the same sweatshirt, because the seller only has 1 sweatshirt. What I want to know is how I should handle this situation. So far, I've come up with two possible solutions, but neither seems satisfactory to me. (By the way, it might be relevant to add that a user's current cart is a session variable)

Option #1: When Person #1 adds the sweatshirt to his cart, you flag the sweatshirt with a boolean field such as available = false. The downside to this is that a user can add an item to his cart, and then go idle. Thus, no one can purchase the item until Person #1's cart expires.

Option #2: The sweatshirt is only flagged as unavailable when I get the IPN from PayPal. The downside to this is that you could theoretically have Person #1 and #2 checking out from PayPal at once, and thus they would both buy the sweatshirt, and it wouldn't be until after I get the IPN that I realize I've sold the product twice.

What does StackOverflow think? Is there anyone with experience in this field who can offer some insight?

Here is the deal -

  • Seller lists his sweatshirt
  • Person #1 adds the sweatshirt to his cart
  • Person #2 adds the same sweatshirt to his cart
  • Person #1 checks out via PayPal
  • Prevent both of them from end up buying same Item which has 1 quantity in your inventory.

One way to handle this scenario in my opinion is(Which is a kind of upgraded version of your option #1)-

When Person 1 adds an item in their cart you will(if not then you should) be changing the order status i.e. in cart, address, payment, complete etc.

So when Person 1 adds Item A in their cart(status: in cart) block it for 10 minutes for others(Person 2) to add them into their cart. Now you need to have a rake script or Delayed Job using crone job running on your server which checks for every products, in this case Item A in orders table with in cart status for 10 or more than 10 minutes and flush/ remove those products from that row. Which will enable others(Person 2) to add that Item into their cart. And have others(Person 2) and Person 1 updated about your process of this 10 minutes flushing thing with some sort of notice. For example: Item 1 Will be available in Time Counter running to Person 2 and Item 1 will go in Time Counter running to Person 1. Here this process will make a sense of urgency in users mind and you'll have a control over your inventory from not getting an item sold more than its count on hand/ quantity. You can check this website for a live and running implementation - http://www.thepeacockparade.com/

Hope it gives you a fair idea to handle this situation. And yes if, you get any better way to do the same thing please, do keep me updated about it because, I'm also looking for an upgrade.

Thanks

Update on performance

After implementing this process you may end up with some performance issues on your website. One way I figured out a way to keep your application running and, serving your customers and, the background process synced at the same time is, If you use amazon rds or any other cloud database service you can have two different application server altogether. One for your customers to serve and one for performing background processes such as, cleaning temporary data, files, rake tasks, uploading data etc. And since you have your application database totally outside of you application servers, it can be updated from both applications. So, it will keep your main application serving even better to its users instead of starving for memory used by background processes.

Update on database settings

Rails is awesome when it comes to database, tables and associations. If you are new to amazon cloud database service then, checkout their plans here: http://aws.amazon.com/rds/pricing. Best part about having your database on amazon rds are -

  • You are free to select the near endpoint/ server location to serve your database for your application, which will reduce latency of your db queries and your server load in processing them.
  • You can use multiple applications to access it. Apparently for different purposes, i.e. One for uploading data, second for running analytics etc.
  • You have to pay only for what you use.
  • If anything(sometimes for some reasons I prefer being a pessimist) goes wrong and server crashes for any weird reason. You are not screwed!! Your DATABASE is safe. Just set up a new application and voila!! You are back in no time.
  • You have freedom to automate database backup without messing your head into BASH SCRIPTS.
  • You can expand it as your Business grow.

To set up an amazon rds database go to your amazon rds console: https://console.aws.amazon.com/rds/home. Select your preferred region from Navigation pane on left side of the page. Click on "Launch DB Instance" button, select you preferred Database and follow rest of the flow, i.e. selecting instance type etc.

Now if you have your rds instance running, you are able to see an endpoint something like this: database-name.random-string.region-endpoint.rds.amazonaws.com. In your rails application edit and update your config/database.yml to this:

production:
  adapter: mysql2
  host: database-name.random-string.region-endpoint.rds.amazonaws.com
  encoding: utf8
  reconnect: false
  database: databse-name
  username: master-username
  password: password

Looked surprised?? Yes, that's it!! You are all set to you use your application with your new amazon rds DB instance. Now rake db:create to test connection and rake db:migrate to create your tables.

Here I'd like to add one more thing. If you care to make your life better then, you should use amazon s3(simple storage service) too. It's damn cheap and reliable, Check it out here: http://aws.amazon.com/s3/

Enjoy!!

When should I use development vs testing group in gemfile for testing gems?

6 votes

For some reason this hasn't hit home, and I'm wondering if someone could help explain..

I noticed when installing 'guard' gem, that they recommend placing a lot of the gems in the 'development group' in the gemfile, such as 'growl' and 'rb-notifu': https://github.com/guard/guard..

Also Ryan Bates seems in one screen cast seems to put many of these in 'development' & 'testing': http://railscasts.com/episodes/264-guard?view=asciicast

But in another puts it all in 'testing': http://railscasts.com/episodes/275-how-i-test

It'd be nice to understand this so I don't have to reference tutorials all the time. Thanks!

Gems that you run from the development environment should be present in both the development and test groups. You run things like rspec cucumber and guard from development and they run in the test environment, you need them in development to run the rake tasks and the executables.

Gems that only run while in test mode, such as capybara email_spec and launchy can exist only in the test group and still function correctly.

I hope this helps clear things up.

As a general rule, gems that are executable need to be in both. Also, if you are unsure, put it in both groups too.

Edit

If the gem you are using has generators (rails generate), it needs to be present in both test and development.

Rails validatation to ensure a username does not clash with an existing route?

5 votes

I want to ensure users can't create usernames that clash with my existing routes. I would also like the ability to deny future routes I may define. I am thinking of accomplishing this like so:

In the model:

class User < ActiveRecord::Base
  @@invalid_usernames = %w()

  cattr_accessor :invalid_usernames

  validates :username, :exclusion { :in => @@invalid_usernames }
end

In some initializer:

User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq

Is this "Rails way"? Is there a better way?

Here's my own answer, tested and working with Rails 3.1.3 and Ruby 1.9.3

app/models/user.rb

class User < ActiveRecord::Base
  class_attribute :invalid_usernames
  self.invalid_usernames = Set.new %w()

  validates :username, presence:   true,
                       uniqueness: { case_sensitive: false },
                       exclusion:  { in: lambda { self.invalid_usernames }}
end

config/application.rb

[:after_initialize, :to_prepare].each do |hook|
  config.send(hook) do
    User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
  end
end

Notes

I initially tried setting User.invalid_usernames during after_initialize but found it needs to be set during to_prepare (i.e. before each request in development mode, and before the first request in production mode) since the models are reloaded in development before each request and the original setting is lost.

I am however also setting User.invalid_usernames during after_initialize because the routes don't seem to be available during to_prepare when running in the test environment. Another workaround I tried for this, which does work, is to force load the routes during to_prepare:

config.to_prepare do
  Rails.application.reload_routes!
  User.invalid_usernames += Rails.application.routes.routes.map(&:path).join("\n").scan(/\s\/(\w+)/).flatten.compact.uniq
end

I like this because it's DRY and easy to read. But I'm wary of reloading the routes on every request, even if it is only in development mode. I'd rather use something a little harder to read if it means I fully understand the impact. Open to criticisms!

I also ditched cattr_accessor for class_attribute when I found out the former applies to the entire class hierarchy (i.e. changing its value on a subclass would affect the superclass)

I also chose to use a Set for User.invalid_usernames instead of an array as there's no need to store and compare against dupes and it was a transparent change.

I also changed to Ruby 1.9 hash syntax (:

needle/definition-context.rb:36: warning: undefining `initialize' may cause serious problems

5 votes

I installed a new gem needle in my rails 3.1.

It installed properly but when I start my rails using command rails server --debugger

I get the following warnings:

.rvm/gems/ruby-1.9.2-p290/gems/needle-1.3.0/lib/needle/definition-context.rb:36: warning: undefining `initialize' may cause serious problems
.rvm/gems/ruby-1.9.2-p290/gems/needle-1.3.0/lib/needle/definition-context.rb:36: warning: undefining 'object_id' may cause serious problems
.rvm/gems/ruby-1.9.2-p290/gems/needle-1.3.0/lib/needle/definition-context.rb:36: warning: undefining '__send__' may cause serious problems

How can I get rid of it?

The problem is within the needle gem itself. It does this:

public_instance_methods -
[ "instance_eval", "object_id", "__id__", "__send__", "initialize",
  "remove_const", "method_missing", "method", "class", "inspect", "to_s",
  "instance_variables", "block_given?" ]

But in Ruby 1.9, the public_instance_methods method returns objects of the Symbol variety, not String. So what happens is effectively this:

[:__send__, <and other methods>] - ["__send__", <and other methods>]
=> [:__send__, <and other methods>]

When it should be NOT removing those methods in the provided Array.

This indicates to me that the library hasn't been updated (or at least tested) for Ruby 1.9. I would recommend finding where the code is for this library, forking it and then applying a patch that converts the array to symbols using something like map(&:to_sym) to fix this problem.

But be aware: there may be other cases where these differences between 1.8 and 1.9 are present.

How to split routes.rb into smaller files

5 votes

Is it possible to split Rails 3.X routes.rb file?

We have so many resources it is difficult to find them. I would like to split at least APP and REST API routes.

Thanks!

You can do that:

routes.rb

require 'application_routes'
require 'rest_api_routes'

lib/application_routes.rb

YourApplication::Application.routes.draw do
  # Application related routes
end

lib/rest_api_routes.rb

YourApplication::Application.routes.draw do
  # REST API related routes
end

how do I upload a file to my ruby on rails app using cURL?

5 votes

On the web side of things, I have two fields: name, and document. Document is a file field, and name is just what the user wants to call the file in the app.

Here is what I have tried:

curl -F "media[document]=@a.png" -F "media[name]=api" "http://example.com/medias/create.xml?api_key=123"

but I get an InvalidAuthenticityToken Error. This error only occurs when I try to upload a file / make a media object. The other API / xml commands work (the ones not involving files)

what is the correct way to upload a file using cURL?

EDIT: adding -H "Content-Type: application/xml" to the above curl command make the server generate this error:

/!\ FAILSAFE /!\  Tue Jan 24 08:45:03 -0500 2012
  Status: 500 Internal Server Error
  #<REXML::ParseException: malformed XML: missing tag start
Line: 
Position: 
Last 80 unconsumed characters:
<:??OH?ɽ?H? ???g??yx~t????op?.$?????P&W ??"?

normally the XML is supplied using the -d argument. but I don't think files can be included in xml? maybe they can? idk.

If you want to create file from xml, you should format your xml to use data, either Base64 and/or CDATA, depending of your needs. Once you have a corretly formatted xml file, you just have to send it with curl.

If you just want to upload file, you don't need to use an xml interface. You can just call :

curl -F "media[document]=@./a.png;type=image/png" -F "media[name]=api" "http://example.com/medias/

For your authentification problem, if you still have it on this url (I do not encounter it on a new rails 3.2.1 fresh app), you can access it with a regular browser, get the cookie information and send it with curl. It means that you will add to your command :

--cookie "csrf-param=authenticity_token" --cookie "csrf-value=XXXXXXX"

Or use a convenient script to send all your current Firefox cookies from an host into curl.