how to transfer this code to model? - ruby-on-rails

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

going to new page sets my user_id to null in Rails

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 ?

redirect_to next instance on update

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

Getting undefined method error in RSpec

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

Combining virtual attributes to single class attributes in super class (STI)

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

Ruby on Rails: Equating items in controllers (or maybe models?)

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.

Resources