I have this method
def create_billing_address(data)
address_data = data.permit(:first_name,
:last_name,
:phone,
:address_1,
:city,
:postcode,
:country)
service_customer.create_address(customer_id, address_data)
end
But now I want to check that all the keys are present. I tried to do this
address_data = data.require(:first_name,
:last_name,
:phone,
:address_1,
:city,
:postcode,
:country)
But require return an array instead of an hash.
How can I do to have the same behaviour of permit but with require ?
require is only intended to ensure that the params have the correct general structure.
For example if you have:
params.require(:foo).permit(:bar, :baz)
require lets us bail early if the :foo key is missing since we cant do anything with the request.
require is not intended to validate the presence of individual params - that is handled with model level validations in Rails.
If you really had to you could do:
def create_billing_address!(data)
keys = [:first_name, :last_name, :phone, :address_1, :city, :postcode, :country]
keys.each do |k|
raise ActionController::ParameterMissing and return unless data[k].present?
end
service_customer.create_address(customer_id, data.permit(*keys))
end
But thats just bad application design as you're letting the model level business logic creep into the controller.
permit and require are not interchangeable the way you think. If you establish that all the required keys are present, they still need to be permitted to be used in a mass assignment.
So you'd likely need to do...
def create_billing_address(data)
fields = %i(first_name last_name phone address_1 city postcode country)
address_data = data.permit(data.require(fields))
service_customer.create_address(customer_id, address_data)
end
The data.require will raise an ActionController::ParameterMissing exception if a key is missing, if not it will return the array of keys which can be used by permit.
Generally, what you want to do with require is more typically handled by model validation.
Documentation on require and permit is here...
http://api.rubyonrails.org/classes/ActionController/Parameters.html
Related
How can I manipulate params in a more DSL way per country where each country has its own logic for province variable.
I would like to organise it better:
- create a configuration file per per each country.
- if files not exist than there will be a default file
- and each file is a ruby file that province parameter can be manipulated via ruby code which gives flexibility.
currently I do it in in the controller like this:
before_filter :modify_location_params, :only => [:create]
def location_params
params.require(:location).permit(
origin: [:name, :country, :city, :state, :postal_code, :address1, :address2,:province],
destination: [:name, :country, :city, :state, :postal_code, :address1, :address2,:province],
)
end
def modify_location_params
[:origin, :destination].each do |location|
unless (params[:location][location][:country].downcase =~ /(Sweden|sw)/).nil?
params[:location][location][:province] = 'SW'
end
unless (params[:location][location][:country].downcase == 'IL' && some_other_condition == true
params[:location][location][:city] = 'OM'
params[:location][location][:name] = 'some name'
end
end
end
Yes, I can do it in a switch/if statements but I think that since I have a lot of countries it would be a better way of doing a DSL like system for this manipulating. any ideas how implement such?
Not totally sure I understand what you're trying to do, but if you just want different implementations of a similar method for each country you could make a class for each one and have them inherit from a parent country class. Something like
class Country
def as_origin
#default code
end
end
class Sweden < Country
def as_origin
#override default code here
end
end
There's also a bunch of good gems to help with country information if you want to avoid doing it all by hand
If I understand correctly you want to add different key-value pairs to a hash depending on the :country value in that hash. If so then a YAML file should work.
Say you have the following in a YAML file
# country_details.yaml
sweden:
province: 'SW'
il:
city: 'OM'
name: 'some name'
Then you can define a method
def country_details(country)
parsed_yaml = YAML::load(File.open('path/to/file'))
details = parsed_yaml[country]
end
that you use like so
details = country_details('SW')
params[:location][location].merge! details
I have been working with jQuery token input and Rails 4.
Devise gem Application controller
devise_parameter_sanitizer.for(:account_update) { |u| u.permit( :email, :first_name, :last_name,
:password, :password_confirmation, :current_password, :passion_tokens => [] ) }
I want to save users :passion_tokens, but not able to save it. I have mentioned it in controller as per strong parameters.
user.rb
attr_reader :passion_tokens
def passion_tokens= (ids)
self.author_ids = ids.split(",")
end
So, please provide possible way to save the data.
attr_reader does not write to the attributes, which is the probable cause of your attributes not saving. You should use attr_accessor if you want to both read and write or attr_writer if only writing to attribute is required.
Let's say that I have an input field with a value, and I want to validate it (on the server side) to make sure, for instance, that the field has at least 5 characters.
The problem is that it is not something that I want to save in the database, or build a model. I just want to check that the value validates.
In PHP with Laravel, validation is quite easy:
$validator = Validator::make($data, [
'email' => ['required', 'email'],
'message' => ['required']]);
if ($validator->fails()) { // Handle it... }
Is there anything similar in Rails, without need of ActiveRecord, or ActiveModel? Not every data sent from a form makes sense as a Model.
You can use ActiveModel::Validations like this
class MyClass
include ActiveModel::Validations
validates :email, presence: true
validates :message, presence: true
end
It will act as a normal model and you will be able to do my_object.valid? and my_object.errors.
Rails validations live in ActiveModel so doing it without ActiveModel seems kind of counter-productive. Now, if you can loosen that requirement a bit, it is definitely possible.
What I read you asking for, and as I read the PHP code doing, is a validator-object that can be configured on the fly.
We can for example build a validator class dynamically and use instance of that class to run our validations. I have opted for an API that looks similar to the PHP one here:
class DataValidator
def self.make(data, validations)
Class.new do
include ActiveModel::Validations
attr_reader(*validations.keys)
validations.each do |attribute, attribute_validations|
validates attribute, attribute_validations
end
def self.model_name
ActiveModel::Name.new(self, nil, "DataValidator::Validator")
end
def initialize(data)
data.each do |key, value|
self.instance_variable_set("##{key.to_sym}", value)
end
end
end.new(data)
end
end
Using DataValidator.make we can now build instances of classes with the specific validations that we need. For example in a controller:
validator = DataValidator.make(
params,
{
:email => {:presence => true},
:name => {:presence => true}
}
)
if validator.valid?
# Success
else
# Error
end
Apologies if this is a really common and/or ridiculous question; I swear I've read over the documentation multiple times and everything seems so focused on ActiveRecord to the point they've wandered off the path of forms that do something other than create or edit model data.
Take for example a form with inputs to control the extraction and display of some statistics. What does rails provide me with for validating the user input of this form, which won't be invoking save on any records? Things like:
:email must be an email address
:num_products must be a positive whole number
:gender must be one of "M" or "F"
:temperature must be between -10 and 120
Etc etc (the sort of stuff that comes standard in most web frameworks)...
Is there something in Rails for me to perform this arbitrary validation and some view helper to display a list of errors, or is everything coupled with ActiveRecord?
Apologies if I've overlooked this in the documentation, but this and this don't really cover it, at least as far as mt weary eyes can tell.
scratches head
Thanks to Rob's answer, here's what I've come up with. I created a utility class (aptly named Validator) which is just embedded into my controllers for anything that needs it.
module MyApp
class Validator
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
def initialize(attributes = {})
super
attributes.each { |n, v| send("#{n}=", v) if respond_to?("#{n}=") }
end
end
end
Now in the controller, for example, just define a little inline-class:
class LoginController < ApplicationController
class AuthenticationValidator < MyApp::Validator
attr_accessor :email
attr_accessor :password
validates_presence_of :email, :message => "E-mail is a required field"
validates_presence_of :password, :message => "Password cannot be blank"
end
def authenticate
if request.post?
#validator = AuthenticationValidator.new(params)
if #validator.valid?
# Do something meaningful
end
end
end
It feels a bit unnecessary to stick every single set of validation rules in their own .rb when the logic is more controller-oriented IMHO. There is probably a more succinct way to write this, but I'm pretty new to Ruby and Rails.
Yes, it can be done fairly easily.
You can use the validations API for it.
As an example, here is a contact us model that I use for an application that is not using ActiveRecord.
class ContactUs
include ActiveModel::Validations
include ActiveModel::Conversion
extend ActiveModel::Naming
attr_accessor :name, :email, :subject, :message
validates_presence_of :name, :email, :message, :subject
validates_format_of :email, :with => /\A[A-Za-z0-9._%+-]+#[A-Za-z0-9.-]+\.[A-Za-z]{2,4}\z/
def initialize(attributes=nil)
attributes.each do |name, value|
send("#{name}=", value)
end unless attributes.nil?
end
def persisted?
false
end
end
There is a valid method on model, which triggers validation.
so instead model.save, try model.valid?
I have a form that allows the user to send a message to an email, and I want to add validation to it. I do not have a model for this, only a controller. How should I do this in Rails?
I was considering doing the validation in the controller, and displaying the errors to the user using the flash object. Is there a better way of doing this?
The best approach would be to wrap up your pseudo-model in a class, and add the validations there. The Rails way states you shouldn't put model behavior on the controllers, the only validations there should be the ones that go with the request itself (authentication, authorization, etc.)
In Rails 2.3+, you can include ActiveRecord::Validations, with the little drawback that you have to define some methods the ActiveRecord layer expects. See this post for a deeper explanation. Code below adapted from that post:
require 'active_record/validations'
class Email
attr_accessor :name, :email
attr_accessor :errors
def initialize(*args)
# Create an Errors object, which is required by validations and to use some view methods.
#errors = ActiveRecord::Errors.new(self)
end
# Required method stubs
def save
end
def save!
end
def new_record?
false
end
def update_attribute
end
# Mix in that validation goodness!
include ActiveRecord::Validations
# Validations! =)
validates_presence_of :name
validates_format_of :email, :with => SOME_EMAIL_REGEXP
end
In Rails3, you have those sexy validations at your disposal :)
For Rails 3+, you should use ActiveModel::Validations to add Rails-style validations to a regular Ruby object.
From the docs:
Active Model Validations
Provides a full validation framework to your objects.
A minimal implementation could be:
class Person
include ActiveModel::Validations
attr_accessor :first_name, :last_name
validates_each :first_name, :last_name do |record, attr, value|
record.errors.add attr, 'starts with z.' if value.to_s[0] == ?z
end
end
Which provides you with the full standard validation stack that you
know from Active Record:
person = Person.new
person.valid? # => true
person.invalid? # => false
person.first_name = 'zoolander'
person.valid? # => false
person.invalid? # => true
person.errors.messages # => {first_name:["starts with z."]}
Note that ActiveModel::Validations automatically adds an errors method
to your instances initialized with a new ActiveModel::Errors object,
so there is no need for you to do this manually.