Why doesn't this test in the Diaspora app fail? - testing

From http://github.com/diaspora/diaspora/blob/master/spec/models/profile_spec.rb
describe Profile do
before do
#person = Factory.build(:person)
end
describe 'requirements' do
it "should include a first name" do
#person.profile = Factory.build(:profile,:first_name => nil)
#person.profile.valid?.should be false
#person.profile.first_name = "Bob"
#person.profile.valid?.should be true
end
end
end
But in http://github.com/diaspora/diaspora/blob/master/app/models/profile.rb is validated the presense of both, the first and last name like so validates_presence_of :first_name, :last_name
Why does the above test pass even though a last name isn't specified?

last_name is actually specified. The profile is create using the Factory.build, which returns the predefined mock of :profile, which is
Factory.define :profile do |p|
p.first_name "Robert"
p.last_name "Grimm"
end

I suspect the Factory.build(:profile, ...) call creates a profile model with a default first_name and last_name set, unless specified otherwise (by the :first_name => nil in this example).
However that's just an educated guess which I am inferring from the code above and what I see here.

Related

sunspot surely this can be done with some ruby magic?

I need to access my rails model instance from inside the searchable block as indicated below.
class Product
include MongoMapper::Document
include Sunspot::Rails::Searchable
key :field_names, Array
searchable do |ss|
self.field_names.each do |field|
ss.double field[:name] do
field[:value]
end
end
end
end
does anyone know how to do this via Sunspot ?
I have a field_names array on each product instance that is different per product so i need to access it.
Thanks a lot
Rick
you mean this?
def Foo
attr_accessible :id, :title
def fields
['something']
end
searchable do
integer :id
string :title
string :fields, :multiple => true do
self.fields
end
end
end
well inside there, you're inside a different evaluation context (Solr::DSL or something like that). That's to provide the ability to have those keywords like "integer, string". Looks like you're trying to evaluate dynamic attributes/filters .. .. so see my modified response (below)
you mean this?
def Foo
attr_accessible :id, :title
#fields_to_dynamically_add = ['title']
searchable do |s|
s.integer :id
s.string :title
#fields_to_dynamically_add.each do |f|
s.string f.to_sym
end
end
end
PS: have not added fields to searchable blocks dynamically every myself (although the above works)

Rails. Validations for many locales at the same time

I have a bilingual web site with two locales: en and ru.
I want my site to have i18n. I use 'globalize3' and 'easy_globalize3_accessors' gems.
There are departments I can create and edit with standard forms.
Locales are given from URL: example.com/en/departments/ or example.com/ru/departments/
Now if I want to create a new department item, I would see such a thing:
A main form for current locale (I18n.locale).
A checkbox to add a translation on the same page.
If checkbox is active, show another form for another locale right next to the main form.
The most important thing — validations for each locale must be different. Say, for en it should pass ASCII symbols; for ru — Cyrillic ones.
My problem is number 4. I can't get my validations work with a checkbox.
The main problem is: checkbox active? If yes, show another form and run validations for it. If no, show nothing and don't run validations for that form, pass it empty.
For now, if I fill in two forms, everything works like a charm.
Ok. What I tried.
Model
class Department < ActiveRecord::Base
attr_accessible :name, :translations_attributes
translates :name, fallbacks_for_empty_translations: true
accepts_nested_attributes_for :translations
# The inline class Translation is a hack to solve
# "Can't mass-assign protected attributes: locale"
# See https://github.com/svenfuchs/globalize3/issues/128#issuecomment-11480650
class Translation
attr_accessible :locale, :name
validates :name, uniqueness: true
validates :name, format: {with: /\A[-а-яА-Я -]+\Z/}, if: ->(l) {l.locale.to_s == 'ru'}
validates :name, format: {with: /\A[-a-zA-Z -']+\Z/}, if: ->(l) {l.locale.to_s == 'en'}
end
end
Controller
def new
#department = Department.new
end
def create
#department = Department.new(params[:department])
#department.save ? (redirect_to action: :index) : (render :new)
end
View (new.haml.html) without checkbox
= form_for #department, url: {action: :create} do |f|
%h2
- f.globalize_fields_for_locale I18n.locale do |g|
= "Translation for"
= I18n.locale
= g.label t("department.form.new.label.name")
= g.text_field :name
%hr
%h2
- I18n.available_locales.each do |locale|
- next if locale == I18n.locale
%br
- f.globalize_fields_for_locale locale do |g|
= "Translation for"
= locale
= g.label t("department.form.new.label.name")
= g.text_field :name
= f.submit t("department.create.link"), class: "btn"
Help me understand what I have to do, please.

Rails 3 validates_uniquess_of virtual field with scope

I am working on a Rails 3 app that is needing to run a validation on a virtual field to see if the record already exists... here is my model code:
#mass-assignment
attr_accessible :first_name, :last_name, :dob, :gender
#validations
validates_presence_of :first_name, :last_name, :gender, :dob
validates :fullname, :uniqueness => { :scope => [:dob] }
def fullname
"#{first_name} #{last_name}"
end
I am not getting any errors, its passing the validation.
I don't think you can do something like that with standard validates. Reason is, it usually is a "simple" sql look-up for uniqueness. But to check uniqueness of what's sent back from your method, it'd have to :
Get all entries of your scope
For each, execute the fullname method.
Add every result in an array while testing for uniqueness
Simple when the data set is small, but then it'd just take ages to do as soon as you reach 10K plus matches in your scope.
I'd personally do this with a standard before_save using the dob scope
before_save :check_full_name_uniqueness
def check_full_name_uniqueness
query_id = id || 0
return !Patient.where("first_name = ? and last_name = ? and dob = ? and id != ?", first_name, last_name, dob, query_id).exists?
end
Add error message (before the return):
errors.add(:fullname, 'Your error')
Back in the controller, get the error :
your_object.errors.each do |k, v| #k is the key of the hash, here fullname, and v the error message
#Do whatever you have to do
end

Rails - how to display value through associations in form_for?

This is my view:
= form_for(#user) do |f|
= f.autocomplete_field #user.city.name, autocomplete_city_name_users_path
On the second line I am trying to display the association, but I am getting
undefined method `London' for #<User:0x00000129bb3030>
The associations:
User belongs_to :city
City has_one :user
The displayed result in the error message (London) is right, but why I am gettng that error message?
The argument to f.autocomplete_field should be the name of a method. The form builder will send this method to #user to get the correct value. Since the value you're interested in is not in user but in an object owned by user, you have a few options:
Add city_name and city_name= methods to your User class:
# app/models/user.rb
def city_name
city && city.name
end
def city_name=(name)
city.name = name # You'll want to make sure the user has a city first
end
If you don't know how to make sure you have a city, you could create one lazily by changing your city_name= method to this:
def city_name=(name)
build_city unless city
city.name = name
end
Then your form would look like this:
= form_for(#user) do |f|
= f.autocomplete_field :city_name, autocomplete_city_name_users_path
Or you could treat this as a nested object. Add this to User:
accepts_nested_attributes_for :city
And use fields_for in your form:
= form_for(#user) do |f|
= f.fields_for :city do |city_f|
= city_f.autocomplete_field :name, autocomplete_city_name_users_path

How can I map between strings and attributes automatically?

I have a tiny logical error in my code somewhere and I can't figure out exactly what the problem is. Let's start from the beginning. I have the following extension that my order class uses.
class ActiveRecord::Base
def self.has_statuses(*status_names)
validates :status,
:presence => true,
:inclusion => { :in => status_names}
status_names.each do |status_name|
scope "all_#{status_name}", where(status: status_name)
end
status_names.each do |status_name|
define_method "#{status_name}?" do
status == status_name
end
end
end
end
This works great for the queries and initial setting of "statuses".
require "#{Rails.root}/lib/active_record_extensions"
class Order < ActiveRecord::Base
has_statuses :created, :in_progress, :approved, :rejected, :shipped
after_initialize :init
attr_accessible :store_id, :user_id, :order_reference, :sales_person
private
def init
if new_record?
self.status = :created
end
end
end
Now I set a status initially and that works great. No problems at all and I can save my new order as expected. Updating the order on the other hand is not working. I get a message saying:
"Status is not included in the list"
When I check it seems that order.status == 'created' and it's trying to match against :created. I tried setting the has_statuses 'created', 'in_progress' etc but couldn't get some of the other things to work.
Anyway to automatically map between string/attribute?
from your description, looks like you're comparing a string to a symbol. Probably need to add:
define_method "#{status_name}=" do
self.status = status_name.to_sym
end
or do a #to_s on the status_names