Best ruby-on-rails questions in November 2011

My JavaScript patterns/practices stink. Where should I seek help?

28 votes

I've been working almost exclusively on back-end tasks for the past few years, and I've just noticed that most JavaScript (and CoffeeScript) projects have got a helluva lot prettier in my absence.

I work primarily in a rails environment, and almost all my JavaScript/jQuery used to look like this:

$(an_element).an_event(function() {
  stuff_i_want_to_do;
})

$(another_element).some_other_event(function() {
  some_other_stuff_i_want_to_do;
})

Callbacks aside, that's pretty much been it.

Anyhow, was just browsing through some other folks' code and noticed many javascripters have been getting a lot prettier in my absence. This isn't complex, but it's typical of the newer/better approach to JavaScript I've been seeing:

jQuery -> 
  if $('#products').length
    new ProductsPager()

class ProductsPager
  constructor: (@page = 1) ->
    $(window).scroll(@check)

  check: =>
    if @nearBottom()
      @page++
      $(window).unbind('scroll', @check)
      $.getJSON($('#products').data('json-url'), page: @page, @render)
#

  nearBottom: =>
    $(window).scrollTop() > $(document).height() - $(window).height() - 50

  render: (products) =>
    for product in products
      $('#products').append Mustache.to_html($('#product_template').html(), product)
    $(window).scroll(@check) if products.length > 0

I've been looking for resources on modern best practices/patterns for JavaScript (and/or CoffeeScript), but I haven't had much luck. So in short, where should I look to be brought up to speed re: best javascript/coffeescript modern patterns & practices?

JavaScript resources

Patterns For Large-Scale JavaScript Application Architecture

Essential JavaScript Design Patterns For Beginners, Volume 1.

JavaScript Patterns

jQuery specific

Tools For jQuery Application Architecture

CoffeeScript

http://coffeescriptcookbook.com/chapters/design_patterns/

11 votes

Importing the language of graph databases, understand

  1. nodes (represented by circles),
  2. edges (represented by arrows), and
  3. properties (metadata of nodes / edges)

Graph Database Property Graph

The graphic (courtesy of wikipedia) describes a directed graph.

What's the best way to model an undirected graph in Rails?

That is to say, a graph where all edges are reciprocal (as in above graphic), and where the properties of each edge are the same regardless of direction (contrary to above graphic).

Let's assume a default Rails 3 setup using a sql store via ActiveRecord.

A double polymorphic association would create a directed graph, able to model the data described by the above image.

def Edge < ActiveRecord::Base
  belongs_to :head, polymorphic: true
  belongs_to :tail, polymorphic: true
end

class Node < ActiveRecord::Base
  has_many :from, as: :head
  has_many :to, as: :tail
end

class Group < ActiveRecord::Base
  # a Node of Type: Group
  has_many :from, as: :head
  has_many :to, as: :tail
end

Should one extend this model to manage inverse relationships, or is a better model available?


One element of an app may be a graph problem, but it does not mean the app is centered around the problem, that graph transversals must be performed on the data, nor that the dataset is larger than available memory.

In an undirected graph, the only thing you need to know, is whether a node is connected to another node. And there is no such thing as a direction.

Simple approach:

class Node
  has_many :connected_nodes
  has_many :nodes, :through => :connected_nodes
end

class ConnectedNode
  belongs_to :node
  belongs_to :connected_node, :class_name => 'Node'
end

This is also called an adjacency list: for each node we can easily get the list of adjacent (connected) nodes.

A possible problem with this approach: we store the connections twice. A is connected to B and B is connected to A.

So it seems better normalized to store each connection only once, and then we get really close to your original proposal.

class Connection
  belongs_to :node1, :class_name => 'Node'
  belongs_to :node2, :clasS_name => 'Node'
end

Only we do our very best to not impose any order or direction through the naming.

Retrieving the connected nodes is all the nodes connected to as node1 or as node2, hence effectively disregarding any possible direction.

In this case you also need to express a validation that a connection with (node1, node2) is unique, but that (node2, node1) is actually the same and cannot be inserted twice.

My personal choice would be to use the second schema, although maintaining the first solution might be quicker (see also this question).

I also found a very interesting article where the author explains how graphs can be stored in the database. Very profound, but more database centric.

Hope this helps.

Trim values before inserting into DB? -- Rails 3.1

9 votes

What's the simplest way to make sure models trim leading and trailing white space from string values.

One inconvenient way seems to be a before_save filter -- although for something as common as removing white space from strings, maybe there's some config that does that?

I do not believe there is a global/config setting for the reason that you would not want to always do this, so it's better done on a case by case basis as you describe and with strip(field) in a before filter.

Note: Also use caution when doing any manipulation to user values. A common best practice is to save "whatever" the user types, "warts and all" (well ok, spaces in this case). Then manipulate internally and format for display as required.
The main line of reasoning here is that when a user re-edits their info, it's better to give them 'exactly what they typed before' rather than getting caught up in what can end up being complicated validations and very brittle user interfaces.

Another Sidenote, make sure to use 'data appropriate' fields, e.g. don't store dates in strings, it's asking for trouble.

Updating massive number of records -- performance optimization

6 votes

I have a baseball tool that allows users to analyze a player's historical batting stats. For example, how many hits does A-Rod have over the past 7 days during night-time conditions? I want to expand the timeframe so a user can analyze a player's batting stats to as far back as 365 days. However, doing so requires some serious performance optimization. Here are my current set of models:

class AtBat < ActiveRecord::Base
  belongs_to :batter
  belongs_to :pitcher
  belongs_to :weather_condition

  ### DATA MODEL ###
  # id
  # batter_id
  # pitcher_id
  # weather_condition_id
  # hit (boolean)
  ##################
end

class BattingStat < ActiveRecord::Base
  belongs_to :batter
  belongs_to :recordable, :polymorphic => true # e.g., Batter, Pitcher, WeatherCondition

  ### DATA MODEL ###
  # id
  # batter_id
  # recordable_id
  # recordable_type
  # hits7
  # outs7
  # at_bats7
  # batting_avg7
  # ...
  # hits365
  # outs365
  # at_bats365
  # batting_avg365
  ##################
end

class Batter < ActiveRecord::Base
  has_many :batting_stats, :as => :recordable, :dependent => :destroy
  has_many :at_bats, :dependent => :destroy
end

class Pitcher < ActiveRecord::Base
  has_many :batting_stats, :as => :recordable, :dependent => :destroy
  has_many :at_bats, :dependent => :destroy
end

class WeatherCondition < ActiveRecord::Base
  has_many :batting_stats, :as => :recordable, :dependent => :destroy
  has_many :at_bats, :dependent => :destroy
end

For the sake of keeping my question at a reasonable length, let me narrate what I am doing to update the batting_stats table instead of copying a bunch of code. Let's start with 7 days.

  1. Retrieve all the at_bat records over the past 7 days.
  2. Iterate over each at_bat record…
  3. Given an at_bat record, grab the associated batter and associated weather_condition, find the correct batting_stat record (BattingStat.find_or_create_by_batter_and_recordable(batter, weather_condition), then update the batting_stat record.
  4. Repeat Step 3 for batter and pitcher (recordables).

Steps 1-4 are repeated for other time periods as well -- 15 days, 30 days, etc.

Now I imagine how laborious this would be to run a script every day to make these updates if I were to expand the time periods from a mangeable 7/15/30 to 7/15/30/45/60/90/180/365.

So my question is how would you approach getting this to run at the highest levels of performance?

AR isn't really meant to do bulk processing like this. You're probably better off doing your batch updates by dropping into SQL proper and doing an INSERT FROM SELECT (or perhaps using a gem that did this for you.)

Localizing a text field containing a number in Ruby on Rails

6 votes

I am currently working on a project to internationalize one of our ruby-on-rails web applications so that it can be used in other countries (France will be the first one in this case).

A particular issue I am haven't worked out yet is with the displaying of numeric fields. When display numbers for display purposes only, I do the following:

<%= number_to_percentage(tax.rate, :precision => 2)%>

In English, this shows 17.50, but in French it shows 17,50 (with a comma in place of the decimal point) which is as expected. The problem comes in the Edit form, when I show a text field

<%= f.text_field :rate, :size => 15 %>

When this renders a text box on the screen, the text box always shows 17.50 with a full stop rather than a comma for French. I am not sure that is correct.

When I tried doing the following:

<%= f.text_field :rate, :size => 15, :value => number_with_precision(f.object.rate, :precision => 2) %>

This did indeed show 17,50 in the text box for French, but when I click on the Update button to save the form, the Ruby validation kicks in and tells me that 17,50 is not a number (or rather it says "n'est pas un nombre"). I have to enter 17.50 to get it to save.

To be honest, I am not entirely sure on the correct thing to do here. Should all countries enter numbers with full stops in text boxes, or is there a way to get Ruby-on-Rails to display commas, and validate them appropriately?

TL;DR

This is the kind of things I hate to do over and over again (I'm serving french users, they're easily confused with dots as the decimal separator).

I exclusively use the delocalize gem now, which does the format translation automatically for you. You just have to install the gem and leave your forms as-is, everything should be taken care of for you.

I like to read, give me the long explanation

The basic conversion is quite simple, you have to convert back and forth between the following formats:

  • The backend one, which is usually English, used by your persistent storage (SQL database, NoSQL store, YAML, flat text file, whatever struck your fancy, ...).

  • The frontend one, which is whatever format your user prefers. I'm going to use French here to stick to the question*.

* also because I'm quite partial towards it ;-)

This means that you have two points where you need to do a conversion:

  1. Outbound: when outputting your HTML, you will need to convert from English to French.

  2. Inbound: When processing the result of the form POST, you will need to convert back from French to English.

The manual way

Let's say I have the following model, with the rate field as a decimal number with a precision of 2 (eg. 19.60):

class Tax < ActiveRecord::Base
  # the attr_accessor isn't really necessary here, I just want to show that there's a database field
  attr_accessor :rate
end

The outbound conversion step (English => French) can be done by overriding text_field_tag:

ActionView::Helpers::FormTagHelper.class_eval do
  include ActionView::Helpers::NumberHelper

  alias original_text_field_tag text_field_tag
  def text_field_tag(name, value = nil, options = {})
    value = options.delete(:value) if options.key?(:value)
    if value.is_a?(Numeric)
      value = number_with_delimiter(value) # this method uses the current locale to format our value
    end
    original_text_field_tag(name, value, options)
  end
end

The inbound conversion step (French => English) will be handled in the model. We will override the rate attribute writer to replace every French separator with the English one:

class Tax < ActiveRecord::Base
  def rate=(rate)
    write_attribute(:rate, rate.gsub(I18n.t('number.format.separator'), '.')
  end
end

This look nice because there's only one attribute in the example and one type of data to parse, but imagine having to do this for every number, date or time field in your model. That's not my idea of fun.

This also is a naïve* way of doing the conversions, it does not handle:

  • Dates
  • Times
  • Delimiters (eg. 100,000.84)

* did I already tell you I like French?

Enter delocalize

Delocalize is working on the same principle I outlined above, but does the job much more comprehensively:

  • It handles Date, Time, DateTime and numbers.
  • You don't have to do anything, just install the gem. It checks your ActiveRecord columns to determine if it's a type that needs conversion and does it automatically.
  • The number conversions are pretty straightforward, but the date ones are really interesting. When parsing the result of a form, it will try the date formats defined in your locale file in descending order and should be able to understand a date formatted like this: 15 janvier 2012.
  • Every outbound conversion will be done automatically.
  • It's tested.
  • It's active.

One caveat though: it doesn't handle client-side validations. If you're using them, you will have to figure out how to use i18n in your favourite JavaScript framework.

Rails sms_fu error

6 votes

brendan lims instructions for setting up the sms_fu gem say run either

sms_fu = SMSFu::Client.configure(:delivery => :action_mailer)

or

sms_fu = SMSFu::Client.configure(:delivery => :pony, :pony_config => { :via => :sendmail })

or

PONY_CONFIG = { 
 :via => :smtp, 
 :via_options => {
 :address              => 'smtp.gmail.com',
 :port                 => '587',
 :user_name            => 'username',
 :password             => 'password',
 :authentication       => :plain, 
 :enable_starttls_auto => true,
 :domain               => "localhost.localdomain"
}}
sms_fu = SMSFu::Client.configure(:delivery => :pony, :pony_config => PONY_CONFIG)

I tried the first (assuming actionmailer is set up by default, maybe thats wrong). Then the second (after installing pony and mailing something to be sure it works, which was fine) and the last. No errors show in anthing above. The instructions say after this set up, a command like this is supposed to work

sms_fu.deliver("5558675309","at&t","message")

In all set ups i get this error

NameError: uninitialized constant RAILS_ROOT
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:102:in `template_directory'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:55:in `config_yaml'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:65:in `from_address'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:41:in `deliver'
from (irb):28
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands/console.rb:45:in `start'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands/console.rb:8:in `start'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

How do i solve this.

P.S.

Im on rails 3

  • Edit

Now i get, after changing RAILS_ROOT to Rails.root in line 102 of lib/sms_fu/sms_fu.rb (according to Dogbert's answer)

Psych::SyntaxError: couldn't parse YAML at line 7 column 11
from /Users/name/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/psych.rb:148:in `parse'
from /Users/name/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/psych.rb:148:in `parse_stream'
from /Users/name/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/psych.rb:119:in `parse'
from /Users/name/.rvm/rubies/ruby-1.9.2-p290/lib/ruby/1.9.1/psych.rb:106:in `load'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:55:in `config_yaml'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:65:in `from_address'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/sms_fu-1.1.2/lib/sms_fu/sms_fu.rb:41:in `deliver'
from (irb):6
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands/console.rb:45:in `start'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands/console.rb:8:in `start'
from /Users/name/.rvm/gems/ruby-1.9.2-p290@rails3tutorial/gems/railties-3.1.0/lib/rails/commands.rb:40:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'

my sms_fu.yml looks like

config:
  from_address: noreply@domain.com

carriers:
 alltel:
  name: Alltel
  value: @message.alltel.com
 ameritech:
  name: Ameritech
  value: @paging.acswireless.com
 at&t: 
  name: AT&T
  value: @txt.att.net
 bell-atlantic: 
  name: Bell Atlantic
  value: @message.bam.com
 bellsouthmobility:
  name: Bellsouth Mobility
  value: @blsdcs.net
 blueskyfrog: 
  name: BlueSkyFrog
  value: @blueskyfrog.com
 boost: 
  name: Boost Mobile
  value: @myboostmobile.com
 cellularsouth: 
  name: Cellular South
  value: @csouth1.com
 comcast: 
  name: Comcast PCS
  value: @comcastpcs.textmsg.com
 cricket: 
  name: Cricket
  value: @sms.mycricket.com
 kajeet: 
  name: kajeet
  value: @mobile.kajeet.net
 metropcs: 
  name: Metro PCS
  value: @mymetropcs.com
 nextel:
  name: Nextel
  value: @messaging.nextel.com
 powertel: 
  name: Powertel
  value: @ptel.net
 pscwireless: 
  name: PSC Wireless
  value: @sms.pscel.com
 qwest: 
  name: Qwest
  value: @qwestmp.com
 southernlink: 
  name: Southern Link
  value: @page.southernlinc.com
 sprint: 
  name: Sprint PCS
  value: @messaging.sprintpcs.com
 suncom: 
  name: Suncom
  value: @tms.suncom.com
 t-mobile: 
  name: T-Mobile
  value: @tmomail.net
 tracfone: 
  name: Tracfone
  value: @mmst5.tracfone.com
 telus-mobility: 
  name: Telus Mobility
  value: @msg.telus.com
 virgin: 
  name: Virgin Mobile
  value: @vmobl.net
 verizon: 
  name: Verizon Wireless
  value: @vtext.com
 # International Carriers
 aliant-canada:
  name: Aliant (Canada)
  value: @chat.wirefree.ca
 beeline-ua: 
  name: Beeline
  value: @sms.beeline.ua
 bellmobility-canada:
  name: Bell Mobility (Canada)
  value: @txt.bell.ca
 bpl-mobile: 
  name: BPL Mobile
  value: @bplmobile.com
 claro-brazil:
  name: Claro (Brazil)
  value: @clarotorpedo.com.br
 claro-nicaragua: 
  name: Claro (Nicaragua)
  value: @ideasclaro-ca.com
 du-arab-emirates: 
  name: Du (UAE)
  value: @email2sms.ae
 e-plus-germany: 
  name: E-Plus (Germany)
  value: @smsmail.eplus.de
 etisalat-arab-emirates: 
  name: Etisalat (UAE)
  value: @email2sms.ae
 fido-canada: 
  name: Fido
  value: @fido.ca
 manitobatelecom-canada:
  name: Manitoba Telecom (Canada)
  value: @text.mtsmobility.com
 mobinil-egypt:
  name: Mobinil
  value: @mobinil.net
 mobistar-belgium: 
  name: Mobistar (Belgium)
  value: @mobistar.be
 mobitel: 
  name: Mobitel
  value: @sms.mobitel.lk
 movistar-spain: 
  name: Movistar (Spain)
  value: @correo.movistar.net
 northerntel-canada:
  name: NorthernTel (Canada)
  value: @txt.northerntelmobility.com
 o2-germany: 
  name: o2 (Germany)
  value: @o2online.de
 o2-uk: 
  name: o2 (UK)
  value: @mmail.co.uk
 orange-mumbai: 
  name: Orange (Mumbai)
  value: @orangemail.co.in
 orange-netherlands: 
  name: Orange (Netherlands)
  value: @sms.orange.nl
 orange-uk: 
  name: Orange (UK)
  value: @orange.net
 rogers-wireless: 
  name: Rogers Wireless
  value: @pcs.rogers.com
 rogers-canada:
  name: Rogers (Canada)
  value: @pcs.rogers.ca
 sasktel-canada:
  name: SaskTel (canada)
  value: @sms.sasktel.ca
 sfr-france: 
  name: SFR (France)
  value: @sfr.fr
 t-mobile-austria: 
  name: T-Mobile (Austria)
  value: @sms.t-mobile.at
 t-mobile-germany: 
  name: T-Mobile (Germany)
  value: @t-d1-sms.de
 t-mobile-germany: 
  name: T-Mobile (Netherlands)
  value: @gin.nl
 t-mobile-uk: 
  name: T-Mobile (UK)
  value: @t-mobile.uk.net
 telebec-canada:
  name: Telebec (Canada)
  value: @txt.telebecmobilite.com
 telefonica-spain: 
  name: Telefonica (Spain)
  value: @movistar.net
 telus-canada:
  name: Telus (Canada)
  value: @msg.telus.com
 virgin-canada:
  name: Virgin (Canada)
  value: @vmobile.ca 
 vodafone-germany: 
  name: Vodafone (Germany)
  value: @vodafone-sms.de
 vodafone-egypt: 
  name: Vodafone (Egypt)
  value: @vodafone.com.eg
 vodafone-uk: 
  name: Vodafone (UK)
  value: @sms.vodafone.net
 vodafone-italy: 
  name: Vodafone (Italy)
  value: @sms.vodafone.it
 vodafone-jp-chuugoku: 
  name: Vodafone (Japan - Chuugoku)
  value: @n.vodafone.ne.jp
 vodafone-jp-hokkaido: 
  name: Vodafone (Japan - Hokkaido)
  value: @d.vodafone.ne.jp
 vodafone-jp-hokuriko: 
  name: Vodafone (Japan - Hokuriko)
  value: @r.vodafone.ne.jp
 vodafone-jp-kansai: 
  name: Vodafone (Japan - Kansai)
  value: @k.vodafone.ne.jp
 vodafone-jp-osaka: 
  name: Vodafone (Japan - Osaka)
  value: @k.vodafone.ne.jp
 vodafone-jp-kanto: 
  name: Vodafone (Japan - Kanto)
  value: @k.vodafone.ne.jp
 vodafone-jp-koushin: 
  name: Vodafone (Japan - Koushin)
  value: @k.vodafone.ne.jp
 vodafone-jp-tokyo: 
  name: Vodafone (Japan - Tokyo)
  value: @k.vodafone.ne.jp
 vodafone-jp-kyuushu: 
  name: Vodafone (Japan - Kyuushu)
  value: @q.vodafone.ne.jp
 vodafone-jp-okinawa: 
  name: Vodafone (Japan - Okinawa)
  value: @q.vodafone.ne.jp
 vodafone-jp-shikoku: 
  name: Vodafone (Japan - Shikoku)
  value: @s.vodafone.ne.jp
 vodafone-jp-touhoku: 
  name: Vodafone (Japan - Touhoku)
  value: @h.vodafone.ne.jp
 vodafone-jp-niigata: 
  name: Vodafone (Japan - Niigata)
  value: @h.vodafone.ne.jp
 vodafone-jp-toukai: 
  name: Vodafone (Japan - Toukai)
  value: @h.vodafone.ne.jp
 vodafone-spain: 
  name: Vodafone (Japan - Spain)
  value: @vodafone.es

read through the comments on the right answer to know the full solution

This plugin hasn't been updated for a while. Rails deprecated RAILS_ROOT in favor of Rails.root

You'll need to remove RAILS_ROOT from this file, line 102, and replace with Rails.root

  directory = defined?(Rails) ? "#{RAILS_ROOT}/config" : "#{File.dirname(__FILE__)}/../../templates"

to

  directory = defined?(Rails) ? "#{Rails.root}/config" : "#{File.dirname(__FILE__)}/../../templates"

https://github.com/brendanlim/sms-fu/blob/master/lib/sms_fu/sms_fu.rb#L102

Rails 3.1 - Developing with Mountable Engines

5 votes

I am developing 2 apps for 2 separate clients, and a 3rd for myself. All 3 apps will have an address book, emailing, and user authentication. Finally, I want the apps to be created "modular" so that I can add (or remove) modules as the are needed.

I have watched the Mountable Engines Railscast and it looks like what I need, yet I am still unclear on the following:

  1. How do you develop each engine module (user authentication, emailing, address book) separately without a common base application?
  2. How do you develop the address book module when it requires user authentication, which is a separate module altogether?

I'm guessing that you develop the user authentication first, and include that in the dummy app of the engine module, but I'm getting confused with the namespacing of the engine.

It might be useful to note that I come from a C# background and I am new to web-development.

Thanks in advance for your help.

People have already written authentication and emailing plugins. For most apps, the only work left to be done for those modules is customization.

You might want to write a engine to share some of the customizations and to share some of the address book functionality. In in that case, you can consider developing an example app which has as little custom code as possible to clearly define the address book api.

attr_accessor strongly typed Ruby on Rails

5 votes

Just wondering if anyone can shed some light on the basics of getter setters in Ruby on Rails with a view on strongly typed. I am very new to ruby on rails and predominately have a good understanding of .NET.

For example, let's consider we have a .net class called Person

class Person
{
 public string Firstname{get;set;}
 public string Lastname{get;set;}
 public Address HomeAddress{get;set;}
}

class Address
{
 public string AddressLine1{get;set;}
 public string City{get;set;}
 public string Country{get;set;}
}

In Ruby, I would write this as

class Person
 attr_accessor :FirstName
 attr_accessor :LastName
 attr_accessor :HomeAddress
end

class Address
 attr_accessor :AddressLine1
 attr_accessor :City
 attr_accessor :Country
end

Looking at the Ruby version of the Person class how do I specify the types for the accessor methods FirstName, LastName and HomeAddress? If I were to consume this class I could feed any type into HomeAddress but I want this accessor method to accept only the TYPE Address.

Any suggestions ?

thanks

TL;DR: No it's not possible ... and long answer, yes it is possible, read the metaprogramming section :)

Ruby is a dynamic language, that's why you won't get compile time type warnings/errors as you get in languages like C#.

Same as you can't specify a type for a variable, you can't specify a type for attr_accessor.

This might sound stupid to you coming from .NET, but in the Ruby community, people kind of expect you to write tests. If you do so, these types of problems will basically vanish. In Ruby on Rails, you should test your models. If you do so, you won't really have any trouble with accidentaly assigning something somewhere wrong.

If you're talking about ActiveRecord in Ruby on Rails specifically, assigning a String into an attribute which is defined as an Integer in the database will result in exception being thrown.

By the way, according to convention, you shouldn't use CamelCase for attributes, so the correct class definition should be

class Person
 attr_accessor :first_name
 attr_accessor :last_name
 attr_accessor :home_address
end

class Address
 attr_accessor :address_line1
 attr_accessor :city
 attr_accessor :country
end

One reason for this is that if you Capitalize the first letter, Ruby will define a constant instead of a variable.

number = 1   # regular variable
Pi = 3.14159 # constant ... changing will result in a warning, not an error

Metaprogramming hacks

By the way, Ruby also has insanely huge metaprogramming capabilities. You could write your own attr_accessor with a type check, that could be used something like

typesafe_accessor :price, Integer

with definition something like

class Foo

  # 'static', or better said 'class' method ...
  def self.typesafe_accessor(name, type)

    # here we dynamically define accessor methods
    define_method(name) do
      # unfortunately you have to add the @ here, so string interpolation comes to help
      instance_variable_get("@#{name}")
    end

    define_method("#{name}=") do |value|
      # simply check a type and raise an exception if it's not what we want
      # since this type of Ruby block is a closure, we don't have to store the 
      # 'type' variable, it will 'remember' it's value 
      if value.is_a? type
        instance_variable_set("@#{name}", value)
      else
        raise ArgumentError.new("Invalid Type")
      end
    end
  end

  # Yes we're actually calling a method here, because class definitions
  # aren't different from a 'running' code. The only difference is that
  # the code inside a class definition is executed in the context of the class object,
  # which means if we were to call 'self' here, it would return Foo
  typesafe_accessor :foo, Integer

end

f = Foo.new
f.foo = 1
f.foo = "bar" # KaboOoOoOoM an exception thrown here!

or at least something along these lines :) This code works! Ruby allows you to define methods on the fly, which is how attr_accessor works.

Also blocks are almost always closures, which means I can do the if value.is_a? type without passing it as a parameter.

It's too complicated to explain here when this is true and when it's not. In short, there are different types of blocks

  • Proc, which is created by Proc.new
  • lambda, which is created by the keyword lambda

one of the differences is that calling return in a lambda will only return from the lambda itself, but when you do the same thing from a Proc, the whole method around the block will return, which is used when iterating, e.g.

def find(array, something)
  array.each do |item| 
    # return will return from the whole 'find()' function
    # we're also comparing 'item' to 'something', because the block passed
    # to the each method is also a closure
    return item if item == something
  end
  return nil # not necessary, but makes it more readable for explanation purposes
end    

If you're into this kind of stuff, I recommend you check out PragProg Ruby Metaprogramming screencast.

Facebook app invite from Ruby on Rails

5 votes

Is there any way to post a Facebook application invite from Ruby on Rails, e.g. by deploying Koala?

Looks to be impossible at the glance. Any workarounds other than simply posting to a wall?

Actually mikeonrails gave a correct link - the Requests dialog is the way to invite friends to your app and send them other types of requests. It does require user interaction though (just as the video shows), for requests sent to users who don't have the application installed.

And now for the details. There are 2 types of requests that you can send:

  • user-generated requests: these can be sent to users who don't have the application installed (ie. application invite). They can only be sent using the Javascript SDK (or the iOS or Android SDKs but I don't think you're interested in those) and they do require user interaction. It will consist of a pop-up that will either display a selection (made by you) of his friends or a friend selector and a send button to send them your message.
  • app-generated requests: these can only be sent to users who have the application installed, but can be sent without user interaction.

The code for user-generated requests is like this (using the Javascript SDK):

// this will show the pop-up dialog with a friend selector
// add a `to: 'friend_id1,friend_id2` to skip the friend selector
FB.ui({
    method: 'apprequests',
    message: 'My Great Request'
}, requestCallback);

For the app-generated requests you can use Koala like this:

user = Koala::Facebook::API.new(user_token)
user.put_object("user_with_app_installed_id", "apprequests", {:message => "Would you like to be friends?")

So, the conclusion is that you cannot invite a user's friends to your application without his approval, but you can make it really simple for him to do it (2 clicks).

If you'd like to read more:

Rails Performance Tuning for Production?

5 votes

I'm getting close to deploying an application built on Rails 3.1.x and started running some performance tests. After fiddling with ab for a bit, I'm seeing some very discouraging results yielding around 15 requests / second on Heroku.

When testing locally I see similar results that really demonstrates that it's an app issue more than anything.

I'm running Unicorn, which is about 40% faster than Thin on Celadon Cedar. Further, I'm using the PGSQL shared db.

I'm hopeful that someone could share a laundry list or essentially a launch checklist that I should move through when prepping an app for production and moving into the need for speed tuning. So far I've not found a real concise list of actionable items to move through that seems to make sense given my situation.

Or if you have solid practical experience moving through issues like this, any input would be appreciated!

There are some pretty low-hanging fruit that almost always yield pretty worthy performance gains:

  1. Reduce the number of DB queries by using more efficient ActiveRecord statements. Be sure to use include and join where appropriate, and make sure you're using empty? over any? where possible to avoid SELECTs when you just need a COUNT.
  2. Especially on heavier pages, cache views, even if only for a few minutes. You can often break larger or dynamic pieces into partials that can be cached without any negative effects, too.
  3. Move any over-the-network activity to background jobs. This includes sending emails, fetching pages from anther website, and making API calls (even [especially?] to Heroku). There are a number of really good background job processing libraries in Ruby, DelayedJob is really popular because it works with any ActiveRecord database, but my favorite is Resque.

You need to be careful not to spend too much time optimizing Ruby routines. Unless you're doing something with a huge amount of data or processing (e.g. image resizing) you probably won't see very significant gains from optimizing loops or minimizing memory usage. And if you find certain pages are problematic, dig into your logs and see what is happening during those requests.

And if you're not already, autoscaling applications like HireFireApp are great for letting you handle loads of requests by scaling horizontally without the cost of running extraneous dynos during slow periods.

PS: There is a new Heroku Add-On called Blitz that lets you test a concurrent load of up to 5,000 users.

Objective C + Reskit - How do I wrap my dictionary with a key to avoid formatting problems?

5 votes

I'm trying to wrap my HTTP POST request with a key. In other words, I want to turn this:

{
"category_id"=>"1", 
"food_name_token"=>"Pizza", 
 "id"=>"1"
}

into this:

{
"dish" => 
    {
    "category_id"=>"1", 
    "food_name_token"=>"Pizza", 
    "id"=>"1"
    }
}

I tried using the 'rootKeyPath' method in RestKit:

serializationMapping.rootKeyPath = @"dish";    

But that gave me this weirdly formatted string :

{
"dish"=>
    "{
    \n \"category_id\" = 1; 
    \n \"food_name_token\" = Pizza;
    \n id = 1;
    \n}
"}

It uses equal signs and semicolons instead of arrows and commas, and adds in all these linebreaks and escape backslashes.

Any idea why? And any suggestions on what I can do instead?

P.S. I'm using a Rails backend

I found out with Restkit I can wrap attributes using brackets:

    [dishMapping mayKeyPath:@"dish[food_name_token]" toAttribute:@"placeToken"];

And this gives me a normal output without the weird formatting.

Rails - Non blocking HTTP requests?

4 votes

I want to display some Amazon products, loaded via Ajax.

I call the method below with Ajax, but the request takes a couple of seconds.

@items = []
@shows.shuffle.first(5).each do |show|
    req = AmazonProduct["us"]
    req.configure do |c|
      c.key = "###"
      c.secret = "###"
      c.tag = "###"
    end
    req << { :operation => 'ItemSearch',
             :search_index => params[:product_type],
             :response_group => %w{ItemAttributes Images},
             :keywords => show.name,
             :sort => "" }
    resp = req.get
    @items << resp.find('Item').shuffle.first
end

I've nothiced that this Action blocks the server. I've tried having the site open in another tab. That tab won't start loading until the first tab with the Ajax call completes.

How can I go about solving this problem?


Setup:

Ubuntu 10.10
Rails 3.1.1
Ruby 1.9.2
Gem: https://github.com/hakanensari/amazon_product

I don't think this is possible in rails.

The way how I would approach this problem is to create a simple sinatra-synchrony app that has only action for fetching products that you want from amazon.

To be able to send ajax request to this app, you need to point this app to your domain (subdomain).

I don't see any other solution, at the moment. Of course you can have more than one application process on your server, but this will not solve your problem in long term.

So to wrap up:

AJAX -> amazon-producs-sinatra-app.yourdomain.com 

Need a less repetitve way to start Rails on Mac for noob

4 votes

I've been on a Mac just over a month and before that I've been using Windows and wrote a .bat file to run a few other little .bat files to fire up MongoDB, guard start for livereload, rails server, and a rails console. And it lastly launches e-texteditor which opens that rails dir as a project.

I hope this is the right place to ask and not superuser. So what is one way I can automate these things on Mac? -- Currently, I launch 1 terminal window with 4 tabs. tab 1 runs 'mongod', tab 2 runs 'rails s', tab 3 runs 'guard start', tab 4 runs 'rails c'. then I open one more tab and do 'mate .'

Foreman is your best friend.

Step 1: Install foreman

gem i foreman

Step 2: Create a file named Procfile

rails: rails server
mongo: mongod
guard: guard start

Step 3: Run foreman start. It will automatically start all 3 servers. If you press Ctrl-C, all servers will be terminated.

Step 4: Run rails c in another terminal.