I have an integer in my Parent Object's table called completion_status with a default value of 0. Id like to set this to the same value as "#parent.child.count('completion', :distinct => true)", but I have no idea how to do this in the controller, or even if the controller is the best place to do this.
I know there's not much information included here, but let me know if I'm missing something important. I'm having kind of a brain-fart moment here.
EDIT: Just tried:
def set_completion
#app = App.find(params[:id])
#app.update_attribute(:completion_status => #app.elements.count('completion', :distinct => true))
end
From what I understand, this value could possibly change everytime you create a new child, because creating a new child will change the value of #parent.child.count, so you want to reset completion_status everytime you create or update a new child, so one way to do this is inside the create and update action of the ChildrenController -assuming you have one :
def create
#parent = Parent.find(params[:parent_id]
#child = #parent.children.build(child_params)
if #child.save
#parent.completion_status = #parent.children.count // this is the line to add
end
end
This code could be improved in different ways but I'm just giving you an example.
Related
I inherited a project - controller has a custom action within which we set a couple instance variables:
#contact = #property.contact ? #property.contact : Contact.new
#contractor = #property.contractor ? #property.contractor : Contractor.new
I think the Rails way would be to initialize Contact.new and Contractor.new when I call #property.contact or #property.contractor if they don't exist instead of setting these up as instance variables in the controller.
But I don't know the syntax to do it.
Without seeing the rest of your app, it's far more likely that this logic belongs in the controller, where it is. Usually these things are done to pre-populate the association objects required for Rails to render a form correctly, so it's not something that really makes sense in the model itself, ie. for a property.contact method to return a Contact.new if one doesn't already exist doesn't (usually) make any logical sense, whereas setting the #contact variable in the controller/view to a new one for display purposes makes sense.
FWIW, the above code is precisely the same as the more concise:
#contact = #property.contact || Contact.new
#contractor = #property.contractor || Contractor.new
Is it possible to return the parent object of a given attribute?
Example
a = User.birthdate
a.parent_object ... should return the user record that is the parent of the birthdate attribute
A better example?
Helper
def item_grade(subject, obj)
obj.scale.grades.find(subject.grade_id).name # would return something like "Pass", "Fail", "Good Job"
end
In the view
item_grade(#course.subject, #course)
This approach requires two options to be passed to the helper. It seems I should be able to pass #course.subject and then get the parent object from that
Helper
def item_grade(subject)
a = subject.parent_object.scale
a.grades.find(subject.grade_id).name
end
View
item_grade(#course.subject)
This approach requires two options to be passed to the helper.
You can remove some duplication by doing this, for example.
def item_grade(obj, property)
obj.scale.grades.find(obj.send(property).grade_id).name
end
item_grade(#course, :subject)
Now you don't have to repeat #course in the call.
Having to pass two parameters is much less harmful than any sort of hackery you can come up with (thanks #muistooshort). There's no built-in way to do this.
I am creating a instance variable that gets passed to my view. This variable 'post' has a user_id associated with it and I wanted to add an extra attribute called 'username' so I can also pass that and use it in the view.
Here is an example of what I would like to do.
#post = Post.find(params[:id])
#post.username = User.find(#post.user_id).username
A username column does exist on my Users model but not my Songs model. So it won't let me use
#post.username
I know I can just make an entirely new instance variable and put that information in there but I would like to keep everything nice and neat, in one variable. Which will also make my json rendered code look cleaner.
Any ideas on how I can accomplish this?
Thanks!
Based on the presence of a user_id in your Post model, you probably already have an association set up that can retrieve the username. It will probably save a lot of trouble to simply use the existing association:
#post = Post.find(params[:id])
username = #post.user.username
If you're likely to be querying more than one post at a time (e.g., on an index page, calling .includes to tell Rails to eager-load an association will help you avoid the N+1 problem:
#posts = Post.includes(:user).all
Finally, to include the associated record in your JSON output, pass the :include parameter as you serialize:
# in controller
render :json => #post.to_json(:include => :user)
This question includes a much more comprehensive discussion of serialization options. Well worth a read.
No need to pass a separate instance variable.
1. You can use #post.user.username in view itself.
2. Or you can create a helper and pass #post.user
def username user
user.username
end
So i need to get instantiate an object which requires parameters. I also need this object to be available in the scope of the entire controller once instantiated. How can this be done?
Edit: some code to help illustrate
def beginLoad(user, category)
#stuff
#gaobj = GraphAssistant.new(#arrays.fetch(0), #arrays.fetch(1))
values = #gaobj.externalize
ret = {"axis_label" => values.fetch(0), "out" => values.fetch(1), "i" => values.fetch(2)}
end
But when I try to call it again from this method:
def resumeLoad(direction)
if direction.eql? "left"
#gaobj.decrementPosition
elsif direction.eql? "right"
#gaobj.incrementPosition
end
#other stuff
end
it doesnt work. I suppose I could do all of this logic in the view, what implications will that have on performance though?
Prefix it with an #sign: #foo = Foo.new.
Update: Sounds like you need this persisted to the session.
At the end of beginLoad, add:
session[:foo] = #foo
At the beginning of resumeLoad, add:
#foo = session[:foo]
If both of those functions are being called from the same controller action, the instance variable (#gaobj) should be accessible to both. You will need to make sure that #beginLoad is called before #resumeLoad for every incoming request. Is that the problem?
I have been trying to get my head around render_to but I haven't had much success.
Essentially I have controller methods:
def first
#I want to get the value of VAR1 here
end
def second
VAR1 = ["Hello", "Goodbye"]
render_to ??
end
What I can't figure out is how to accomplish that. Originally I just wanted to render the first.html.erb file but that didn't seem to work either.
Thanks
Edit: I appreciate the answers I have received, however all of them tend to avoid using the render method or redirect_to. Is it basically the case then that a you cannot pass variables from controller to controller? I have to think that there is some way but I can't seem to find it.
It is not a good idea to assign the object to a constant. True this is in a global space, but it is global for everyone so any other user going to this request will get this object. There are a few solutions to this.
I am assuming you have a multi-step form you are going through. In that case you can pass the set attributes as hidden fields.
<%= f.hidden_field :name %>
If there are a lot of fields this can be tedious so you may want to loop through the params[...] hash or column_names method to determine which attributes to pass.
Alternatively you can store attributes in the session.
def first
#item = Item.new(params[:item])
session[:item_attributes] = #item.attributes
end
def second
#item = Item.new(session[:item_attributes])
#item.attributes = params[:item]
end
Thirdly, as Paul Keeble mentioned you can save the model to the database but mark it as incomplete. You may want to use a state machine for this.
Finally, you may want to take a look at the Acts As Wizard plugin.
I usually don't have my controllers calling each other's actions. If you have an identifier that starts with a capital letter, in Ruby that is a constant. If you want to an instance level variable, have it start with #.
#var1 = ["Hello", "Goodbye"]
Can you explain what your goal is?
Have you considered using the flash hash? A lot of people use it solely for error messages and the like, it's explicitly for the sort of transient data passing you might be interested in.
Basically, the flash method returns a hash. Any value you assign to a key in the hash will be available to the next action, but then it's gone. So:
def first
flash[:var] = ["hello", "goodbye"]
redirect_to :action => :second
end
def second
#hello = flash[:var].first
end
way 1
Global variable
(fail during concurrent requests)
way 2
class variable
(fail during concurrent requests)
way 3
Stash the object on the server between requests. The typical way is to save it in the session, since it automatically serializes/deserializes the object for you.
Serialize the object and include it in the form somewhere, and
deserialize it from the parameters in the next request. so you can store attributes in the session.
def first
#item = Item.new(params[:item])
session[:item_attributes] = #item.attributes
end
def second
#item = Item.new(session[:item_attributes])
#item.attributes = params[:item]
end
way 4
The flash provides a way to pass temporary objects between actions. Anything you place in the flash will be exposed to the very next action and then cleared out.
def new
#test_suite_run = TestSuiteRun.new
#tests = Test.find(:all, :conditions => { :test_suite_id => params[:number] })
flash[:someval] = params[:number]
end
def create
#test_suite_run = TestSuiteRun.new(params[:test_suite_run])
#tests = Test.find(:all, :conditions => { :test_suite_id => flash[:someval] })
end
way 5
you can use rails cache.
Rails.cache.write("list",[1,2,3])
Rails.cache.read("list")
But what happens when different sessions have different values?
Unless you ensure the uniqueness of the list name across the session this solution will fail during concurrent requests
way 6
In one action store the value in db table based on the session id and other action can retrieve it from db based on session id.
way 7
class BarsController < UsersController
before_filter :init_foo_list
def method1
render :method2
end
def method2
#foo_list.each do | item|
# do something
end
end
def init_foo_list
#foo_list ||= ['Money', 'Animals', 'Ummagumma']
end
end
way 8
From action sent to view and again from view sent to other actions in controller.