I'm working some legacy code right now in seeds.rb. The previous developer used a method like this to add rows to the tables:
things_holder.oldthings_add(name)
where:
oldthings.rb
belongs_to :things_holder
things_holder.rb
has_many :oldthings
I can manipulate the object they created and adjust the seeds of the models they'd created. However, when I try to do the same to a model that I created myself (newthings), I can't seem to make it work.
Instead I get:
undefined method: newthings_add
where does this things_add method come from? I don't see it in any of the oldthings.rb files
*_add is not a standard Rails / Active Record method, so it's either defined somewhere in your application, or it's provided by some other gem.
As #jvillian's commented, you may be able to things_holder.method(:oldthings_add).source_location to learn where the method is defined.
If that doesn't work, you could try passing a blatantly invalid value to the method (e.g. things_holder.oldthings_add(true)), and see where the backtrace points.
Related
I'm using the gem discarded which adds a scope with_discarded on every model on which it is loaded on. To write some code that works with any model I added with_discarded scope (via a class method) on all models in application_record that does nothing so that then it will get overridden in those models which include the module from the discarded gem.
However, rails now produces a warning:
Creating scope :with_discarded. Overwriting existing method User.with_discarded every time I load a class that overwrites the base method. For completeness that's implemented just as:
def self.with_discarded
all
end
Now everything seems to be working fine, but I'm wondering what the correct way to do this would be and why I'm being warned. Am I supposed to define it as a scope rather than a class method (EDIT: tried it still get the warning)? Alternatively, is there a way to just filter this warning out of all my logs without loading a gem like Semantic Logger. Problem is that this warning shows up every time my GoodJob Scheduler runs and is polluting my logs.
So I followed #tadman's suggestion and just implemented a new method as follows
def self.include_discarded
respond_to?(:with_discarded) ? with_discarded : all
end
This doesn't quite let me overload the original with_discarded scope in the sense that I had to switch all the uses to include_discarded but it has the same effect. You could try and give the method the same name via metaprogramming but then you'll need a good way to run code in every subclass of ApplicationRecord.
I have two models Plan and Student::Plan. When calling Student::Plan.create! in my seeds.rb file I get back a Plan object. It seems as though rails is getting confused by the namespacing or something. Any thoughts that don't involve renaming the class?
file structure
models
plan.rb
student
plan.rb
tables
plans
student_plans
Update
This gets even weirder when testing w/ rails c. If you call Student::Plan.create! first then you'll get the expected object back. If you instead call Plan.create! and then Student::Plan.create! you'll be back a Plan object instead of what you'd actually expect.
Works
Student::Plan.create # <Student::Plan...>
Doesn't Work
Plan.create # <Plan...>
Student::Plan.create # <Plan...>
I can imagine a scenario where you have namespaced models (I've used it a few times...usually for integrating different db's). And this answer doesn't get to the issue of dealing with namespacing issues, but I do think the "Judo" solution is to just create a model called student_plan.rb at the root of the models directory, and use StudentPlan.create and Plan.create respectively.
I'm trying to figure out something in a Rails application. We have a class Site that has themes (of class Theme) and when a site uploads a theme it can install it for a specific site. So I'm tracking down the code for that and I see it calls the method 'build' like this:
#theme = #current_site.themes.build(params[:theme])
the themes method simply returns an array of Theme objects associated with that site. So in the rails console I tried this (result included):
>> site.themes.method(:build).__file__
NameError: undefined method `build' for class `Array'
and then I tried this:
>> site.themes.respond_to? :build
=> true
So my question is twofold, how can I find out where 'build' is defined so I can understand how it works?, and can someone explain to me how a method like 'build' works in terms of it being able to be called at the end of an Array object that has no such method? Thanks!
Given your Site model has_many :themes, this is one of the association methods that automatically gets added to it by the association.
If you want some more reading material, check out the Association Basics Guide (for example, the section on has_many associations), this lists all of the methods added along with the parameters they take.
As to the exactly how this works, the .themes object is actually an AssociationProxy (source here), with an Array as its target, not an Array itself. .class returns 'Array' because the proxy itself does not respond to the .class method, and instead forwards it to its target (the Array) via method_missing. This answer gives a more detailed explanation.
As the official site, I defined two models Post and Comment. Comment is nested in Post.
resources :posts do
resources :comments
end
So I can use #post.comments to access all comments a post own. In console:
$ post = Post.new()
$ post.comments.class
> Array
But:
$ post.comments.respond_to?("build")
> true
Why? An array has the method build? How did Rails do? And how can I know other hidden methods of post.comments?
Firstly, your resources calls are in your routes.rb file, which only deals with the URL parsing side of Rails. It's nothing to do with the associations between your models, which are set up using has_many and belongs_to calls in the relevant model files. Ignore the routes file for now as it's not related to the main part of your question.
With respect to associations, you'll find that post.comments is not returning you an Array, it's actually returning an ActiveRecord::Relation object. A Relation object is like an array - in fact any method you call on it which isn't relation-specific (like build) is actually passed on to an Array representation of the Relation's contents. So, when you call post.comments.length, the comments association object is calling .length on its internal array.
One of the consequences of this is that calling post.comments.class actually passes on the .class call to the Array too!
So, what can that Relation object actually do? Since the association is set up by the has_many call, you can find out in the has_many documentation. Similarly for a belongs_to association
In ruby you can add or change any method in any object. You can even add a new method to a string instance, for example:
x = "xyzzy"
x.name # => NoMethodError: undefined method `name' for "xyzzy":String
x.instance_eval do
class << self
define_method(:name) {"I got a name"}
end
end
x.class # => String
x.name # => "I got a name"
y = "other"
y.class # => String
y.name # => NoMethodError: undefined method `name' for "other":String
That build method might have been 'added' to an instance of Array returned by the comments accessor, created by a has_many macro. (As Gareth pointed, Rails complicated things a little, and the associations do not work this way. However, extending the objects may be a more 'clean' way in comparison to working as transparent proxy. Consider my example as a ruby-related, not rails)
As for the second part of your question, you may know the methods of a given object by accessing its methods function. It returns the names of all the (public) methods defined for this object. (If you want private ones, see the private_methods function.)
In my example x.methods would include also the newly defined "name" method, but y.methods will not.
There are also other functions in ruby, with which you can examine the objects. You may find them in Ruby API documentation. The API may have some (usually slight) changes in various versions of ruby. See the documentation for the version you are using.
If you want to examine the code of some method, then it may be a little problem, because the "executable code" of a function may be created by many ways: it may be a copy (alias) of another function, it may be created dynamically by using eval function, and so on.
Knowing the name of the method you may 'grep' the source code you have available, and maybe the method you want has not been created dynamically.
I see that the project ParseTree is still alive. Maybe you will find it helpful, if you really want to know the code of a function to which you do not have sources.
I am new to ruby and rails and I am having difficulty conceptualizing the MVC techniques in conjunction with database views. I am dealing with a legacy database that has several viiews that are used to generate reports.
Where I get lost is how do I actually use a database view. Should it be put in a model? If so what exactly would that look like?
As an example the legacy db has a view called qryTranscriptByGroup. It is used in the legacy application in an SQL statement such as "SELECT * FROM qryTranscriptByGroup WHERE group='test_group'". This returns a small number of records usually less than 100.
If i create a model, Transcript, how would I define a method like Transcript.find_by_group(group)? As well, it would seem that I might need to prevent any other "find" methods as they would be invalid in this context.
There is also the the fact that the view is read-only and I would need to prevent any attempts to create, update or destroy it.
Perhaps I am going about this entirely the wrong way. The bottom line is that I need to get information from several tables (models?) that represent the information about a user (a transcript). Actually one or more users (transcripts plural).
-Thanks!
You can use a database view like a normal model.
In your case:
class Transcript < ActiveRecord::Base
set_table_name "qryTranscriptByGroup"
set_primary_key "if_not_id"
end
The query will be then:
Trascript.find_by_group('test_group')
without you need to declare anything.
Rails uses the method_missing method to magically generate find_by_column_name methods.
For the create/update/delete action you can simply delete them or not create them in the controller.