Automatic associations in ruby on rails fixtures - ruby-on-rails

As described in this article, I am using automatic associations in fixtures. For example, if a region object has a country id, instead of doing "country_id": 1, I do "country": "USA". "USA" is a label in my countries.yml file, so fixtures knows how to take care of this. However, this only works when you do not specify an ID value for the countries object. So I cannot assign USA's ID to be 1. But if I do not assign it to be 1, it ends up being some large value 8974343...which is kinda strange. Is there a way to get fixtures to auto-generate id's that are not super high? ....or is this ok?

This is how you get an autogenerated id of the fixture label.
Fixtures.identify(:reginald)

Reading the API documentation, this is exactly how autogenerated fixtures are supposed to behave -- if you want to have a specific ID value for a fixture in advance, you should probably just assign it yourself.
If not, well, from the API docs:
The generated ID for a given label is constant, so we can discover any fixture‘s ID without loading anything, as long as we know the label.

Since I don't have enough reputation to comment, this is the actual Rails 4.1 documentation:
http://edgeapi.rubyonrails.org/classes/ActiveRecord/FixtureSet.html#class-ActiveRecord::FixtureSet-label-Fixture+label+interpolation
Under Fixture label interpolation:
monkey_id: <%= ActiveRecord::FixtureSet.identify(:reginald) %>
pirate_id: <%= ActiveRecord::FixtureSet.identify(:george) %>

The fixture's id comes directly from hashing its name (that's how "we can discover any fixture‘s ID without loading anything, as long as we know the label")

automated test to enforce fixture integrity
class FixtureIntegrityTest < ActiveSupport::TestCase
context "fixture integrity" do
should "work" do
fixtures = Dir["test/fixtures/*.yml"].map do |file|
[file, File.basename(file).sub(/\..*/, "").singularize, YAML.load(ERB.new(File.read(file)).result)]
end
failures = fixtures.reject(&:last).map { |file,*| "#{file} is empty!"}
failures = failures.presence || fixtures.map do |_, klass, content|
content.select{ |_,fixture| fixture["id"] }.map do |name, _|
fixtures.map do |file, _, content|
content.select { |_,fixture| fixture[klass] == name }.map do |_, fixture|
"#{file} uses #{klass}: #{name}, but should use the id!"
end
end
end
end.flatten.compact
assert_equal [], failures
end
end
end

Related

In Rails, what is the cleanest method of getting a variable name preferably in symbol form?

Suppose you have
[started, stopped].each do |action|
# puts "#{action} is :started" # or
# puts "#{action} is :stopped"
end
Getting the original variable name of the current item (action)
I followed this answer https://stackoverflow.com/a/14074652/3577482, but it requires additional helper methods
Easy solution, put them into a Hash and iterate over that:
{
started: started,
stopped: stopped
}.each do |name, value|
puts "name is {name}, value is #{value}"
end
Organizing data into structures is always a good idea, but it's especially important in Ruby since a lot of Ruby's strength comes from being able to manipulate and transform structures into other structures.
[:started, :stopped].each do |key|
action = self.send(key)
puts "name is #{key}, value is #{action}"
...
end
EDIT:
Was looking for this example which returns the variable meta

How to iterate through an array and check that attributes for an object match

I am working on a Ruby script to address users accidentally deleted from the app, which clones a User record and the UserGroups right before deletion.
I'm looping through both objects, and now trying to figure how to assert that the attributes match.
I tried
assert_equal archived_user_groups, deleted_user_groups
but that is asserting the objects not the attributes, so it fails.
I am trying to loop through the array and check that attributes (user_id, group_id) are the same for every object at the same index (0, 1, 2, ...). Is each_with_index suitable for this, and how would it be implemented?
archived_user_groups_test.rb:
def test_user_with_user_groups
user = users(:employee_bb)
deleted_user_groups = user.user_groups.sort_by{ |u| [u.user_id, u.group_id] }
user.archive!
archived_user_groups = ArchivedUserGroup.where(user_id: user.id).sort_by{ |u| [u.user_id, u.group_id] }
deleted_user_groups.each do |ug|
archived_user_groups.find do |attribute_names, attribute_values|
assert_equal archived_user_groups, deleted_user_groups
end
end
end
The include matcher is versatile and can be used for strings, arrays, hashes, etc.
In this case you could iterate over the records and compare their attribute hashes:
archived_user_groups.each_with_index do |archived_user, idx|
expect(archived_user.attributes).to include(deleted_user_groups[idx].attributes)
end
However this test might fail if you have different created_at, updated_at, id, etc. for the archive vs. deleted records. In that case you could be more explicit about which attributes you're testing against. There are many ways to do this but for example:
archived_user_groups.each_with_index do |archived_user, idx|
expect(
archived_user.attributes.values_at("id", "email")
).to match_array(
deleted_user_groups[idx].attributes.values_at("id", "email")
)
end
I admit this seems verbose for what could potentially be a one-liner. This might be a good use-case for writing a custom matcher.

Rails multilinguism stored in DataBase (translation by the end-user)

We are having a dilemna about a feature we need to implement. We want to support n languages in our app (0 < n < +infinity).
We decided to go with the solution below:
module TranslatedAttributes
def use_translated_attributes
has_many :translated_attributes, as: :owner
accepts_nested_attributes_for :translated_attributes
define_method :translate_attribute do |attribute, locale = I18n.locale|
TranslatedAttribute.where(attribute_name: attribute.to_s, owner_type: self.class.model_name, owner_id: self.id, locale: locale.to_s).first.try(:translation)
end
end
end
The User should be able to define X translation(s) for an instance of the Product model for example. Also, depending on the user's locale set in his profile, he will see the translated version of the attribute.
Example:
Product
id: 12
TranslatedAttribute
attribute_name: 'name'
owner_id: 12
owner_type: 'Product'
locale: 'en'
translation: 'Magnificent shiny shoes'
TranslatedAttribute
attribute_name: 'name'
owner_id: 12
owner_type: 'Product'
locale: 'fr'
translation: 'Magnifiques chaussures brillantes'
In the view, it would be called like this:
product.translate_attribute(:name)
# or we will eventually define_method for each attribute to be translated
# so we could use the following
# product.name
This works, already tested.
The problem is when we will try to load tons of records, each one needing to query the DB to know the proper translation to display.
My question is: How would you handle a CACHE about this?
Another question I have is: is there another problem you see that I might not see so far? Also, I thought about accepts_nested_attributes_for :translated_attributes to build the translation's form with fields_for. Do you think it is a bad idea to handle it like this?
Thanks you!
You could use something such as the globalize3 gem to implement this feature.
A first optimization could be to fetch all the translations in the desired locale[1] for a Product instance at the first call to translate_attribute, and cache them in an instance variable.
This way, the number of requests for translations would be reduced to just one by Product instance.
Quick example:
define_method :translate_attribute do |attribute, locale = I18n.locale|
locale = locale.to_s
#attributes_translations_cache ||= {}
#attributes_translations_cache[locale] ||= Hash[
self.translated_attributes
.where(locale: locale)
.map do |translated_attribute|
[translated_attribute.name, translated_attribute.translation]
end
]
#attributes_translations_cache[locale][attribute]
end
I think that it should also be possible to join or at least include the translation to the Products in some way, but I haven't given much thought to this idea yet. I'll try to update this answer.
[1] This assumes that you only use a single locale in a given page, but you could also fetch all the locales at the same time, or any combination of locale and attributes.

Rspec: How can i test an optional field

Currently i am learning with the youtube video Efficient Rails Test Driven Development - by Wolfram Arnold
One exercise is:
A Person object has an optional middle_name.
I create a migration to add the middle name to the database
rake g migration AddMiddleNameToPerson middle_name:string
and i write a spec
it "can have a middle name"
But i got no idea how to test this issue
how can i test an optional field
thanks for help
Bulleric
To say that an attribute is 'optional' implies that the instance is valid when the attribute is nil. So:
it "does not require a middle name" do
#person = Person.new
#person.valid?
#person.errors[:middle_name].should_not include("can't be blank")
end
If you're using shoulda then this can be made even simpler:
describe Person do
it { should_not validate_presence_of(:middle_name) }
end
Assuming you are using Factor_girl, create two factories,
person1 = Factory.create(:person, :first_name=> "A")
person2 = Factory.create(:person, :first_name=> "A", :middle_name => "M")
Now, in your test, show that person1 and person2 both get saved to the database.
You could do the same with fixtures, if you are not using factory_girl.

Which is the best way to test if a model instance is "empty" in Ruby on Rails?

I want to implement a method that checks if a model's instance has only nil or empty attributes, except from its id or timestamps.
I've made use of an auxiliary method that removes a key from Hash and return the remaining hash ( question 6227600)
class ActiveRecord::Base
def blank?
self.attributes.remove("id","created_at","updated_at").reject{|attr| self[attr].blank?}.empty?
end
end
I guess that there may be much simpler, efficient or safer way to do this. Any suggestion?
def blank?
self.attributes.all?{|k,v| v.blank? || %w(id created_at updated_at).include?(k)}
end
My response is almost the same that tadman gave, but expressed in a more concise way.
Be careful with two situations:
- **blank?** is not a good choice as name, since if you call **object_a.object_b.blank?** trying to know if there is or not a object_b inside object_a, you'll get true event if the object exists. **empty?** seems a better name
- If databases sets defaults values, it can be tricky.
EDIT: Since build an array every iteration is slow (thanks tadman), a beter solution is:
def empty?
ignored_attrs = {'id' => 1, 'created_at' => 1, 'updated_at' => 1}
self.attributes.all?{|k,v| v.blank? || ignored_attrs[k]}
end
You could just check that all the properties in the attributes hash are not present, or the converse:
class ActiveRecord::Base
def blank?
!self.attributes.find do |key, value|
case (key)
when 'id', 'created_at', 'updated_at'
false
else
value.present?
end
end
end
end
Unfortunately this will not account for things that are set with a default in your database, if any relationship keys are assigned, among other things. You will have to add those as exceptions, or compare the values to a known default state of some sort.
This sort of thing is probably best implemented on a case by case basis.

Resources