Project.joins(:project_status).where(id: 1).first
I want to use a string such as "Project.ProjectStatus.name" and it will return the name of the joined project status, and simply something like Project.name.
.read_attribute() doesn't seem to go deeper to joined/included records. .send works when I do .send('ProjectStatus').send('id') but this does not seem ideal and could be dangerous, these variables come from users.
It's for a templating engine so I might have something like..
"{{Project.name}} status was changed to {{ProjectStatus.name}}"
Are there any gems I can pick up? I'm really exhausting what I've been searching for in Google now. In CakePHP I have used Hash::extract before which is a little like xpath.
What is the association between Project and ProjectStatus here? I assume it's has_one: :project_status?
Anyway, if you want to be able to call #project.name ( where #project is an instance of the class Project; .name should be specific to each individual project, so you should define it as an instance method rather than the class method Project.name), you can create a method in Project model:
def name
project_status.name
end
Assuming that you previously defined the association in model as
has_one: :project_status
Now the name can be retrieved simply by using .name
Related
delete_all is useful, but I never want to see it called on the same line with a table name. I'd like to disable things like TableName.destroy_all in both console and code.
One interesting issue happened earlier this month:
Application.destroy_all was called on a model instead of applications.destroy_all
(the model has_many applications)
For somebody new to ROR, it looks very similar, but the results were disastrous.
I'm open to some form of lint/code style tool, but that really wouldn't catch it in the console scenario. (Plus, I haven't been able to get rubo-cop to do something like this yet)
Basically, I'm asking for a way to make the console and codebase more secure so that newer developers can't inadvertantly delete everything in a table.
I'm not entirely clear on what you are trying to accomplish, but you could try overriding the method in your ApplicationModel with something like this (assuming Rails 5 or greater, or otherwise a root model in existence).
class ApplicationModel < ActiveRecord::Base
def self.destroy_all(*args)
raise('Cannot destroy all records of a model this way. Did you mean to delete a subset of records instead?')
end
end
Possibly make this method private if you'd like it even harder to run...
def self.destroy_all(*args)
raise('Cannot destroy all records of a model this way. Did you mean to delete a subset of records instead?')
end
private_class_method :destroy_all
You could get fancy and allow this to be bypassed with a special argument that you check for, but give this a try and see how it goes.
hope you are all well!
I have a general enquiry that I was wondering if some kind soul could help with? it's really a matter of curiosity at the minute but I feel like it could be quite a useful snippet of information in the future.
Is it possible to write a method that can be passed the name of a table and the name of an attribute (column) and perform operations on these? I suppose the main use for such methods would be for keeping code dry when doing repetitive operations on tables.
as an example (though entirely a toy example) suppose I had a method:
def switch(table_name, column_name)
#do some operation on table_name.column_name
end
I have figured out how to access a table by doing something like this:
def model_for_table(table_name)
table_name.to_s.classify.constantize
end
this will take an underscored_lowercase_string and return the table name so that something like model_for_table("registered_user").find(1) though this is unnecessary in situations where the table name can be hard coded
But it does not like model_for_table("registered_user").column_name as used in the example above. is there something analogous to the model_for_table method supplied above to turn a string into an attribute name?
Does anybody know how I could implement this? is it even possible?
Thanks in advance
The problem is that you need an instance of the model you are working on in order to access a column. If you have a RegisteredUser model try doing (in a Rails console) RegisteredUser.id (or any attribute name). It won't work. However, if you did RegisteredUser.first.id (assuming you have one saved) it will work.
So it depends on what you want to accomplish. If your switch method is meant to do something with instances of your model, then this can still work.
def switch(table_name, column_name)
model = model_for_table(table_name)
model.all.each do |model_instance|
puts "model_instance #{column_name} is #{model_instance.send(column_name)}"
end
end
Note: The send method takes in a symbol or a String and executes the method with that name on the instance it was called on. This is a normal Ruby thing, not a Rails thing.
Remember, your model_for_table method is returning back the class, not an instance.
In Ryan Bates's first episode on complex forms, he adds the following to a model:
# models/project.rb
has_many :tasks
def task_attributes=(task_attributes)
task_attributes.each do |attributes|
tasks.build(attributes)
end
end
I've never thought about this before, but how does the Project model know what "tasks" of which Project instance? Does that come from the has_many association? Is it like, when the project is running and I'm viewing a Project, that's the "active" object so project.rb knows which Project object we're referring to, so it knows that tasks is really some_current_project.tasks? (I'm obviously grasping at straws here.)
Also, if someone would point me to some reference that explains other questions like this one, I'd really appreciate it.
I hope my question is clear. Please ask for more clarification in comments if needed.
Please note: I know that Active Record handles CRUD actions and that objects correspond to rows in tables, etc. Those are just descriptions of what Active Record is. I'm looking for how it works when the project is running. I also now the constructs MVC, but I can't seem to find a detailed explanation of what information is sent where with respect to Rails.
(Not sure I fully understood your question, feel free to let me know if that's the case.)
A rails model is basically a ruby class that is persisted to a database. So it acts like a normal ruby object for the most part, with some database magic mixed in.
You tell rails which project instance to load (e.g. by providing an id), and it loads the data from the database.
Then, when you call project.tasks is when the magic happens: the Project model has no tasks method, so it will trigger ruby's method_missing method. This will then load the associated records into model instances and provide access to them via a rails object.
Since a project has many tasks, rails knows it should look into the tasks database and load the rows where project_id is equal to the project model's id attribute.
In short, ruby meta-programming and monkey patching possibilities make much of rails' magic possible.
(Edit for question on routing.)
When you want to edit project number 13, you go to a URL that looks something like www.mysite.com/projects/13/edit. If you look at routes.rb in your config directory, you'll see (in Rails3) resources :projects what Rails does is set up all sorts of paths for you. Behind the magic, the edit path looks like
get '/projects/:id/edit' => 'projects#edit'
This basically says "when a user wants to see www.mysite.com/projects/13/edit, send him to the edit action in the projects controller and set the id parameter to the value that's in that place.
Then in your controller, you'll load the appropriate project with
#project = Project.find(params[:id])
In a similar way, you could do this (this is an dumb example):
In routes.rb, put
get '/projects/:id/edit_name/:name' => 'projects#edit'
And then in you controller
#project = Project.find(params[:id])
#project.name = params[:name]
So rails basically uses magic to assign values in the URL to params you can work with in your controller. You can read more about routing here: http://guides.rubyonrails.org/routing.html
I'm writing a Rails application against a legacy database. One of the tables in this legacy database has a column named object_id. Unfortunately object_id is also an attribute of every object in Ruby, so when ActiveRecord is trying to use these objects to formulate a query it is using the Ruby defined object_id, rather than the value that is in the database.
The legacy application is immense at well over a million lines of code, so simply changing the name of the column in the database would be an option of last resort.
Questions:
1. Is there any way to make ActiveRecord/Rails use an alias or synonym for this column?
2. Is there any way in Ruby to make the object_id method behave differently, depending on who is calling it?
3. Can I simply override the behavior of the object_id method in my model (I assume this is a terrible idea, but had to ask)
Any suggestions are greatly appreciated.
I'm just kind of spitballing here, but you might try something like this:
class Legacy < ActiveRecord::Base
#... all the other stuff
#give yourself a way to access the DB version of object_id
def oid
attributes[:object_id]
end
def oid=(val)
attributes[:object_id]=val
end
#restore ruby's default #object_id implementation
def object_id
super
end
end
Check out alias_attribute http://www.railstips.org/blog/archives/2008/06/20/alias-attribute/ I believe that it does what you are looking for.
I have a simple has_one/belongs_to relationship between two models.
This is a new association in my application so there are many records that do not yet have the associated record created.
Throughout my application I'm assuming the model has the association and I'm accessing its attributes and methods. However, because the association doesn't exist, I'm running into a lot of errors.
What I would like to do is unobtrusively build the associated record on the fly whenever it's access for the first time through any of its methods and attributes. It does not matter that there is data in record, I simply need it to exist so those methods I'm calling can build the data.
Edit: I do not want to check and create the record on all of the instances where I'm trying to access the relationship, so idealy this needs to be done on the model itself and not in my controllers anywhere.
Any thoughts?
Thanks!
Here's what we ended up with that did the trick. I didn't write it (a co-worker did) but it passes the previously failing tests that I wrote for this case.
def stats_with_create
stats_without_create || create_stats
end
alias_method_chain :stats, :create
In the controller, you could put something like this in the show method (untested, but it should give you an idea:
#thing = Thing.find params[:id]
if #thing.other_thing.nil?
#thing.other_thing = OtherThing.new.save!
#thing.save!
end
This isn't ideal, and you could probably clean it up a lot by putting a method in the Thing model that would check for and create the related model instead of putting it into your controller.
Another option would be to create a new accessor that you use to access the other_thing, creating it as required.
However, the correct thing to do is probably to fix your data, either in a migration or directly, creating the related models properly.
The direct answer is to override method for the relationship. When called it will check if the record exists and create it if it doesn't.
However, I would recommend that you use a migration to create all of the records up front.
I have done this type of thing before but not on the model level. Ive done it on the controller level with a before_filter that ran before all methods which needed to access the model association that did or did not exist yet.
I just realized there is the after_find and after_initialize callbacks that you can use in the model.
You could stick:
def after_initialize
association.build if association.nil?
end
in your model and it should solve your problems.. (disclaimer: untested by me) :)