My Rails model has some bad lines. In general, first error is when accessing roles variable from has_access def. And then, second is when making where action.
class Organization < ActiveRecord::Base
has_and_belongs_to_many :organizations_users
belongs_to :user
validates :name, :presence => true, :length => { minimum: 4, maximum: 35 }, uniqueness: true
roles = { :administrator => 1, :support => 2 }
def has_access(chosen_user, role)
self.organizations_users.where('user_id = ? and role = ?', chosen_user.id, roles[role]).exists?
end
def add_user(chosen_user, role)
if !self.has_access(chosen_user, role)
self.organizations_users.create({user_id: chosen_user.id, role: roles[role]})
end
has_access(chosen_user,role)
end
end
I need also to query over organizations_users table to get information about access. How can I fix that?
Simplest fix would be to change
def has_access(chosen_user, role)
where('user_id = ? and role = ?', chosen_user.id, roles[role]).exists?
end
to:
def has_access(chosen_user, role)
Organization.where('user_id = ? and role = ?', chosen_user.id, roles[role]).exists?
end
The reason for this is the where() method is a class method and only accessible from the class itself, IE. Organization.where. BTW, I am assuming (dangerously :) ) That this where clause is used from Organization.
To find or add the organization do:
def has_access(role)
self.organizations_user.find_or_create_by_role(role)
end
This allow you to use:
user = User.has_access("administrator")
user = User.first.has_acess("administrator")
And will do a find in the table user_organizations or create a new entry if can't find one. ActiveRecord relation will take care of the user_id
Related
I'm new to Rails and I'm having a hard time understanding how to do the following:
a user creates an entry that has two values "Name" and "Position".
I want to check and see if that name + position combo already exists in the DB
If so, I want the db to spit an error that says that name and position already exist.
I thought it was going to be easy... by just writing:
class Rater < ApplicationRecord
validate :unique_position, :on => :create, :on => :edit
private
def unique_position
if Rater.where(name: name, position: position).any?
return false
end
end
end
I thought that by returning FALSE, the validate would not allow the action to continue, but I'm able to create duplicates. I've tried searching the net and this site, played around with different methods but not sure I'm using the incorrect keywords for what I'm trying to do.
Appreciative of any direction. 🙏
You need to add the validation error in the errors object instead of simply returning false when the validation fails.
class Rater < ApplicationRecord
validate :validate_name_and_position_uniqueness, on: [:create, :update]
private
def validate_name_and_position_uniqueness
return true unless Rater.exists?(name: name, position: position)
errors.add :base, 'Name and position already taken.'
end
end
You can use uniqueness validation for a column in scope of other column.
Here is the example that can help you.
validates :name, presence: true, uniqueness: { scope: :position }
OR
validates :name, uniqueness: { scope: :position }
*(In case the name field is not mandatory).
If the position is association then user position_id the foreign key with the scope.
ex: { scope: :position_id }.
I have this piece of example to create a new record through post which is passed
describe 'POST create' do
let(:schedule_child) { FactoryGirl.create(:schedule_child) }
let(:post_queue) { post :create, schedule_child_id: schedule_child.id, format: :js }
it { expect{post_queue}.to change(PatientQueue, :count).by(1) }
end
And I have one attribute, PatientQueue.queue_number, which will be increased by 1 every time a new record is added. Now I'd like to see if this attributes has changed.
it { expect{post_queue}.to change(PatientQueue, :queue_number).by(1) }
But here is what I got
NoMethodError: undefined method `queue_number' for #<Class:0x0000000849e780>
How should I write it properly?
== UPDATE ==
model PatientQueue
class PatientQueue < ActiveRecord::Base
# Validations
validates :patient, :schedule_child, presence: true
validate :is_not_exist
# Relations
belongs_to :schedule_child
belongs_to :patient
before_create :insert_queue_number
def is_exist?
PatientQueue.find_by_schedule_child_id_and_patient_id(schedule_child_id, patient_id).present?
end
private
def insert_queue_number
last_id = PatientQueue.where("schedule_child_id = ?", self.schedule_child_id).count
self.queue_number = last_id + 1
end
def is_not_exist
errors.add(:schedule_child, :is_exist) if is_exist?
end
end
PatientQueue is an activerecord class, which has a method count
post_queue is an instance of the class and has the method queue_number
the class does not have the same methods as the instance, so you might write your test like change(post_queue, :queue_number).by(1)
However, the test is a little hard to follow, can you show us your data model relationships? if a PatientQueue has_many schedule_child, maybe you just want to use rails cache_counter? http://www.elegantruby.com/Tutorials/2013/01/25/adding-a-counter-cache-for-fun-and-profit/
I have some custom validations on my reservation_start and reservation_end attributes on my model
class DateRangeValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.date_ranges.each do |range|
if range.include? value
record.errors.add(attribute, "#{options[:name]} not available")
end
end
end
end
The Reservation model:
class Reservation < ActiveRecord::Base
belongs_to :user
belongs_to :transport
validates_presence_of :user, :transport, :reservation_start, :reservation_end
validates :reservation_start,
date_range: {name: "Start date"},
date_past: true
validates :reservation_end,
date_range: {name: "End date"},
date_subset: true,
start_bigger: true
def date_ranges
reservations = Reservation.where(["transport_id =?", transport_id])
reservations.map { |e| e.reservation_start..e.reservation_end }
end
end
The problem with this is that if I want to for example, edit a record to decrease the reservation_end by one day the validations kicks in. I tried setting the validation to on: :create but that is not a good idea.
What is the best way to proceed?
My initial thought is to change your query in date_ranges to exclude the current record, should it already be persisted (ie. the id field has been populated). It would look something like:
def date_ranges
reservations = Reservation.where({transport_id: transport_id})
reservations = reservations.where(['id <> ?', id]) if !id.nil?
reservations.map { |e| e.reservation_start..e.reservation_end }
end
The line reservations = reservations.where(['id <> ?', id]) if !id.nil? should exclude the current record iff (if and only if) it has already been persisted, in which case the validation should pass when editing a previously created Reservation so long as it does not overlap any other Reservation.
I recently added a new field called "Review" in a column called "Posts"
I want to display only the posts where Review = false.
I am trying to add the correct scope into my Posts model to do this.
Here's the current scope that I have.
default_scope order: 'posts.created_at DESC'
Model < ActiveRecord::Base
def self.without_review
where(review: false) # or how ever you have the boolean in your db
end
end
And of course a spec
describe Model do
describe "::without_review" do
it 'loads posts without reviews' do
no_review = Model.create(review: false)
Model.create(review: true)
Model.without_reivew.all.should == [no_review]
end
end
end
Just use a class method and chain scopes
You could create a named scope:
scope :unreviewed, where(:review => false)
Assuming that I have a class such as the following:
class Book < ActiveRecord::Base
validates :title, :length => {:maximum => 10}
end
Is there a way (gem to install?) that I can have ActiveRecord automatically truncate values according to maximum length?
For instance, when I write:
b = Book.new
b.title = "123456789012345" # this is longer than maximum length of title 10
b.save
should save and return true?
If there is not such a way, how would you suggest that I proceed facing such a problem more generally?
Well, if you want the value truncated if its too long, you don't really need a validation, because it will always pass. I'd handle that like this:
class Book < ActiveRecord::Base
before_save :truncate_values
def truncate_values
self.title = self.title[0..9] if self.title.length > 10
end
end
I have come up with a new validator that does truncation. Here is how I did that:
I created the "validators" folder inside "app" folder and then created the file "length_truncate_validator.rb" with the following content:
class LengthTruncateValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
ml = options[:maximum]
record.send("#{attribute}=", value.mb_chars.slice(0,ml)) if value.mb_chars.length > ml unless value.nil? or ml.nil?
end
class << self
def maximum(record_class, attribute)
ltv = record_class.validators_on(attribute).detect { |v| v.is_a?(LengthTruncateValidator) }
ltv.options[:maximum] unless ltv.nil?
end
end
end
And inside my model class I have something like:
class Book < ActiveRecord::Base
validates :title, :length_truncate => {:maximum => 10}
end
which is quite handy and works the way I require.
But still, if you think that this one can be improved or done in another way, you are welcome.
This may not have been an option in 2011, but now there's a before_validation callback that will work.
class Book < ApplicationRecord
before_validation do
if self.params && self.params.length > 1000
self.params = self.title[0...10]
end
end
validate :title, length: { maximum: 10 }, allow_nil: true
end
I like the idea of using the before_validation callback. Here's my stab that automatically truncates all strings to within the database's limit
before_validation :truncate_strings
def truncate_strings
self.class.columns.each do |column|
next if column.type != :string
if self[column.name].length > column.limit
self[column.name] = self[column.name][0...column.limit]
end
end
end