I want to return an object(#state) along with true or false from rails method in application helper.
I have a model named User which calls an action which is in applicaiton helper.
class User
include ApplicationHelper
*****-----_********
def user_state
user = self
state = get_state(user)
end
end
in applicaiton helper,
def get_state (user)
#user = User.find(user.id)
#state = UserState.where('user_id = ?',#user.id)
if #state.present?
*** here I want to return #state along with true ****
return true
else
return false
end
end
I tried 'return #state and true' didn't work but, 'return #state' worked.
Seems to me either object or a boolean can be returned and not both at once.
Is there any way to return both of them. Thankyou in advance.
Well, I'm not sure a "Helper" is what you need here, but returning multiple values from a ruby method is pretty easy:
def myfun(x)
if x
[true, "one"] # or use return true, "one"
else
false
end
end
a, b = myfun(true)
p a, b # prints true, "one"
a, b = myfun(false)
p a, b # prints false, nil
Related
In my application, 'Users' has_many 'Jobs', through 'applications'
I'm trying to create a helper method has_job(#user, #job). Where, it will return true if the user has already associated itself to a particular job.
When I'm doing this though, if I apply to 1 job, then it returns true for all other jobs.
Why is this happening?
This is what my helper method looks like ->
def has_job(user,current_job)
if user.applications.any?
user.applications.each do |application|
return true if application.job_id = current_job.id
end
end
false
end
return true if application.job_id == current_job.id
The single = would cause it to return true every time.
I create a Subscription for a User via the method .create_subscription(plan_title).
That method checks that it's possible to subscribe (not oversubscribed plan or archived subscription) via the method .plan_subscribable?(plan).
This method does either return true or false, but I would like it to return an error message as well that could be passed to the user if false.
How and where do I implement these validation errors?
class User
def plan_subscribable?(plan)
users_subscribed = Subscription.where(plan_id: plan.id).size
return false unless users_subscribed <= plan.quantity
return false unless plan.archived == false
return true
end
def create_subscription(plan_title)
plan = Plan.where(title: plan_title).first
if plan_subscribable?(plan)
Subscription.create(...)
end
end
end
You could modify plan_subscribable? to return a boolean true or a string containing the specific error message:
def plan_subscribable?(plan)
return 'The number of users cannot be higher than the plan quantity' unless users_subscribed <= plan.quantity
return 'Archived plans are not subscribable' unless plan.archived == false
return true
end
Then, evaluate whether the returned value from plan_subscribable? is true. If it is not, the statement is implicitly false and you can use the returned value as the error message:
def create_subscription(plan_title)
plan = Plan.where(title: plan_title).first
subscribable_or_error = plan_subscribable?(plan)
if subscribable_or_error === true
Subscription.create(...)
else
error_message = subscribable_or_error
end
end
I have Coupon model and in this model file I have a suitable_for_use method.I want to list Coupons if coupon.suitable_for_use == true.Is there any short way to do this ? I wrote this code but it doesn't work.
#coupons = []
coupons = Coupon.all.each do |coupon|
if coupon.suitable_for_use
#coupons << coupon
end
end
#coupons = coupons
suitable_for_use method
def suitable_for_use
result = true
if is_used?
result = false
elsif self.start > Time.now.in_time_zone
result = false
elsif self.end < Time.now.in_time_zone
result = false
end
return result
end
The problem is your assigning twice to #coupons. The return value from each is the collection it was given. So your last line reassigns the original set of coupons returned by Coupon.all.
#coupons = Coupon.all.select(&:suitable_for_use)
If your not sure what that does, here's the expanded version.
#coupons = Coupon.all.select {|coupon| coupon.suitable_for_select}
Basically, select takes a block that it will iterate over and if the block returns true then it will add that element to the returned collection. So any coupon that returns false will not be returned by select.
The &:suitable_for_use is called a symbol to proc. It literally expands to the block in the second line and is pretty common in ruby one-liners.
I'm making an task-manager and have an boolean attribute for 'finished'. I've tried to override the setter to implement an 'finished_at' date when i toggle 'finished' to true.
But i getting some mixed result. It doesn't work in browser but it will work in my rspec test.
Please help me out.
class TasksController < ApplicationController
# ...
def update
# ..
if #task.update_attributes(params[:task]) # where params[:task][:finished] is true
# ...
end
class Task < ActiveRecord::Base
#...
def finished=(f)
write_attribute :finished, f
write_attribute :finished_at, f == true ? DateTime.now : nil
end
end
# and in rspec i have
describe "when marked as finished" do
before { #task.update_attributes(finished: true) }
its(:finished_at) { should_not be_nil }
its(:finished_at) { should > (DateTime.now - 1.minute) }
describe "and then marked as unfinished" do
before { #task.update_attributes(finished: false) }
its(:finished_at) { should be_nil }
end
end
in browser it executes "UPDATE "tasks" SET "finished" = 't', "updated_at" = '2012-10-02 18:55:07.220361' WHERE "tasks"."id" = 17"
and in rails console i got the same with update_attributes.
But in rspec with update_attributes i get "UPDATE "tasks" SET "finished" = 't', "finished_at" = '2012-10-02 18:36:47.725813', "updated_at" = '2012-10-02 18:36:51.607143' WHERE "tasks"."id" = 1"
So I use the same method but it's only working in rspec for some reson...
using latest rails and latest spec (not any rc or beta).
Solution
Not mush i did need to edit. Thanks #Frederick Cheung for the hint.
I did notice i did like "self[:attr]" more than "write_attribute". Looks better imo.
def finished=(value)
self[:finished] = value
self[:finished_at] = (self.finished? ? Time.now.utc : nil)
end
Your setter is passed the values as they are passed to update_attributes. In particular when this is triggered by a form submission (and assuming you are using the regular rails form helpers) f will actually be "0" or "1", so the comparison with true will always be false.
The easiest thing would be to check the value of finished? after the first call to write_attribute, so that rails can convert the submitted value to true/false. It's also unrubyish to do == true - this will break if the thing you are testing returns a truthy value rather than actually true (for example =~ on strings returns an integer when there is a match)
You could use ActiveRecord Dirty Tracking to be notified of this change.
http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
class Task < ActiveRecord::Base
before_save :toggle_finished_at
def toggle_finished_at
if finished_changed?
before = changes['finished'][0]
after = changes['finished'][1]
# transition from finished => not-finished
if before == true && after == false
self.finished_at = nil
end
# transition from not finished => finished
if before == false && after == true
self.finished_at = Time.now.utc
end
end
end
end
This is a use case for a state machine. You call a :finish! event (a method) which is configured to change the state and to do whatever else needed.
https://github.com/pluginaweek/state_machine/
I'm trying to trigger a warning when a price is entered too low. But for some reason, it always returns true and I see the warning regardless. I'm sure there something wrong in the way I'm doing this as I'm really new to RoR.
In model:
def self.too_low(value)
res = Class.find_by_sql("SELECT price ……. WHERE value = '#{value}'")
res.each do |v|
if #{value} < v.price.round(2)
return true
else
return false
end
end
end
In controller:
#too_low = Class.too_low(params[:amount])
if #too_low == true
flash[:warning] = 'Price is too low.'
end
I would write it somewhat different. You iterate over all items, but you are only interested in the first element. You return from inside the iteration block, but for each element the block will be executed. In ruby 1.9.2 this gives an error.
Also i would propose using a different class-name (Class is used to define a class)
So my suggestion:
Class YourGoodClassName
def self.too_low(amount)
res = YourGoodClassName.find_by_sql(...)
if res.size > 0
res[0].price.round(2) < 1.00
else
true
end
end
end
You can see i test if any result is found, and if it is i just return the value of the test (which is true or false); and return true if no price was found.
In the controller you write something like
flash[:warning] = 'Price is too low' if YourGoodClassName.too_low(params[:amount])