I have a code which calculates a parameter in the create action and update action differently. Here expiry_time is the column in the db and expiry_duration is a virtual parameter.
class SomeController
def create
params[:model][:expiry_time] = Time.now + params[:model][:expiry_duration].to_i
#model = Model.new(params[:model])
if #model.save
redirect_to ..
else
render ..
end
end
def update
#model = Model.find(params[:id])
params[:model][:expiry_time] = #model.created_at + params[:model][:expiry_duration].to_i
if #model.update_params(params[:model])
redirect_to ..
else
render ..
end
end
end
I was thinking of moving the calculation part to model in a before_save function. Should I check for the id there to decide if it is a new record or an existing one like this?
class Model
before_save :update_expiry_time
def update_expiry_time
start_time = id ? created_at : Time.now
expiry_time = start_time + expiry_duration.to_i
end
def expiry_duration
expiry_time - created_at
end
end
What do you think?
use new_record?, or use :on => :create to call a different funciton
Related
I have 2 models .. one for User(amitian) and other about
They have a has_one and belongs_to association
Problem is that whenever I create a new about it works fine and set my amitian_id to current_amitian but whenever I go to my new page again.. it updates my amitian_id to NULL
for eg.
this is my sql after I submit the form
insert into about_amitian('values' , amitian_id = 1)
and if i go to new page again it says
update about_amitian set amitian_id = null where about_amitian.id= 1
this is my controller and model
class AboutAmitiansController < ApplicationController
before_action :authenticate_amitian!
def new
#amitian = current_amitian
#about_amitian = #amitian.build_about_amitian
end
def create
#amitian = current_amitian
#about_amitian = #amitian.create_about_amitian(about_amitian_params)
if #about_amitian.save
redirect_to root_url
flash[:notice] = 'success'
else
render root_url
end
end
private
def about_amitian_params
params.require(:about_amitian).permit(:dob,:interest,:bio,:catch_phrase,:relationship_status)
end
end
and model
belongs_to :amitian
and in amitian
has_one :about_amitian
Why is it Updating my database ?
I'm trying to redirect users to the next instance of my WordExposition model after update. What I have currently works for immediately-adjacent word_exposition id's, but raises RecordNotFound if the next lesson's word_exposition's ID skips (i.e. it will redirect properly between id's 1-4, but will break if the next id is 6). How can I get it to redirect also for those non-adjacent WordExposition instances that belong to the same lesson?
I based the next_exposition model method on the ideas from this post, but I'm missing something to get it to work here.
WordExposition model:
class WordExposition < ActiveRecord::Base
belongs_to :enrollment
belongs_to :word
def next_exposition
WordExposition.where(["id > ? AND enrollment_id = ?", id, enrollment_id]).first
end
end
WordExpositions controller:
class WordExpositionsController < ApplicationController
def update
current_word_exposition
#current_word_exposition.completed = true
#current_word_exposition.term_given_by_student = params[:word_exposition][:term_given_by_student]
if #current_word_exposition.save
flash[:notice] = "Congratulations!"
#currently only redirects correctly for adjacent words in the same lesson, should do so for non-adjacent word_expositions in the same lesson
if next_word = #current_word_exposition.next_exposition
redirect_to lesson_word_exposition_path(current_lesson, next_word)
end
else
flash[:alert] = "Enter the word exactly as shown!"
redirect_to lesson_word_exposition_path(current_lesson, current_word_exposition)
end
end
private
helper_method :current_lesson
def current_lesson
#current_lesson ||= Lesson.find(params[:lesson_id])
end
helper_method :current_enrollment
def current_enrollment
#current_enrollment ||= Enrollment.find_by!(lesson_id: params[:lesson_id], user_id: current_user.id)
end
def word_exposition_params
params.require(:word_exposition).permit(:completed)
end
helper_method :current_word_exposition
def current_word_exposition
#current_word_exposition ||= current_enrollment.word_expositions.find_by!(word_id: params[:id])
end
end
You can try this
def next_exposition
WordExposition.where('id = (select min(id) from word_expositions where id > ?)', self.id).first
end
I'm using RSpec and FactoryGirl for testing my models and I'm stuck at "highest_priority" method which can't be seen by RSpec for some reason.
Here's the method itself:
models/task.rb
class Task < ActiveRecord::Base
#some stuff
def self.highest_priority
p = Task.order(:priority).last.try(:priority)
p ? p + 1 : 1
end
end
And when I run task_spec.rb
require 'spec_helper'
describe Task do
it "returns highest priority" do
last_task = FactoryGirl.build(:task, priority: "5")
last_task.highest_priority
expect(last_task(:priority)).to eq("6")
end
end
I get the following error:
When I'm calling this method in my controller like this
def create
#task = current_user.tasks.build(task_params)
#task.highest_priority
#task.complete = false
respond_to do |format|
if #task.save
format.js
else
format.js
end
end
end
And the method looks like
def highest_priority
self.maximum(:priority).to_i + 1
end
I'm getting
First of all, you better use ActiveRecord's maximum instead of ordering and then picking one, you'll avoid the instance initialization and get a number directly from the query
Task.maximum(:priority)
this could be put in a class method like this
def self.maximum_priority
Task.maximum(:priority) || 0 # fall back to zero if no maximum exists
end
Then for the second half which is updating the method, i would create an instance method for that, and using the class method
def set_maximum_priority
self.priority = self.class.maximum_priority + 1
self
end
Note that I returned self at the end for chainability
Then your action would become something like this
def create
#task = current_user.tasks.build(task_params).set_maximum_priority
#task.complete = false
...
end
You need to create the method as an instance method of Task model. Like below :
class Task < ActiveRecord::Base
#some stuff
def highest_priority
p = Task.order(:priority).last.try(:priority)
p ? p + 1 : 1
end
end
I'm trying to combine start_date, start hour, and start_minute virtual attributes from my Event form, in order to create a start_datetime attribute (which is stored in the database).
I have (via STI) several subclasses of Event; let's call them TrainingSession and WorkSession and PersonalTime.
Classes are structured as such:
class Event < ActiveRecord::Base
...
end
class TrainingSession < Event
...
end
class WorkSession < Event
...
end
class PersonalTime < Event
...
end
The relevant parts of event.rb:
class Event < ActiveRecord::Base
attr_accessor :start_date, :start_hour, :start_minute
validates :start_datetime, :presence => true
before_validation :merge_attributes_for_datetime_string
def merge_attributes_for_datetime_string
start_datetime_string = "#{ start_date } #{ start_hour }:#{ start_minute }:00"
end
def start_datetime=(start_datetime_string)
self.start_datetime = start_datetime_string
end
def start_date
start_datetime.strftime("%d") if start_datetime?
end
def start_hour
start_datetime.strftime("%H") if start_datetime?
end
def start_minute
start_datetime.strftime("%M") if start_datetime?
end
end
... and of events_controller.rb:
def create
#event = Event.new(event_params)
if #event.save
redirect_to :root, :flash => { :success => "Event added." }
else
redirect_to :back, :flash => { :notice => "There was an error creating the event." }
end
end
private
def event_params
params.require(:event).permit(
:type,
:start_datetime,
:start_date,
:start_hour,
:start_minute,
...
)
end
def training_session_params
params.require(:training_session).permit(
...
)
end
def work_session_params
params.require(:work_session).permit(
...
)
end
def personal_time_params
params.require(:personal_time).permit(
...
)
end
I've verified in my server logs that the correct params are being sent from the form:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"<TOKEN HERE>=", "event"=>{"start_date" => "2013-08-23", "start_hour"=>"15", "start_minute"=>"00", "type"=>"PersonalTime"}, "commit"=>"Add Personal Time"}
Yet every time I try to create an Event (of any type), I get the notice There was an error creating the event. (as per my create method). If I comment out validates :start_datetime, the event is created, but with start_datetime of nil.
This has to mean the start_datetime string isn't being properly merged from the virtual attributes, but I can't figure out why.
What am I missing here? Is there a better way to set start_datetime?
Based on what you've posted, I don't see where you are calling the start_datetime method.
Instead of defining a new method, you could do the merging in your start_datetime method as follows:
before_validation :merge_attributes_for_datetime_string
def merge_attributes_for_datetime_string
self.start_datetime = "#{ start_date } #{ start_hour }:#{ start_minute }:00"
end
I'm trying to make attributes equal predetermined values, and I'm not sure if I'm doing that efficiently with the following (in my orders controller):
def create
#order = Order.find(params[:id])
#order.price = 5.99
#order.representative = Product.find(params[:product_id]).representative
#order.shipping_location = SHIPPING_LOCATION
#order.user = current_user
respond_to do |format|
...
end
end
Is there a more efficient way to equate attributes in Rails (maybe using models)? If I'm using two different controllers, do I just repeat what I did above for the new controller?
Use before_create callback in model to assign default values.
Your code is a little off, it looks like a controller action for create, but the code reads like it's for an update.
Regardless...
You could use a parameter hash to update everything at once.
In the case where you're creating:
order_update = {:price => 5.99, :representative =>
Product.find(params[:product_id]).representative,
:shipping_location => SHIPPING_LOCATION,
:user => current_user}
#order = Order.new(order_update)
In the case where you're updating:
#order.update_attributes(order_update) #attempts to save.
Mixing it into your controller code we get:
def create
#order = Order.find(params[:id])
order_update = {:price => 5.99, :representative =>
Product.find(params[:product_id]).representative,
:shipping_location => SHIPPING_LOCATION,
:user => current_user}
respond_to do |format|
if #order.update_attributes(order_update)
# save succeeded. Redirect.
else
# save failed. Render with errors.
end
end
end
Another solution:
class Example < ActiveRecord::Base
DEFAULTS = HashWithIndifferentAccess.new(:some => 'default', :values => 'here')
def initialize(params = {})
super(DEFAULTS.merge(params))
end
end
Either use initialize and merge with params, or use an ActiveRecord hook like before_create etc.