I am using Ruby on Rails 3 and I am tring to do some like this in my model file
if request.headers["CONTENT_LENGTH"]
...
end
but I get this error:
NameError (undefined local variable or method `request' for #<User:0x00...>):
So, is it possible to use the 'request' method in a model? If so, how?
No, because the request is only available in controllers and view code. From a design point of view you're ill advised to use the request within model code, but let's say you want to do something with the request on a particular instance of your User, just create a method for it:
class User
...
def has_a_request?(request)
raise ArgumentError if(request.blank? || !request.respond_to(:headers))
if(request.headers["CONTENT_LENGTH"] # Does it really have CONTENT_LENGTH btw?
puts "#{self.username} got a request with content length"
return true
else
puts "#{self.username} didn't get a request with content length"
return false
end
end
And then elsewhere in your application:
User.has_a_request?(request)
Is not good app design to use the request object in the method, but if you do absolutely need it you can do :
class User
def a_method request
end
end
class UserController
def index
User.find(params[:user]).a_method request
end
end
Extract relevant data from the request object in your controller, then pass them on to the model -- either by setting attributes on an instance of it or by passing them as parameters to a method call.
Related
I have this function in rails controller:
def validate_params(*props)
props.each do |prop|
unless params[prop].start_with?('abc')
# return error
end
end
end
im thinking if I have params[:name] and params[:bio] and I want to validate name & bio with this function (not every attribute I might want to validate), I will call it with validate_params(:name, :bio). But, for nested param it won't work like params[:user][:name]. Is there anything I can do to pass this nested property to my function or is there a completely different approach? Thanks
Rails Validations generally belong in the model. You should post some additional info about what you're trying to do. For example, if you wanted to run the validation in the controller because these validations should only run in a certain context (i.e., only when this resource is interacted with from this specific endpoint), use on: to define custom contexts.
If you don't want to do things the rails way (which you should, imo), then don't call params in the method body. i.e.
def validate_params(*args)
args.each do |arg|
unless arg.start_with?('abc')
# return error
end
end
end
and call with validate_params(params[:user], params[:user][:name]
but yeah... just do it the rails way, you'll thank yourself later.
I'm attempting to restrict an API's content type in a RoR application, with a method that gets inherited by all controllers.
CONTENT_TYPE = 'application/vnd.api+json'
def restrict_content_Type
return if request.content_type = CONTENT_TYPE
render_content_type_error
end
this works fine, but now I must use a different content type for a single endpoint and controller, and I'd like to just change the content of the CONTENT_TYPE constant while reusing the code I already have. To use a different constant I must use a reader method that looks up the constant in the current controller.
I refactored the code into:
def get_content_type
self::CONTENT_TYPE
end
def restrict_content_type
return if request.content_type == get_content_type
...
end
The reason I've used a get_* reader is that self.content_type returns the Request's content type: https://api.rubyonrails.org/classes/ActionDispatch/Response.html#method-i-content_type
At this point Rubocop is complaining because of the name I used, get_*readers are not idiomatic Ruby.
I can surely override this behaviour in rubocop but I'd like to hear what are my other options and if there are other solutions, because I don't like the name of the method either.
Any idea?
You can use some other names that reveals the purpose of this this method, e.g. current_content_type, restricted_content_type, disabled_content_type - whatever suits you best.
About the naming it could be nice to have a method called invalid_content_type? which returns a Boolean.
For e.g :
def invalid_content_type?(content_type)
request.content_type == content_type
end
def restrict_content_type
return if invalid_content_type(self::CONTENT_TYPE)
...
end
I have an app structure with nested routes in which a proposal belongs to a request and a request has many proposals.
When I execute a send_proposal method, I am trying to get it to update the status of the request to which that proposal belongs, but I am getting an error that says undefined method 'request' for true:TrueClass.
My route for this method (not that I think it matters) is:
put "proposal/:id/send_proposal" => "proposals#send_proposal", as: "send_proposal"
Here is my send_proposal method, as found in my proposals_controller:
def send_proposal
#proposal = Proposal.find(params[:id])
ProposalMailer.send_proposal_to_client(#proposal, #proposal.request.user).deliver_now
#proposal = #proposal.update_attributes(status: "Sent to Client")
#proposal.request = #proposal.request.update_attributes(archived: "Proposal Sent to Client") <<<<<<<<<ERROR CALLED ON THIS LINE
flash[:notice] = "Your proposal has been sent to the client!"
end
I have looked at many SO posts for other TrueClass errors, but can't seem to find one with a problem like this. Can anyone see what I'm doing wrong or help me conceptualize what TrueClass errors are generally about?
update_attributes is an alias for update:
update(attributes)
Updates the attributes of the model from the passed-in hash and saves the record, all wrapped in a transaction. If the object is invalid, the saving will fail and false will be returned.
and update returns true or false (the documentation could be a lot more explicit about this) not the updated model instance. So this:
#proposal = #proposal.update_attributes(status: "Sent to Client")
will leave #proposal as true or false and neither of those have an update_attributes method.
Your controller method should look more like this:
def send_proposal
#...
#proposal.update(status: "Sent to Client"))
#proposal.request.update(archived: "Proposal Sent to Client")
#...
end
You probably want to do some error checking on those two update calls too.
I am fairly new to the world of Ruby and Rails (started a week ago, my background is mostly PHP). I've got this class here which acts as a factory.
I get the following error :
undefined method `stringify_keys' for #
I understand the constructor (.new) expects a Hash instead of an object (why ?) but, although I spent a couple of hours searching the www, I didn't come up with a viable solution at this point.
I just want to inject that Soap object into my constructor. That constructor is pretty straightforward, it puts the object parameter in the instance variable that is supposed to store it.
I've been looking for methods that would turn that object into a proper Hash but all I saw was a bunch of OLD posts with rather dirty hacks. I'd rather quit programming than use them ^^.
I never thought doing this would cause a headache...
Thanks for the tips !
class WebServices::WebServiceFactory
def initialize (type, url, login, password, protocol = "soap")
#type, #protocol, #url, #login, #password = type, protocol, url, login, password
case #protocol.capitalize
when "Soap" then
requestor = WebServices::Soap::Soap.new(url, login, password)
end
#class = #type.constantize.new(requestor)
end
def getservice
return #class
end
end
The initialize method can accept whatever arguments you define, it doesn't need to be a Hash, and in fact, the code above it looks like you have 5 arguments that can be of any type.
Are you wanting to pass the Soap object as an argument of initialize?
If you are, something like this would do (assuming you are using your version of Ruby is >= 2.0)
class WebServices::WebServiceFactory
def initialize(requestor:, type:)
#class = type.constantize.new(requestor)
end
end
If you want this class to handle the details of creating the Soap object, maybe something like this would work for you:
module WebServices
class WebServiceFactory
SERVICES = {
soap: 'WebServices::Soap::Soap'
}
def initialize(type:, protocol: :soap, params: {})
return unless SERVICES.key?(protocol.to_sym)
requestor = SERVICES[protocol.to_sym].constantize.new(params[:url], params[:login], params[:password])
#class = type.constantize.new(requestor)
end
end
end
I'm new to Ruby and I would like to find out what the best way of doing things is.
Assume the following scenario:
I have a text field where the user can input strings. Based on what the user inputs (after validation) I would like to access different fields of an instance variable.
Example: #zoo is an instance variable. The user inputs "monkey" and I would like to access #zoo.monkey. How can I do that in Ruby?
One idea that crossed my mind is to have a hash:
zoo_hash = { "monkey" => #zoo.monkey, ... }
but I was wondering if there is a better way to do this?
Thanks!
#zoo.attributes gives you a hash of the object attributes. So you can access them like
#zoo.attributes['monkey']
This will give nil if the attribute is not present. Calling a method which doesn't exist will throw NoMethodError
In your controller you could use the public_send (or even send) method like this:
def your_action
#zoo.public_send(params[:your_field])
end
Obviously this is no good, since someone can post somehing like delete_all as the method name, so you must sanitize the value you get from the form. As a simple example:
ALLOWED_METHODS = [:monkey, :tiger]
def your_action
raise unless ALLOWED_METHODS.include?(params[:your_field])
#zoo.public_send(params[:your_field])
end
There is much better way to do this - you should use Object#send or (even better, because it raises error if you try to call private or protected method) Object#public_send, like this:
message = 'monkey'
#zoo.public_send( message )
You could implement method_missing in your class and have it interrogate #zoo for a matching method. Documentation: http://ruby-doc.org/core-1.9.3/BasicObject.html#method-i-method_missing
require 'ostruct' # only necessary for my example
class ZooKeeper
def initialize
#zoo = OpenStruct.new(monkey: 'chimp')
end
def method_missing(method, *args)
if #zoo.respond_to?(method)
return #zoo.send(method)
else
super
end
end
end
keeper = ZooKeeper.new
keeper.monkey #=> "chimp"
keeper.lion #=> NoMethodError: undefined method `lion'