Creating multiple different factories using the same create method - ruby-on-rails

So I have different factories: factory_1, factory_2
They have different traits trait_1_factory_1, trait_2_factory_1 etc
Is possbile to create factory_1 and factory_2 with specific trairs from the same command, like:
FactoryBot.create(
:factory_1, traits_1_factory_1,
:factory_2,
:factory_2, traits_1_factory2
)
I tried different combinations of () {} [] but doesn't seem to work for me? Is this possbile?
Is this the only solution
Factory.create(:factory_1, traits_1_factory_1)
Factory.create etc.
Thank you

No, it is not possbile to create factory_1 and factory_2 from the same command.
Here, 'factory1' is the Model or class whose objects are created.
FactoryBot.create(:factory1)
If you are doing it for making spec DRY, then rather than doing as above, you can do as below :
[
[:factory_1, traits_1_factory_1],
[:factory_2, traits_1_factory2]
].each do |factory|
FactoryBot.create(*factory)
end
Store multiple factories in array and iterate it.

I believe you want to create objects using different factories here i.e. when you say Factory.create, you mean FactoryBot.create.
Unfortunately, i couldn't find any direct command to accomplish such thing. Here is something using a Hash and a loop.
factories_and_traits = { factory_1: [trait_1_factory_1, trait_2_factory_1],
factory_2: [trait_1_factory_2] }
factories_and_traits.each do |factory, traits|
FactoryBot.create(factory, *traits)
end

Related

Check if a ruby Class is in a particular Module

I have a controller object with controller.class == Admin::TeamsController. I might also have a circumstance like controller.class == Admin::UsersController. Now I want to check if this is true:
controller.class.to_s.match?('Admin::')
I.e., I want to know: Is this object of a class that's defined within the Admin module namespace? To spell that out, is the structure like the following?
module Admin
module SomeOtherModulePerhaps
class TeamsController
end
end
end
My question: Is there a nicer Ruby way to test for this? It feels kind of hacky to convert the class to a string, then do a regex match like that.
EDIT:
For my constrained use case, I could check like this:
controller.class.to_s.split('::').first == 'Admin'
But that doesn't quite solve the general case that other people might have. For example, there might be cases like XyzAdmin::TeamsController that one might want to exclude, on which my first solution fails, or Foo::Admin::TeamsController that one might want to include, on which my second solution fails.
I'd like to find a better way.
Rails comes with module_parents:
module Admin
module SomeOtherModulePerhaps
class TeamsController
end
end
end
controller = Admin::SomeOtherModulePerhaps::TeamsController.new
controller.class.module_parents
#=> [Admin::SomeOtherModulePerhaps, Admin, Object]
controller.class.module_parents.include?(Admin)
#=> true
Under the hood, it uses Module#name, i.e. "Admin::SomeOtherModulePerhaps::TeamsController".
How about
controller.class.const_defined?(:Admin)
returns true or false
What about to use controller_path
https://api.rubyonrails.org/classes/AbstractController/Base.html#method-c-controller_path
controller_path.match?('admin')
You might try playing with Module#nesting, but it’d return rather unexpected results depending on whether the class was defined using fully qualified name or a set of nesting statements.
After all, class names in ruby are simple constants, and one might define the class name in many ways, like:
module A
def self.class!
Class.new do |c|
define_method :test do puts c.name end
end
end
end
A.const_set :C, A.class!
#⇒ A::C
A::C.new.test
#⇒ A::C
Which roughly means, there are tons of ways to fool the best detection mechanism. That said, I’d go with the easiest one.
controller.class.to_s.split('::')[0...-1].include?('Admin')
Any occurrence of Admin would be counted, save for when Admin is the last item in the class name chain.
I want to know: Is this object of a class that's defined within the Admin module namespace?
[...]
Is there a nicer Ruby way to test for this?
Classes aren't defined in modules, therefore, there is neither a nice way nor any other way to test for it.
When you write a class definition body inside a module definition body, you do not create any relationship whatsoever between the module and the class. The only relationship is between the constant that the class gets assigned to and the module, not the class.
Therefore, since this relationship does not exist, you cannot test for it.

How do I generate all new records for a given model randomly using pre-defined options?

I'd like one of my models, Stones, to be generated at random using pre-defined options I've stored in a set of arrays and hashes. Instead of Create using params from the URL, I'd like new Stones to always be defined using this random generation process. I don't need any user input at all, except that each stone belongs to a given player.
I'm still new to rails; where should I put all this code? I know enough to be able to define the arrays and hashes and randomly select from them when I need to, but I'm not sure where and how to replace the part of the code that draws params from URLs and fills in a new record before it is saved. I know controllers are supposed to be skinny, so do I do this in the model?
Apologies if this is a duplicate. I searched extensively and couldn't find an applicable solution.
Thanks for any help!
I would create a service for this. Something like:
# app/services/stone_creator.rb
class RandomStoneCreator
RANDOM_FOOS = ['bar', 'baz', 'bat']
def self.call(user)
Stone.create!({
foo: RANDOM_FOOS.sample,
user: user
})
end
end
And then anywhere that you need a new random stone you can call it like:
random_stone = RandomStoneCreator.call(current_user)

When and how is it useful to dynamically add a method to a Ruby object?

I have been trying to learn Ruby from 'The Well-grounded Rubyist' and I came across the idea of adding methods to an object at run-time:
obj = Object.new
obj.respond_to? "hello" # Returns false
def obj.hello
puts "something"
end
obj.respond_to? "hello" # Returns true
obj.hello() # Output is "something"
I have a background in Python and Java, and I cannot imagine any way for me to use this new idea. So, how is this useful? How does it fit into the spirit of object-oriented programming? Is it expensive to do this at run-time?
There's always a long list of things you can do in any language but shouldn't do without a good reason and extending a single object is certainly high on that list.
Normally you wouldn't define individual methods, but you might include a bunch of them:
module Extensions
def is_special?
true
end
end
obj = Object.new
obj.send(:extend, Extensions)
obj.is_special?
# => true
ActiveRecord from Rails does this to dynamically create methods for models based on whatever the schema is at the time the Rails instance is launched, so each column gets an associated method. This sort of dynamic programming can be used to make the code adapt seamlessly to a changing environment.
There's a lot of cases where you'll want to spell this out explicitly so your methods are well documented, but for cases where it doesn't matter and responding dynamically is better than maintaining two things, like schema and the associated methods in your code, then it could be the best option.

How to write Rspec spec for the following query

Hi I have the following query in my controller and I want to write the Rspec spec . I am new to Rspec and I don't know how to write the spec. Kindly help
table1.includes(:table2).where(table1: {id: params[:id]}).includes(:table3)
I also tried looking into mocks and stubs but i don't understand how to use them for a query like this.
Thanks
When faced with these issues, I tend to encapsulate the query in a method. That way, you can stub out the method with data simply and without worrying about data-sanitation.
For example:
def fetch_table1_results(id)
table1.includes(:table2).where(table1: {id: id}).includes(:table3)
end
At this point, you can stub out the method when you need to test things that depend on it:
awesome_model = stub_model(Table1, fetch_table1_results: [1, 2, 'etc']) # You should include models, stubs, or mocks here.
As far as testing the actual method, I'm not sure you need to. There aren't many interesting parts of that method chain. If you wanted to be complete, here are the cases:
Ensure fetch_table1_results calls any instance of Table1.find with id
Ensure fetch_table1_results eager-loads table2 and table3
The way of doing the latter varies, but I'm rather fond (and this won't be a popular opinion) of checking the database query directly. So you could type something like the following:
fetch_table1_results(1).to_sql.should include('JOIN table2')
That, or something similar. I should also note that these tests should be in the model, not the controller.

rails/activerecord search eager loaded associations

I have a simple find statement as such:
m = MyModel.find(1, :include => :my_children)
With m.mychildren being an Array; is there anyway to find a particular record from within the array without having to iterate over the entire thing. If I do mychildren.find(1), a new DB query is issues, which doesn't make sense, since they are all loaded already
It looks like there's a little Rails magic going on here. Where Enumerable#find is being overridden by ActiveRecord::Base#find on methods created for associations.
On the upside Enumerable#find is aliased to Enumerable#detect.
Unfortunately Enumerable#find/Enumerable#detect have significantly different syntax from ActiveRecord::Base#find.
So you can't just do mychildren.find(1), instead you've got to do mychildren.detect{|c| c.id == 1} if you want to avoid hitting the database again. You may also want to consider extending Array for a more DRY way of doing this.
class Array
def id_find id
self.detect{|element| element.id == id}
end
end
I'm not quite sure what your asking, but have you tried select:
m.mychildren.select{ |child| child == <<some_statement>> }
This won't hit the database assuming you've used the :include option as you stated in your question.
Alternatively, if you know the number of the child you want, you should be able to just use
m.mychildren[1]

Resources