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
Related
I'm looking to pass arguments from the controller to the model, but I keep getting wrong number of arguments.
Model:
before_create :embed_info
def embed_info(embed_url)
something = embed_url
## Do some stuff
self.title = "Title" ##This works.
end
Controller:
Create action:
#post = Post.new post_params
#post.embed_info(params[:title])
if #post.save
redirect_to root_url, notice: 'post created'
else
render 'new'
end
You can not pass arguments from controller to model callback like this.
You can use attr_accessor to create a virtual attribute and then set that attribute as part of your create action from controller.
If you're manually invoking the embed_info method, you shouldn't also be invoking it automatically via a before_create callback. Remove the line before_create :embed_info as it's currently serving no purpose except to invoking embed_info with no arguments when you attempt to save your model.
It's possible that you intended to add a validation, which is different than a before_create callback. If your intent was to make sure that embed_info had been called then you should use validations to insure that whatever side effects embed_info has leave the model in a valid state.
but I keep getting wrong number of arguments.
You are getting an arguments error because of this: before_create :embed_info. You can delete that line and then explicitly call the method like you are already doing in the controller.
This is probably not best practice but I think it will get your code working.
Another thing you could do is to move the logic for deriving the title to the controller and then pass the title in with the params.
# controller
def create
#post = Post.new post_params.merge(title: embed_info)
...
def embed_info
something = params[:title]
## Do some stuff
...
You could use attr_accessor to create a virtual attribute of embed_url. Pass it in new action itself. And when you call save, before_save will be called by itself and you can use embed_url there (as its a virtual attribute).
Let me know if you need help if the explanation is not clear.
Parameters: {"utf8"=>"✓", "authenticity_token"=>"yULNPCbrSyV/AeJqx9Phvg4yk7pWMjJ5BYpm4JVLr0SZ3zZdvEmvSUjKGqgH9XOwFEoS07wG5d9vrilsyntpCA==", "task"=>{"title"=>"smtp", "complexity"=>"high", "best_case_estimation"=>"333", "worst_case_estimation"=>"33"}, "button"=>"", "project_lead_id"=>"94", "task_module_id"=>"11"}
It's called "strong parameters" and is implemented by Rails to help ensure certain parameters aren't touched unless they're supposed to be. Looking at your parameters you have a task object (so a task form) being submitted so you'll have to whitelist those parameters if you're looking to update an object via "mass assignment". There is a ton of documentation on the subject and I highly recommend getting familiar with this concept since it's used very heavily. What you'll want to do is the following in your controller:
private
def task_params
params.require(:task).permit(:complexity, :title, :best_case_estimation)
end
Make sure to add this at the bottom since it's stating that all methods below the private line will be in fact considered "private" methods. Now you can add something like this in the 'create' action of that controller:
def create
#task = Task.new(task_params)
if #task.save
#stuff here
else
end
end
I'm using service objects to abstract a stripe payment feature into it's own class. I'm using this method https://gist.github.com/ryanb/4172391 talked about by ryan bates.
class Purchase
def initialize(order)
#order = order
end
def create_customer
customer = stipe create customer
if customer
charge_customer(customer)
else
stipe error in creating customer
end
end
def charge_customer(customer)
if charge is sucessfull
update_order(charge details)
else
stripe error in charging card
end
end
def update_order
#order.update(payment attributes)
end
end
Then in the order controller i'm doing something like
def create
#order = Order.new(params[:order])
if #order.save
payment = Payment.new(#order)
else
render 'new' with flash message "payment error"
end
end
My question is, how do i get the stipe error messages("stipe error in creating customer" and "stripe error in charging card") to display to the user?
Or can i call a service object in the order model and add it to order error messages? For example,
Order controller
#order.save_with_payment
Order model
def save_with_payement
payment = Payment.new(self)
#update order
self.payment_token = payment.token
etc
end
If i can do that with the model, how to i make a validation that shows the stripe errors?
Thanks in advance.
First of all try to separate concerns as possible. It already feels like Your Purchase/Payment class is doing too much, probably it should delegate part of it's routines to other service objects.
Second, I agree with phoet. I don't see the reason why You wouldn't pass params hash to service object. In our latest project we fully rely on service objects we call Factories to produce/manipulate our model objects. In Your case You could do like this:
class OrderFactory
def self.create(params)
new(params).create
end
def initialize(params)
#order = Order.new(params[:order])
end
def create
#payment = Payment.new(self)
#order.payment_token = payment.token
#....
end
end
Talking about validations - You can add validation methods to plain Ruby objects. For example by using ActiveModel:
http://yehudakatz.com/2010/01/10/activemodel-make-any-ruby-object-feel-like-activerecord/
Then You can create custom validators, like Ryan suggested.
Or You can use a gem like Virtus and add some custom validation rules.
i think that the service should be responsible for handling all this. so it might look like this
def create
#payment = Payment.new(params[:order])
if #order = #payment.execute_transaction
[...]
else
[...]
end
end
so the payment service would handle creating and persisting the order and also might be adding validation errors. you could also return some kind of #result object that might consist of the order and depending error messages.
I asked a very similar question just one day ago. The response I received was very helpful indeed (credit to #gary-s-weaver) so take a look.
Rails generic errors array
I went with the rescue_from approach in the end and it works well for me. Let me know how you get on as I'm very interested in this area. And if you need any code samples give me a shout.
I've written a gem for service objects. Check out peafowl gem.
With the koala gem I am trying to count checkins for a page. I am using rails.
In my user.rb I have a method for getting a new connection to the Facebook graph:
class User < ActiveRecord::Base
def facebook
#facebook ||= Koala::Facebook::API.new(oauth_token)
end
end
In my school.rb I have a method for counting the checkins:
class school < ActiveRecord::Base
def count_checkins(name)
checkins = #facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
end
And I am calling it from the view like this:
<%= #school.count_checkins(#school.name) %>
But I get the following error:
undefined method `fql_query' for nil:NilClass
Dont really understand why I get this error, any help would be wonderful.
It looks like you haven't actually created the #facebook object inside your School model. We'd need to see the rest of your school.rb file to know for sure. I'd suggest you create the object inside your School.initialize() method like so:
def initialize(oauth_token)
unless oauth_token.nil?
#facebook = Koala::facebook::API.new(oauth_token)
end
end
In order for this to work, you'll need to pass the desired oauth_token to your School.new() call. Then you'll have one #facebook object for each School.
Edit
After looking at the gist, I realized that you had actually intantiated a User object, and called the facebook method on that. That is actually the better way to do it. The problem is, you're using #current_user, which would have to be setup as a property of the school model. You probably meant to use the helper function current_user instead.
def count_checkins(name)
u = current_user
u.#facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
Try that and see what happens. At the very least, you should get a different error message.
Edit 2
So, now I'm thinking the current_user function should be called in controller code, not model code. This is because the current user is something that doesn't really exist except as part of an active request. Therefore, we should take User u as a parameter to the count_checkins function like so:
def count_checkins(name, u)
u.facebook.fql_query("SELECT checkins FROM page WHERE name = #{name}")
end
You'll need to change the code where you call count_checkins() too:
count_checkins(name, current_user)
That should do it. Let's see!
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.