I'm trying to make a chainable method such that I can write:
#blog = Blog.new.set_user(current_user).save
instead of
#blog = Blog.new
#blog.user = current_user
I have defined the following method inside the model:
def set_user(user)
self.user = user
return self
end
except that it doesn't work. How do I make a method to return the updated instance so that further chaining can be done upon it?
UPDATE:
My bad, here's what I was doing wrong: The chainable method was named "user" and so it was conflicting with the model's own blog.user method. I changed the name to something unique and voila! it works.
I would try to avoid this in Rails and use the models associations and scopes to do part of the work:
#blog = current_user.blogs.create
About the question you asked, returning self should do the work idd, can you write the output of the console when you create the blog and also let us know what is the output of:
#blog.inspect
Maybe the error is somewhere else...
Your set_user method should return a user instance. self in this context is Blog
def set_user(user)
user
end
If you are using Rails and there is an association between Blog and User you are already able to do
blog.user or blog.users based on what type of association you have in between these models.
Related
This is what I am trying to do here. I call a function test from user controller. and inside the test function I initialize a new article object like this article = new Article. But this gives me error
ArgumentError in UsersController#create
When assigning attributes, you must pass a hash as an argument.
I have following code
users_controller
def create
User.test
end
private
def user_params
params.require(:user).permit(:name, articles_attributes:[:content)
end
User model
class User
def self.test
article = new Article
article.attributes = {"content"=>"this is some sample content"}
article.save
end
end
I know this question has been asked and answered lots of time but I could not find anything that matches with my problem or a solution for this. So please tell me how to save the article object inside user model call.
How is article = new Article even working? Oh I see. its calling Class#new and then passing in Class definition of itself... so it compiles. but its still not doing what you think it is. Try article = Article.new
Also, your assignment inside the User model has no bearing on the controller user_params method which sanitizes your parameters
I am trying to assign values of ActiveRecord attributes within my models, but for whatever reason, I can't set them.
For instance, I have an AccountModel and this has an Attribute name
If I set it from the controller or the console (like user.name = "John"), everything works fine.
But, if I try to set it from within the model, like
def set_name(new_name)
name = new_name
end
then it doesn't work. On the other hand, retrieving the name, like
def get_name
name
end
works just fine. Am I missing something?!
I am using Ruby 2.0.0-p247 and Rails 4.0.0; Please note, that this examples aren't real world examples, I just tried to keep them simple to clarify my problem.
Best regards,
Mandi
Try:
def set_name(new_name)
self.name = new_name
end
You need to use the self keyword to refer to your instance attributes on assignment. Otherwise ruby will assign your new name to a local variable called name.
You might want to save your changes after
user = User.new
user.set_name('foo')
user.save
Take a look at the example here, there is one similar to your question at the end ;)
Your code looks fine, but are you saving the changes? Try adding a save call:
def set_name(new_name)
self.name = new_name
self.save
end
You don't need to do self.save, just call save inside your model. You only use self.attribute when you need to assign.
Try
def set_name(new_name)
self.name = new_name
self.save!
end
Then call the instance method from controller simply
user.set_name('foo')
I have an activeadmin resource which has a belongs_to :user relationship.
When I create a new Instance of the model in active admin, I want to associate the currently logged in user as the user who created the instance (pretty standard stuff I'd imagine).
So... I got it working with:
controller do
def create
#item = Item.new(params[:item])
#item.user = current_curator
super
end
end
However ;) I'm just wondering how this works? I just hoped that assigning the #item variable the user and then calling super would work (and it does). I also started looking through the gem but couldn't see how it was actually working.
Any pointers would be great. I'm assuming this is something that InheritedResources gives you?
Thanks!
I ran into a similar situation where I didn't really need to completely override the create method. I really only wanted to inject properties before save, and only on create; very similar to your example. After reading through the ActiveAdmin source, I determined that I could use before_create to do what I needed:
ActiveAdmin.register Product do
before_create do |product|
product.creator = current_user
end
end
Another option:
def create
params[:item].merge!({ user_id: current_curator.id })
create!
end
You are right active admin use InheritedResources, all other tools you can see on the end of the page.
As per the AA source code this worked for me:
controller do
def call_before_create(offer)
end
end
Scenario: I have a has_many association (Post has many Authors), and I have a nested Post form to accept attributes for Authors.
What I found is that when I call post.update_attributes(params[:post]) where params[:post] is a hash with post and all author attributes to add, there doesn't seem to be a way to ask Rails to only create Authors if certain criteria is met, e.g. the username for the Author already exists. What Rails would do is just failing and rollback update_attributes routine if username has uniqueness validation in the model. If not, then Rails would add a new record Author if one that does not have an id is in the hash.
Now my code for the update action in the Post controller becomes this:
def update
#post = Post.find(params[:id])
# custom code to work around by inspecting the author attributes
# and pre-inserting the association of existing authors into the testrun's author
# collection
params[:post][:authors_attributes].values.each do |author_attribute|
if author_attribute[:id].nil? and author_attribute[:username].present?
existing_author = Author.find_by_username(author_attribute[:username])
if existing_author.present?
author_attribute[:id] = existing_author.id
#testrun.authors << existing_author
end
end
end
if #post.update_attributes(params[:post])
flash[:success] = 'great!'
else
flash[:error] = 'Urgg!'
end
redirect_to ...
end
Are there better ways to handle this that I missed?
EDIT: Thanks for #Robd'Apice who lead me to look into overriding the default authors_attributes= function that accepts_nested_attributes_for inserts into the model on my behalf, I was able to come up with something that is better:
def authors_attributes=(authors_attributes)
authors_attributes.values.each do |author_attributes|
if author_attributes[:id].nil? and author_attributes[:username].present?
author = Radar.find_by_username(radar_attributes[:username])
if author.present?
author_attributes[:id] = author.id
self.authors << author
end
end
end
assign_nested_attributes_for_collection_association(:authors, authors_attributes, mass_assignment_options)
end
But I'm not completely satisfied with it, for one, I'm still mucking the attribute hashes from the caller directly which requires understanding of how the logic works for these hashes (:id set or not set, for instance), and two, I'm calling a function that is not trivial to fit here. It would be nice if there are ways to tell 'accepts_nested_attributes_for' to only create new record when certain condition is not met. The one-to-one association has a :update_only flag that does something similar but this is lacking for one-to-many relationship.
Are there better solutions out there?
This kind of logic probably belongs in your model, not your controller. I'd consider re-writing the author_attributes= method that is created by default for your association.
def authors_attributes=(authors_attributes)
authors_attributes.values.each do |author_attributes|
author_to_update = Author.find_by_id(author_attributes[:id]) || Author.find_by_username(author_attributes[:username]) || self.authors.build
author_to_update.update_attributes(author_attributes)
end
end
I haven't tested that code, but I think that should work.
EDIT: To retain the other functionality of accepts_nested_Attributes_for, you could use super:
def authors_attributes=(authors_attributes)
authors_attributes.each do |key, author_attributes|
authors_attributes[key][:id] = Author.find_by_username(author_attributes[:username]).id if author_attributes[:username] && !author_attributes[:username].present?
end
super(authors_attributes)
end
If that implementation with super doesn't work, you probably have two options: continue with the 'processing' of the attributes hash in the controller (but turn it into a private method of your controller to clean it up a bit), or continue with my first solution by adding in the functionality you've lost from :destroy => true and reject_if with your own code (which wouldn't be too hard to do). I'd probably go with the first option.
I'd suggest using a form object instead of trying to get accepts_nested_attributes to work. I find that form object are often much cleaner and much more flexible. Check out this railscast
I have the following which works great:
#request_thread = current_user.request_threads.new(params[:request_thread])
And I have this which works great:
#requestable = find_requestable
#request_thread = #requestable.request_threads.new(params[:request_thread])
But if I try the 2nd line with:
#request_thread = #requestable.current_user.request_threads.new(params[:request_thread])
With the current_user I get the following error:
NoMethodError (undefined method `current_user' for #<Photo:0x10f95c828>):
app/controllers/request_threads_controller.rb:52:in `create'
app/middleware/flash_session_cookie_middleware.rb:14:in `call'
What did I mess up on here?
Thanks
UPDATE - with find_requestable
# http://asciicasts.com/episodes/154-polymorphic-association
def find_requestable
params.each do |name, value|
if name =~ /(.+)_id$/
return $1.classify.constantize.find(value)
end
end
nil
end
The error message tells you exactly whats wrong: current_user doesn't exist for the #requestable object, whatever that is.
current_user is most likely a function inherited from ApplicationController, or at least that's usually where it lives. It usually returns a User object according to the current session. But that isn't a built-in part of Rails, so we need more information if you want me to go into greater detail.
#requestable looks like a polymorphic model instance so it wouldn't be aware of the current session.
There's a bit of Rails magic happening here that I think is confusing you.
Both #requestable.request_threads and current_user.request_threads are helper functions that have been generated by Rails on those objects to save you time by filtering results and filling in values automatically.
I think, by the code you have shown us so far, that you are trying to associate current_user with the new request_thread you are creating. In that case, you can simple merge that into the attributes manually. Since I don't know what your models look like I can only guess at the field names:
#request_thread = #requestable.request_threads.new( params[:request_thread].merge(:user => current_user) )
Like I said, the chainable functions are merely for convenience. For instance, you could write it this way instead:
#request_thread = request_thread.new(params[:request_thread])
#request_thread.requestable = find_requestable
#request_thread.user = current_user