Rails 3 - Seed.rb data for Money Class - ruby-on-rails

I am trying out seeds.rb for the first time, and one of my data models uses encapsulation provided by the money gem.
Relevant gems:
money (3.6.1)
rails (3.0.5)
My model thus far:
app/models/list.rb
class List < ActiveRecord::Base
attr_accessible :alias, :unit, :participating_manufacturer, :quantity
:latest_price_cents, :latest_price_currency, :url
belongs_to :user
composed_of :latest_price,
:class_name => "Money",
:mapping => [%w(latest_price_cents latest_price_cents), %w(latest_price_currency currency_as_string)],
:constructor => Proc.new {
|latest_price_cents, latest_price_currency| Money.new(latest_price_cents ||
0, latest_price_currency || Money.default_currency)
},
:converter => Proc.new {
|value| value.respond_to?(:to_money) ? value.to_money : raise(ArgumentError,
"Can't convert #{value.class} to Money")
}
end
1) (Addressed successfully)
2) When I get to writing validations, would it be best to write them for the :latest_price attribute or for the :latest_price_cents & :latest_price_currency attributes seperately?
/db/seeds.rb
users = User.create([{ :name => "Foo", :email => "foo#gmail.com",
:password => "foobar", :password_confirmation => "foobar" }])
# etc, will add more users to the array
list = List.create(:user_id => users.first.id, :alias => "Januvia 100mg",
:unit => "tablet", :participating_manufacturer => "Merck",
:quantity => 30, :latest_price_cents => 7500,
:latest_price_currency => "USD", :url =>
"http://www.foobar.com/januvia/100mg-tablets/")
3) Perhaps it is minutiae, but in the seed, should I be assigning values to the virtual :latest_price attribute or to the latest_price_cents and latest_price_currency attributes directly? Is there any way to use faker rather than /db/seeds.rb to perform this task?
I am new to rails and web development.

I can't see your latest_price attribute anywhere, so I'm not sure how to answer your question. Generally, you should validate the attributes entered in the user form. So if a user enters latest_price_cents and latest_price_currency in a form, then they're the ones which need validating.
There's a bug in your seed file. You want to pass in a hash, not an array, when creating a new user; and users should be an array.
users = []
users << User.create!(:name => "Foo",
:email => "foo#gmail.com",
:password => "foobar",)
:password_confirmation => "foobar")
However, if you're considering faker because you want to create some dummy data, take a look at Machinist or Factory Girl. They're designed for creating dummy data, normally for automated tests.
Once you've set up some blueprints, if you want to create dummy data in your seeds file, you can do something like this in seeds.rb:
20.times { List.make } unless Rails.env.production?

Related

Targeting announcements Starburst gem

I am using Starburst gem to show announcements upon user login. There is an option to limit the users
Starburst::Announcement.create(
:body => 'Upgrade to platinum and save 10% with coupon code XYZ!',
:limit_to_users =>
[
{
:field => "subscription",
:value => "gold"
}
]
)
Is there any way to pass multiple values for a field like this:
{
:field => "subscription",
:value => ["gold","silver"]
}
Thanks
Currently Starburst doesn't support 'or' in queries. Your best bet is to create a method on your user class and use that for targeting, which will achieve the same result.
Add to the User class:
class User < ActiveRecord::Base
def gold_or_silver
subscription == "gold" || subscription == "silver"
end
end
Then enable the method in config/initializers/starburst.rb (create it if it does not already exist):
Starburst.configuration do |config|
config.user_instance_methods = ["gold_or_silver"] '
end
Then in filtering the message:
Starburst::Announcement.create(
:body => 'Upgrade to platinum and save 10% with coupon code XYZ!',
:limit_to_users =>
[
{
:field => "silver_or_gold",
:value => true
}
]
)

rake db:seed give validation error: Email already exists

I am setting up a fork on one of the projects.
After creating the database I:
rake db:schema:load
then
rake db:seed
to load seed data.
This throws an error
rake aborted! Validation failed: Webiso account has already been
taken, Email has already been taken
Where Webiso and Email are the fields in one of the tables.
I have dropped, created and loaded the schema again.
I have tried the buzzwords. db:reset, db:setup etc.
Nothing works.
Any help would be appreciated.
db/seeds.rb
# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
#
# Examples:
#
# cities = City.create([{ :name => 'Chicago' }, { :name => 'Copenhagen' }])
# Mayor.create(:name => 'Daley', :city => cities.first)
require 'factory_girl'
FactoryGirl.define do
factory :xx, :parent => :person do
first_name "xx"
last_name "xx"
twiki_name "xx"
human_name "xx"
email "xxx#xx"
is_staff 1
end
factory :ed, :parent => :person do
first_name "Ed"
last_name "xx"
twiki_name "xx"
human_name "xx"
email "xx#xx"
is_staff 1
image_uri "/images/staff/xx.jpg"
end
factory :anubhav, :parent => :person do
is_student 1
is_part_time 0
graduation_year "2021"
masters_program "SE"
masters_track "Tech"
twiki_name "AnubhavAeron"
first_name "Anubhav"
last_name "Aeron"
human_name "Anubhav Aeron"
email "xx#xx"
webiso_account "x#xx"
end
end
Factory(:task_type, :name => "Working on deliverables")
Factory(:task_type, :name => "Readings")
Factory(:task_type, :name => "Meetings")
Factory(:task_type, :name => "Other")
xx = Factory.create(:xx)
xx = Factory.create(:xx)
Factory.create(:anubhav)
Factory.create(:team_terrific) #This will create awe_smith, betty_ross, and charlie_moss
FactoryGirl.create(:presentation_feedback_questions, :label => "Content", :text => "Did the talk cover all the content suggested on the checklist? (ie goals, progress, and the process for achieving the goals, outcomes)")
FactoryGirl.create(:presentation_feedback_questions, :label => "Organization", :text => "How logical was the organization? How smooth were transactions between points and parts of the talk? Was the talk focused? To the point? Were the main points clearly stated? easy to find?")
FactoryGirl.create(:presentation_feedback_questions, :label => "Visuals", :text => "Were they well-designed? Were all of them readable? Were they helpful? Were they manipulated well?")
FactoryGirl.create(:presentation_feedback_questions, :label => "Delivery", :text => "Bodily delivery: (eye-contact, gestures, energy) Vocal delivery: (loudness, rate, articulation) Question handling (poise, tact, team support; did the team answer the question asked?)")
This (typo?) may be the cause of your error:
xx = Factory.create(:xx)
xx = Factory.create(:xx)
which will try to create two Person instances and save them to the database. Both of them share the same attributes (email) which triggers your validates_uniqueness_of :email (or similar) validation in your Person model.
N.B. if it's not a typo, please have a look into sequences and/or Faker.

using scope in Rails custom validations

I want to apply scope limiter in my custom validation
I have this Product Model
which has make,model,serial_number, vin as a attributes
Now I have a custom validation to check against vin if vin is not present to check for combination of make+model+serial_number uniqueness in database something like this
validate :combination_vin,:if => "vin.nil?"
def combination_vin
if Product.exists?(:make => make,:model => model,:serial_number => serial_number)
errors.add(:base,"The Combination of 'make+model+serial_number' already present")
end
end
I want to introduce a scope in this validator against user_id
Now I know I could easily write this to achieve same using
def combination_vin
if Product.exists?(:make => make,:model => model,:serial_number => serial_number,:user_id => user_id)
errors.add(:base,"The Combination of 'make+model+serial_number' already present")
end
end
But out of curiosity I was thinking is there a scope validator (something like {:scope => :user_id}) on custom validation
so that I dont have to pass that extra user_id in the exists? hash
Thanks
Try :
validate :combination_vin , :uniqueness => { :scope => :user_id } , :if => "vin.nil?"

Is there find_or_create_by_ that takes a hash in Rails?

Here's some of my production code (I had to force line breaks):
task = Task.find_or_create_by_username_and_timestamp_and_des \
cription_and_driver_spec_and_driver_spec_origin(username,tim \
estamp,description,driver_spec,driver_spec_origin)
Yes, I'm trying to find or create a unique ActiveRecord::Base object. But in current form it's very ugly. Instead, I'd like to use something like this:
task = Task.SOME_METHOD :username => username, :timestamp => timestamp ...
I know about find_by_something key=>value, but it's not an option here. I need all values to be unique. Is there a method that'll do the same as find_or_create_by, but take a hash as an input? Or something else with similat semantics?
Rails 3.2 first introduced first_or_create to ActiveRecord. Not only does it have the requested functionality, but it also fits in the rest of the ActiveRecord relations:
Task.where(attributes).first_or_create
In Rails 3.0 and 3.1:
Task.where(attributes).first || Task.create(attributes)
In Rails 2.1 - 2.3:
Task.first(:conditions => attributes) || Task.create(attributes)
In the older versions, you could always write a method called find_or_create to encapsulate this if you'd like. Definitely done it myself in the past:
class Task
def self.find_or_create(attributes)
# add one of the implementations above
end
end
I also extend the #wuputah's method to take in an array of hashes, which is very useful when used inside db/seeds.rb
class ActiveRecord::Base
def self.find_or_create(attributes)
if attributes.is_a?(Array)
attributes.each do |attr|
self.find_or_create(attr)
end
else
self.first(:conditions => attributes) || self.create(attributes)
end
end
end
# Example
Country.find_or_create({:name => 'Aland Islands', :iso_code => 'AX'})
# take array of hashes
Country.find_or_create([
{:name => 'Aland Islands', :iso_code => 'AX'},
{:name => 'Albania', :iso_code => 'AL'},
{:name => 'Algeria', :iso_code => 'DZ'}
])

Ruby on Rails attr_protected

I have the following code in my User model:
attr_protected :email
I'm trying to create a new user object, but I get a mass assignment protected error with the following code.
user = User.new(
:first_name => signup.first_name,
:last_name => signup.last_name,
:email => signup.email,
:birthday => signup.birthday,
:encrypted_password => signup.encrypted_password,
:salt => signup.salt
)
Does anyone know how I can work around the attr_protected to get this code to work and assign a value to email?
Thank you.
user = User.new(
:first_name => signup.first_name,
:last_name => signup.last_name,
:birthday => signup.birthday,
:encrypted_password => signup.encrypted_password,
:salt => signup.salt
)
user.email = signup.email
I literally just wrote a gem tonight to deal with this exact issue. I'm planning on adding it to RubyGems.org later this week after I give a presentation on it. Feel free to checkout the code in the mean time. http://github.com/beerlington/sudo_attributes
Using the gem, your code would change to:
user = User.sudo_new(
:first_name => signup.first_name,
:last_name => signup.last_name,
:email => signup.email,
:birthday => signup.birthday,
:encrypted_password => signup.encrypted_password,
:salt => signup.salt
)
You could also use sudo_create() if you want to save the instance
update_attributes now allows you to override the protection if you know the hash to be safe, say for internal use where the fields you're setting are not coming from user-controllable hash.
user = User.new({
first_name: signup.first_name,
last_name: signup.last_name,
email: signup.email,
birthday: signup.birthday,
encrypted_password: signup.encrypted_password,
salt: signup.salt
}, {without_protection: true})
You may also want to consider roles:
User.new(params, as: :admin)
The core of the protection is on self.attributes=, which if you use self.send you can trigger a special hidden parameter at the end called guard_protected_attributes as false.
Example:
self.send(:attributes=, hash, false)
This would skip the protection feature completely. It doesn't work on new, but you can simply create the object first then call the same method and save, not too painful I guess.

Resources