Is it possible to ask Factory Girl what associations a given factory has? - ruby

Factory Girl is incredibly useful for functional testing, but has one annoying property that makes it slightly harder to use in unit tests, where I don't want to rely on the test database. I often use Factory.build to create a factory that I can then pass around or assign to an ActiveRecord.find call using flexmock:
require 'test_helper'
require 'flexmock'
class SomeMixinTest < ActiveSupport::TestCase
include FlexMock::TestCase
def setup
#foo = Factory.build(:foo, :id => 123,
:bar => Factory.build(:bar, :id => 456,
:baz => Factory.build(:baz, :id => 789)
)
)
flexmock Foo, :find => #foo
end
def test_find_by_reverse_id
assert_equal #foo, Foo.find_by_reverse_id(321)
end
end
This pattern is very nice, since it cares not about the presence of the database, and runs much faster than if the objects had to actually be persisted. However, it is a bit annoying to have to build the associated objects manually. If you don't, the associated objects are actually created in the database by the build call, as if you had used create instead.
assert_equal [], Foo.all
foo = Factory.build :foo # build my associations too, please
assert_equal [], Foo.all # look Ma, no mocks!
assert_equal [], Bar.all # <=== ASSERTION FAILED
assert_equal [], Baz.all
This is non-intuitive to say the least, and causes an actual problem when I'm testing a few classes that need to play nicely with a mixin. I want to be able to do this:
KLASSES_UNDER_TEST = [Foo, Bar, Baz]
def test_find_by_reverse_id
KLASSES_UNDER_TEST.each do |klass|
objects = (123..456).map do |id|
Factory.build klass.to_s.downcase.to_sym, :id => id
end
flexmock klass, :all => objects
objects.each do |object|
assert_equal object, klass.find_by_reverse_id(object.id.to_s.reverse), "#{klass} #{object.id}"
end
end
But this has the nasty side effect of creating 333 Bars and 666 Bazes ("Baz" does sound kind of like a demon's nickname, so maybe that's fitting) in the database, making this test slower than molasses flowing uphill in the winter.
I'd like to create a helper method like this:
def setup_mocks(klass)
klass_sym = klass.to_s.downcase.to_sym
objects = (123..456).map{|id|
associated_objects = Hash[
Factory.associations(klass_sym).map do |association|
[ association, setup_mocks(association, 1) ]
end
]
Factory.build klass_sym, associated_objects
end
flexmock klass, :all => objects
objects
end
So, does anything like Factory.associations exist?

I've not tested this, but looking at the source it seems that something like this should work:
FactoryGirl.find(:factory_name).associations

Related

Ruby: many related objects with similiar method naming pattern. How to map them to standardized methods

I'm working on a program that receives responses from an API that represent 'songs' from a database. Those responses arrive in my program as Struct objects, and they are structured slightly differently depending on which table they were pulled from.
For instance, the song object pulled from the 'track' table looks like:
song_1 = <struct Song track_artist="Michael Jackson", track_title="Billie Jean">
And the song object returned from the 'license' table looks like:
song_2 = <struct Song license_artist="Michael Jackson", license_title="Billie Jean">
If I want to get the 'artist' from song_1, I'd call song_1.track_artist, and with song_2, song_2.license_artist. But this is problematic when running loops. I want to be able to call song.title on any of them and receive the title.
Right now, I'm putting each Struct through a 'Normalizer' object when I receive it. It uses a hash mapping to change the method name of each Struct; the mapping more or less looks like:
{ track_artist: artist,
track_title: title,
license_artist: artist,
license_title: title }
This seems like it might be overkill. What's the best way to go about this?
You could use method_missing for this
module Unifier
def method_missing(name, *args, &block)
meth = public_methods.find { |m| m[/_#{name}/] }
meth ? send(meth, *args, *block) : super
end
def respond_to_missing?(method_name, include_private = false)
public_methods.any? { |m| m[/_#{method_name}/] } || super
end
end
class A
include Unifier
attr_reader :artist_name
def initialize
#artist_name = 123
end
end
a = A.new
a.respond_to?(:name) # => true
a.name # => 123
a.respond_to?(:title) # => false
a.title # => undefined method `title' for #<A:0x007fb3f4054330 #artist_name=123> (NoMethodError)
Update
For you case it will be more complex and tricky.
If you can make changes to place, where this Struct objects are created, then just patch classes, generated from Struct
song_1_class = Struct.new(:track_artist, :track_title) do
include Unifier
end
song_1 = song_1_class.new('Michael Jackson', 'Billie Jean')
puts "#{song_1.artist} - #{song_1.title}"
# => Michael Jackson - Billie Jean
If you can work only with objects of that classes - you could patch it dynamically
# We get objects of licence_struct class
licence_struct = Struct.new(:license_artist, :license_title)
song_2 = licence_struct.new('Michael Jackson', 'Billie Jean')
song_3 = licence_struct.new('Michael Jackson', 'Black of White')
def process_song(song)
puts "Song #{song} patched - #{song.respond_to?(:artist)}"
"#{song.artist} - #{song.title}"
rescue NoMethodError => err
# If we don't have methods on our struct - patch it
# If after patching object still dont respond to our method - throw exception
patch_object_from_error(err) ? retry : raise(err)
end
def patch_object_from_error(error)
receiver = error.receiver
receiver.class.class_exec { include Unifier }
meth = error.message.match(/undefined method `(\S+)'/)[1].to_sym
receiver.respond_to?(meth)
end
puts process_song(song_2)
# => Song #<struct license_artist="Michael Jackson", license_title="Billie Jean"> patched - false
# after retry
# => Song #<struct license_artist="Michael Jackson", license_title="Billie Jean"> patched - true
# => Michael Jackson - Billie Jean
puts process_song(song_3)
# dont need retry - class already patched
# => Song #<struct license_artist="Michael Jackson", license_title="Black of White"> patched - true
# => Michael Jackson - Black of White

Ruby Motion: Removing a Ruby Class Completely from Object Space

I'm running into a brick wall testing class redefinitions and just don't know how to approach it. Here's the scenario I'm testing (this is not Core Data):
Application is run with a model in version 1
Eager programmer modifies model by adding/removing/redefining columns
Application is run with the model in version 2
Where I'm running into problems is in simulating the actual removal of the application from memory and rebuilding of it from scratch. This is important because a number of model-specific things are set up when the MotionModel::Model module is included, and that only happens once: When the module is included in the class. Here is what I felt might work:
it "column removal" do
class Removeable
include MotionModel::Model
columns :name => :string, :desc => :string
end
#foo = Removeable.create(:name=> 'Bob', :desc => 'who cares anyway?')
Removeable.serialize_to_file('test.dat')
#foo.should.respond_to :desc
Object.send(:remove_const, :Removeable) # Should remove all traces of Removeable
class Removeable
include MotionModel::model # Should include this again instead
columns :name => :string, # of just reopening the old Removeable
:address => :string # class
end
Removeable.deserialize_from_file # Deserialize old data into new model
Removeable.length.should == 1
#bar = Removeable.first
#bar.should.respond_to :name
#bar.should.respond_to :address
#bar.should.not.respond_to :desc
#bar.name.should == 'Bob'
#bar.address.should == nil
end
end
Unfortunately, Object.send(:remove_const, :Removeable) does not do what I'd hoped it would, and Ruby just thinks it can reopen Removeable and not run the self.included() method of the MotionModel::Model module.
Any ideas on how to emulate creation of this class from scratch in the context of a spec example?
I'd try working with anonymous classes (you'd have to tell MotionModel the table name).
Fictional example:
model_before_update = Class.new do
# This tells MotionModel the name of the class (don't know if that actually exists)
table_name "SomeTable"
include MotionModel::Model
columns :name => :string, :desc => :string
end
You do not remove the class at all, you just define another (anonymous) class with the same table name.
model_after_update = Class.new do
table_name "SomeTable"
include MotionModel::model
columns :name => :string,
:address => :string
end
Thinking of it, if there is a table_name setter like above, you don't even need to use anonymous classes, in case that does not work with RubyMotion.

Ruby structure for extendable handler/plugin architechture

I'm writing something that is a bit like Facebook's shared link preview.
I would like to make it easily extendable for new sites by just dropping in a new file for each new site I want to write a custom parser for. I have the basic idea of the design pattern figured out but don't have enough experience with modules to nail the details. I'm sure there are plenty of examples of something like this in other projects.
The result should be something like this:
> require 'link'
=> true
> Link.new('http://youtube.com/foo').preview
=> {:title => 'Xxx', :description => 'Yyy', :embed => '<zzz/>' }
> Link.new('http://stackoverflow.com/bar').preview
=> {:title => 'Xyz', :description => 'Zyx' }
And the code would be something like this:
#parsers/youtube.rb
module YoutubeParser
url_match /(youtube\.com)|(youtu.be)\//
def preview
get_stuff_using youtube_api
end
end
#parsers/stackoverflow.rb
module SOFParser
url_match /stachoverflow.com\//
def preview
get_stuff
end
end
#link.rb
class Link
def initialize(url)
extend self with the module that has matching regexp
end
end
# url_processor.rb
class UrlProcessor
# registers url handler for given pattern
def self.register_url pattern, &block
#patterns ||= {}
#patterns[pattern] = block
end
def self.process_url url
_, handler = #patterns.find{|p, _| url =~ p}
if handler
handler.call(url)
else
{}
end
end
end
# plugins/so_plugin.rb
class SOPlugin
UrlProcessor.register_url /stackoverflow\.com/ do |url|
{:title => 'foo', :description => 'bar'}
end
end
# plugins/youtube_plugin.rb
class YoutubePlugin
UrlProcessor.register_url /youtube\.com/ do |url|
{:title => 'baz', :description => 'boo'}
end
end
p UrlProcessor.process_url 'http://www.stackoverflow.com/1234'
#=>{:title=>"foo", :description=>"bar"}
p UrlProcessor.process_url 'http://www.youtube.com/1234'
#=>{:title=>"baz", :description=>"boo"}
p UrlProcessor.process_url 'http://www.foobar.com/1234'
#=>{}
You just need to require every .rb from plugins directory.
If you're willing to take this approach you should probably scan the filed for the mathing string and then include the right one.
In the same situation I attempted a different approach. I'm extending the module with new methods, ##registering them so that I won't register two identically named methods. So far it works good, though the project I started is nowhere near leaving the specific domain of one tangled mess of a particular web-site.
This is the main file.
module Onigiri
extend self
##registry ||= {}
class OnigiriHandlerTaken < StandardError
def description
"There was an attempt to override registered handler. This usually indicates a bug in Onigiri."
end
end
def clean(data, *params)
dupe = Onigiri::Document.parse data
params.flatten.each do |method|
dupe = dupe.send(method) if ##registry[method]
end
dupe.to_html
end
class Document < Nokogiri::HTML::DocumentFragment
end
private
def register_handler(name)
unless ##registry[name]
##registry[name] = true
else
raise OnigiriHandlerTaken
end
end
end
And here's the extending file.
# encoding: utf-8
module Onigiri
register_handler :fix_backslash
class Document
def fix_backslash
dupe = dup
attrset = ['src', 'longdesc', 'href', 'action']
dupe.css("[#{attrset.join('], [')}]").each do |target|
attrset.each do |attr|
target[attr] = target[attr].gsub("\\", "/") if target[attr]
end
end
dupe
end
end
end
Another way I see is to use a set of different (but behaviorally indistinguishable) classes with a simple decision making mechanism to call a right one. A simple hash that holds class names and corresponding url_matcher would probably suffice.
Hope this helps.

How to retrieve a class name?

I am using Ruby on Rails 3.0.7 and I would like to retrieve the class name, also if it is namespaced. For example, if I have a class named User::Profile::Manager I would retrieve the Manager string from that using some unknown to me Ruby or Ruby on Rails method and in a secure way.
BTW: What other "usefull" information that are "commonly" used can I get for the class?
Some useful simple metaprogramming calls:
user = User::Profile::Manager.new(some_params)
user.class # => User::Profile::Manager
user.class.class # => Class
user.class.name # => "User::Profile::Manager"
user.class.name.class # => String
# respond_to? lets you know if you can call a method on an object or if the method you specify is undefined
user.respond_to?(:class) # => true
user.respond_to?(:authenticate!) # => Might be true depending on your authentication solution
user.respond_to?(:herpderp) # => false (unless you're the best programmer ever)
# class.ancestors is an array of the class names of the inheritance chain for an object
# In rails 3.1 it yields this for strings:
"string".class.ancestors.each{|anc| puts anc}
String
JSON::Ext::Generator::GeneratorMethods::String
Comparable
Object
PP::ObjectMixin
JSON::Ext::Generator::GeneratorMethods::Object
ActiveSupport::Dependencies::Loadable
Kernel
BasicObject
If you want the lowest-level class from User::Profile::Manager I'd probably do the following [using a regex for this seems like overkill to me ;)]:
user = User::Profile::Manager.new
class_as_string = user.class.name.split('::').last # => "Manager"
class_as_class = class_name.constantize # => Manager
Edit:
If you actually want to look through some more metaprogramming calls, check the docs for the Object and Module classes, and check out the google results for "Ruby Metaprogramming".
Have you tried class method:
class A
class B
end
end
myobject = A::B.new
myobject.class
=> A::B
To expand on #JCorcuera's answer, some other useful information can be found with kind_of? and methods
class A
class B
def foo
end
end
end
myobject = A::B.new
p myobject.class
=> A::B
p myobject.kind_of? A::B
=> true
p myobject.methods
=> [:foo, :nil?, :===, :=~, ...
p myobject.methods.include? :foo
=> true

Is there a way to check if a record was built by another model in active record?

When using accepts_nested_attributes_for, I got stuck when having a validation which required the original to be present. The code will help clear up that sentence.
class Foo < ActiveRecord::Base
has_one :bar
accepts_nested_attributes :bar
end
class Bar < ActiveRecord::Base
#property name: string
belongs_to :foo
validates_presence_of :foo #trouble line!
end
#now when you do
foo = Foo.create! :bar_attributes => {:name => 'steve'}
#you get an error because the bar validation failed
I would like to write a validation that goes something like...
class Bar < ActiveRecord::Base
validates_presence_of :foo, :unless => :being_built_by_foo?
end
I am currently using rails3.beta4
Thank you
Alas I don't have an answer to this post, but the I came up with another way so I didn't need the validation.
Since bar should never be without a foo then any request to create a bar without a foo_id is an error. In the real example a foo is a project, and bar is a bid. It is a nested resource, but I wanted to give access to json apps to be able to query the info from the /bids location so the router looked like.
resources :bids
resources :projects do
resources: bids
end
and then I just had to make sure all html access used project_bids_path or form_for [:project,#bid] etc. This next part is largely untested but so far the desired behavior is there. I got the idea from Yehuda's post on generic actions http://yehudakatz.com/2009/12/20/generic-actions-in-rails-3/
#I'm sure there is a better way then map.connect
map.connect "projects/invalid_id", :controller => "projects", :action => "invalid_id"
resources :projects
resources :bids
end
#couple of changes from Yehuda
def redirect(*args, &block)
options = args.last.is_a?(Hash) ? args.pop : {}
path = args.shift || block
path_proc = path.is_a?(Proc) ? path : proc {|params| path % params }
status = options[:status] || 301
lambda do |env|
req = Rack::Request.new(env)
#Get both the query paramaters and url paramaters
params = env["action_dispatch.request.path_parameters"].merge req.params
url = path_proc.call(params.stringify_keys)
#Doesn't add the port back in!
#url = req.scheme + '://' + req.host + params
#content-type might be a bad idea, need to look into what happens for different requests
[status, {'Location' => url, 'Content-Type' => env['HTTP_ACCEPT'].split(',').first}, ['Moved Permanently']]
end
end
def bid_path
redirect do |params|
if params['project_id']
"/projects/#{params['project_id']}/bids/#{params['id']}"
else
'/projects/invalid_id'
end
end
end
match "bids", :to => bid_path
match "bids/:id", :to => bid_path
however, after doing all of this I most definitely don't think it worth it. I think nested_attributes breaks things and can be improved if that validation doesn't work, but after looking through the code for a little while I'm not sure exactly how to fix it or if it's worth it.
first of all, when using nested_attributes, you'll get the presence of the container. in the example: when you save Foo and there's also a nested form for Bar, then Bar is built by Foo.
I think there's no need to make this kind of validation if you're sure to use Bar only in contexts with Foo.
btw, try to write validation as follow (new preferred syntax for Rails3):
validates :foo, :presence => true
hope this helps,
a.

Resources