How do I call a static function with model scopes in Rails? - ruby-on-rails

I wasn't sure how to get it to work syntax wise. I did get it to work like the following though:
scope :out_of_stock, lambda{ |company| get_inventory(company, 'out_of_stock') }
scope :in_stock, lambda{ |company| get_inventory(company, 'in_stock') }
def self.get_inventory(company, stock_status)
StockInventory.find_all_by_....
end
However, is there a way to do it without the anonymous functions?

First of all, there are no static functions in Ruby. The self.get_inventory is a class method, which is in effect a singleton instance method on the Company class. The scope call is itself a class method that dynamically defines other class methods, in this case out_of_stock and in_stock.
Second, it's hard to tell what you're trying to do, since you didn't include your entire method, but I'll assume you're trying to get the instances of StockInventory that have a particular company id and inventory status.
ActiveRecord allows you to use finders, as it appears you've done here, or relations, which are a bit more flexible, as they are typically chainable, and because their execution is delayed until absolutely necessary, meaning you can pass them around to different methods without hitting the database. Unless I'm looking up a single object with a known id, I usually stick to relations for this reason.
I'm going to make a few assumptions, since your question isn't particularly clear. If you have defined a relation between Company and StockInventory (like has_many :stock_inventories and belongs_to :company), you can use that relation as a starting point and defined a method like this on the StockInventory class:
def self.in_stock
where(stock_status: "in stock") # or whatever
end
Note that instead of passing in a Company instance as an argument to a Company class method that ultimately loads some other class (this should be a red flag), you call this directly on the StockInventory class or any StockInventory relation. Plus, since it's a relation, you can chain other ActiveRecord methods to it, i.e. my_company.stock_inventories.in_stock.limit(10).
You'll need to alter this method to fit your particular database columns and statuses, but this is as complicated as it needs to get.
Whatever the case, I recommend you read the Active Record Query Interface guide - there's a lot you can do with it once you understand how it works.

Related

How is ActiveRecord scope accessible through object from Rails console?

I am in a dilemma right now regarding ActiveRecord scope. AFAIU, scope inside model are converted to a class method so they can be invoked only by class not by object.
Say for example if I have a User belongs to Company association and Company has a scope like this:
scope :blacklisted_companies, -> { ...some code here...}
I can only call blacklisted_companies like Company.blacklisted_companies, right?
However, I am able to access it through the associated object from Rails console like this:
user.company.blacklisted_companies.
Can someone explain how is this possible?
EDIT: Clarification as per suggestions, I intend to ask why does it (user.company.blacklisted_companies) work in console and not in real code
Reaching through associations in rails makes an ActiveRecord Relation which is a valid target for scopes.
For example: user.company from Rails' PoV is equivalent to Company.where(user: user), especially since it's all lazy evaluated.
That's equivalent to a class method, therefore you can call scopes on it.
Edit: OP Changed the question, ignore this answer.

Force reload another model's methods in rails?

I have a model that defines methods based off of the entries in another model's table: eg Article and Type. An article habtm types and vice versa.
I define in Article.rb:
Type.all.each do |type|
define_method "#{type.name}?" do
is?(:"#{type.name}")
end
end
This works great! it allows me to ensure that any types in the type db result in the methods associated being created, such as:
article.type?
However, these methods only run when you load the Article model. This introduces certain caveats: for example, in Rails Console, if I create a new Type, its method article.type_name? won't be defined until I reload! everything.
Additionally, the same problem exists in test/rspec: if I create a certain number of types, their associated methods won't exist yet. And in rspec, I don't know how to reload the User model.
Does anyone know a solution here? Perhaps, is there some way to, on creation of a new Type, to reload the Article model's methods? This sounds unlikely.. Any advice or guidance would be great!
I think you'll be better off avoiding reloading the model and changing your api a bit. In Article, are you really opposed to a single point of access through a more generic method?
def type?(type)
return is? type if type.is_a? String # for when type is the Type name already
is? type.name # for when an instance of Type is passed
end
If you're set on having separate methods for each type, perhaps something like this would work in your Type class
after_insert do
block = eval <<-END.gsub(/^ {6}/, '')
Proc.new { is? :#{self.name} }
END
Article.send(:define_method, "#{self.name}?", block)
end

Should model methods always return models of their own type?

For model methods that return models or collections of models, should these methods return only models of type corresponding to the class they live in?
For instance, if I have a Theater model, should its methods always return models of type Theater, or would it ever make sense to return models of type Showtime (eg. Theater.get_showtimes(theater_id) vs. Showtimes.get_by_theater_id(theater_id)).
I asked myself this question just about every time I run into situations just like yours. I personally try to stick to a model returning its own type. So in that situation I'd go with Showtimes.get_by_theater_id(theater_id). It just makes more sense when I look at the code later.
If your Theater has_many Showtimes, then you could access it by Theater.Showtimes.
A couple of more ActiveRecord-style ways of implementing your example (Theater.get_showtimes(theater_id)) would be:
Theater.find(theater_id).showtimes instead. This assumes you've used ActiveRecord associations as zsalzbank mentions.
Showtimes.find_by_theater_id(theater_id)
I'd suggest you use the power of ActiveRecord for what it does best, and not worry about artificially restricting yourself to what class methods may return.
Now, if we're talking purely theory, I'd argue for following the conventions of the language and/or framework you're using.
Looking at ActiveRecord::Base (http://apidock.com/rails/ActiveRecord/Base) it is trivial to find a bunch of class methods that return something not ActiveRecord::Base. For example, ActiveRecord::Base.connection returns a subclass of ActiveRecord::ConnectionAdapters::AbstractAdapter and ActiveRecord::Base.table_name returns a String. Obviously, your model has those methods as well, thus it returns those values.
I don't see any benefit to putting the proposed restriction on yourself/your code - especially since you've already broken the rule by inheriting from ActiveRecord::Base and will have to deal with different return classes anyways.
Short answer: no.
A model is simply a ruby class file that represents some table in a database and is really only used in the ruby-on-rails world. A model doesn't return anything, it's just a container. A model has methods/functions that return data and by no means do they have to return only the same model object.
For example (pseudo code):
class Dog < AR:B
def hack_the_world
//do hacking and return array of passwords
//...
end
end
We have a Dog model here. For example we get these methods from rails which return Dog objects:
dogs=Dog.all
dog=Dog.find(1)
dog=Dog.where(:name => 'fido')
But then we can call our method that doesn't return any Dog objects
passwords=Dog.new.hack_the_world

What role do ActiveRecord model constructors have in Rails (if any)?

I've just been reading this question which is about giving an ActiveRecord model's date field a default value. The accepted answer shows how to set the default value from within the controller. To my mind, this sort of business logic really belongs in the model itself.
Then I got to thinking how if this were Java I'd probably set the initial field value when declaring the instance variable or within the constructor. Since database-backed fields don't have to be explicitly declared within ActiveRecord models, is this something that you could use the model's initialize method for? I'm curious because I've not really seen much use of constructors for ActiveRecord models within the Rails code that I've looked at. Do they have a role to play and if so, what is it?
I do this quite often actually for default values. It works well and still lets the user change it. Remember, the initialize method is called when you say MyObject.new. However, you may want to read this blog entry (albeit a bit outdated) about using initialize.
You should use after_initialize instead of initialize. The initialize method is required by ActiveRecord::Base to prepare many of the convenience methods. If an after_initialize method is defined in your model it gets called as a callback to new, create, find and any other methods that generate instances of your model.
Ideally you'd want to define it like this:
def after_initialize
#attribute ||= default_value
end
Also note, you cannot use this callback like the others, you must define a method named after_initialize (like above) for it to work. You can't do the following:
after_initialize :run_some_other_method
#TopherFangio's answer is correct. It seems that the ActiveRecord API changed some time between his answer (2009) and now (2015).
As of today (Rails 4 with ActiveRecord 4.2.0), here's how you add initializers according to the ActiveRecord docs:
class Widget < ActiveRecord::Base
after_initialize |new_widget|
new_widget.name ||= 'Unnamed Widget'
end
end
You can verify with puts statements or by inspecting the new object from rails console that it actually initializes correctly.
According to this blog, active record doesn't always use new, so initialize might not be called on your object.

Rails Single Table Inheritance - What is the best way to explicitly set type?

I am using single table inheritance in my rails application, and want to explicitly set the type of an instance.
I have the following;
class Event < ActiveRecord::Base
class SpecialEvent < Event
which is implemented through single table inheritance.
SpecialEvent.new works as expected, but I want to be able to do things like
Event.new(:type => 'SpecialEvent')
So I can create different sub_types easily in the application.
However this doesn't work and seems to set :type to nil, not the value I set it to; I suspect this is because by calling Event.new it is overwriting the :type argument.
Has anyone got a good way of doing this?
If you're trying to dynamically instantiate a subtype, and you have the type as a string, you can do this:
'SpecialEvent'.constantize.new()
from "Pragmatic - Agile Web Development with rails 3rd edition", page 380
There’s also a less obvious constraint (with STI). The attribute type
is also the name of a built-in Ruby method, so accessing it directly
to set or change the type of a row may result in strange Ruby
messages. Instead, access it implicitly by creating objects of the
appropriate class, or access it via the model object’s indexing
interface, using something such as this:
person[:type] = 'Manager'
man, this book really rocks
No, I want to create instances of
sub-types, where I want to
programmatically determine which
sub_type they are
– HermanD
You could use a factory pattern, although I have heard recently that people frown on the overuse of this pattern. Basically, use the factory to create the actual types you want to get
class EventFactory
def EventFactory.create_event(event_type)
event_type.constantize.new()
end
end
To me it sounds like you'll need some mojo in the event#create action:
type = params[:event].delete(:type)
# check that it is an expected value!!!
die unless ['Event', 'SpecialEvent'].include(type)
type.constantize.new(params[:event])
Apparently, Rails does not allow you to set Type directly. Here's what I do...
klass_name = 'Foo'
...
klass = Class.const_get(klass_name)
klass.new # Foo.new
I believe .constantize is a Rails inflector shortcut. const_get is a Ruby method on Class and Module.
Up front I'll agree that STI is often NOT the best way to deal with things. Polymorphism, yes, but it's often better to use a polymorphic association than STI.
That said, I had a system in which STI was important. It was a judicial system and things like court cases were remarkably similar across their types and generally shared all their essential attributes. However, a civil case and a criminal case differed in the elements they managed. This happened at several levels in the system so abstracted my solution.
https://github.com/arvanasse/sti_factory
Long story short, it uses a factory method to leverage the common approach described above. As a result, the controller can remain neutral/ignorant of the particular type of STI class that you're creating.
You can use the Rails safe_constantize method, which will ensure the object/class actually exists.
For example:
def typeify(string)
string.classify.safe_constantize
end
new_special_event = typeify('special_event').new

Resources