how to query based on boolean attribute in rails? - ruby-on-rails

I'm trying to query only objects from the database whose boolean attribute "in_season" returns true and its not working.
class Item < ApplicationRecord
validates :name, presence: true, uniqueness: true
validates :price, presence: true, numericality: true
validates :quantity, presence: true, numericality: { only_integer: true }
validates :description, presence: true
def self.in_season
where("in_season = ?", "true")
end
end
class ItemsController < ApplicationController
def index
#items = Item.in_season
end
end
I feel like everything is set up correctly and I'm not getting any errors, but my index page is blank because none of the items are being queried correctly.

You need to fix the syntax. Your mistake is that you're passing a string "true", whereas you want to pass boolean value true.
def self.in_season
where(in_season: true)
end
It's more Railsy to use scope for such needs:
scope :in_season, -> { where(in_season: true) }
Read more about scoping in Rails Guides on scopes.

Related

Rails changing a proc to a method to validate if present in model

I have a model that has a long list of attributes. Not all attributes need to be present to save, but, if they are present I want to validate them.
Currently I am adding an if: proc to the end of each validation as such:
# app/models/bio.rb
class Bio < ApplicationRecord
...
validates :age, numericality: { only_integer: true }, if: Proc.new { |a| a.age.present? }
validates :height, numericality: { only_integer: true }, if: Proc.new { |a| a.height.present? }
validates :weight, numericality: { only_integer: true }, if: Proc.new { |a| a.weight.present? }
...
end
This doesn't seem very DRY, so, I wanted to move this proc to a class method and call it via it's symbol as:
...
validates :age, numericality: { only_integer: true }, if: :has_attr(:age)
...
def has_attr(a)
#some_code.a.present?
end
I'm just unsure what the a in the proc refers to. I am assuming it's self or something similar.
How do I need to modify the method to get it to work the same as the proc? And do I need to add a ? to the method name, such as def has_attr?(a) or def has_attr(a)?? And, honestly, I'm not even sure if you can pass an argument to a symbol. Any light you can shed on this is greatly appreciated.
You can't do it that way, the :if option wants something callable (such as a Proc or lambda) or a symbol which names an instance method. There's no way to tunnel arguments down to the instance method unless you want to wrap it as in your first example.
However, in this case, you can say allow_nil: true which will have the same effect:
validates :age, numericality: { only_integer: true }, allow_nil: true
# ----------------------------------------------------^^^^^^^^^^^^^^^
Adding that will skip the validation if the age is nil and that should have the same effect as your current logic.
You could do something close to what you were trying to do with a class method that returns lambdas. Something like:
def self.has_attr(a)
->(obj) { obj.public_send(a).present? }
end
and then:
validates :age, ..., if: has_attr(:age)
You'd need to arrange for self.has_attr to be defined before your validates calls but that's not too hard, you'd probably put has_attr in a concern and include it at the top of your model class to deal with that problem and make has_attr easily available in other models as well.

How to access the parameter being validated

In the following example, is there a way to retrieve the name of the parameter being currently validated inside the if proc ?
class MyModel < ActiveRecord::Base
with_options if: proc{|o| "how to retreive the parameter being validated here?"} do
validates :param_1, presence: true
validates :param_2, presence: true
end
end
I would like to avoid this kind of solution:
class MyModel < ActiveRecord::Base
validates :param_1, presence: true, if: proc{|o| o.need_validation?(:param_1)}
validates :param_2, presence: true, if: proc{|o| o.need_validation?(:param_2)}
end
If you wish to know name, and other data like option to validation, use validators:
app/validators/param_validator.rb:
ParamValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
# your validation code here...
end
end
The arguments say about themself.
Use it in a model:
validates :param_1, param: true
validates :param_2, param: true
If each of the validations are identical apart from the name, you could iterate over them:
class MyModel < ActiveRecord::Base
[:param1,:param2].each do |param|
validates param, presence: true, if: proc{|o| o.need_validation?(param) }
end
end
I'm pretty sure there is no easy answer to my question, I'll find an other way.

Overriding presence true in User model

I have the following in my models/user.rb:
validates :company, presence: true
validates :title, presence: true
I have a secondary view where I want to create a user but not require this user to enter a company and a title. How would I do that without modifying the main user.rb?
This is for Rails 3.2
You can do by declaring custom validations the way #BroiSatse has answered or when saving the user you can pass validate: false as argument, do this way
#user.save(:validate => false)
I usually do sth like:
class User < AR::Base
validates :company, :title, presence: true, if: :validate_company_and_title?
def validate_company_and_title?
#validate_company_and_title.nil? || #validate_company_and_title
end
def skip_company_and_title_validation!
#validate_company_and_title = false
end
end
Then in your controller create action for given view you can do:
#user.skip_company_and_title_validation!

Rails create a method accessible via both the model and controller

Is there anyway to create a function I can call both in the model and controller? I have a function that grabs an array of files and strips the extension off and want to validate against the list. However I also want access to this list in the controller so I can output a select box for the options. I currently have this, however the VALID_MODULES doesnt get populated all the time.
class Job < ActiveRecord::Base
after_initialize :init
VALID_MODULES =[];
validates :name, presence: true
validates :desc, presence: true
validates :api, presence: true, :inclusion => { :in => VALID_MODULES}
validates :filters, presence: true
validates :toe, presence: true
def init
Dir.foreach('lib/resources') do |item|
next if item == '.' or item == '..' or item == 'resource.rb'
#Wont be called very often so O(n) complexity is fine (small #elements)
VALID_MODULES.push(item[0..-4]) unless VALID_MODULES.include?(item[0..-4])
end
end
end
Instead of using a constant (VALID_MODULES), try making it an attribute of your job.
class Job < ActiveRecord::Base
attr_accessor :valid_modules
after_initialize :init
validates :name, presence: true
validates :desc, presence: true
validates :api, presence: true, :inclusion => { :in => VALID_MODULES}
validates :filters, presence: true
validates :toe, presence: true
def init
#valid_modules ||= []
Dir.foreach('lib/resources') do |item|
next if ['.', '..', 'resource.rb'].include?(item)
#Wont be called very often so O(n) complexity is fine (small #elements)
#valid_modules << item[0..-4] unless #valid_modules.include?(item[0..-4])
end
end
end
Now in your controller you can just call valid_modules on your Job object to return the array.
Example:
job = Job.new
job.valid_modules
# in config/initializers/api_modules.rb
module ApiModules
def self.modules
# the Dir[""] glob syntax here I believe exclude dot directories
# but I still like the Array#include? syntax here for your exclusions list
# you may need to massage the strings of your file list to be more appropriate to your case
#modules ||= Dir["lib/*"].select{|f| !["resource.rb"].include?(f) }
end
end
#app/models/job.rb
validates :api, presence: true, :inclusion => { :in => ApiModules.modules}

Default value along with uniqueness validation

I have a Path model with name attribute as unique. I want to set default value as '/'
to the same.
I have done in the following manner.
class Path < ActiveRecord::Base
attr_accessible :name
validates :name, presence: true, uniqueness: true
before_validation :set_default_path
private
def set_default_path
self.name = name.presence || '/'
end
end
Domain model is designed as:
class Domain < ActiveRecord::Base
attr_accessible :name, path_id
validates :name, :path_id, presence: true
validates :name, uniqueness: {scope: :path_id}
end
But this doesn't work for consecutive inserts with a blank name for path.
path = Path.find_or_create_by_name('')
domain = Domain.new(name: 'stackoverflow.com')
domain.path = path
domain.save! # Fails with validation error
ActiveRecord::RecordInvalid:
Validation failed: Path can't be blank
Is there a robust way to achieve this ?
You should remove following callback
before_validation :set_default_path
and use validation for name as following:--
validates :name, presence: true, uniqueness: true, :if => 'name.present?'
and write a migration file to add default value to name attribute of paths table as either of followings:--
change_column :paths, :name, :string, :default => '/'
or
change_column_default :paths, :name, '/'
add condition on validation:
validates :name, presence: true
validates :name, uniqueness: true, unless: proc { |e| e.name == "/" }

Resources