I was wondering if it is possible to create a method partial in ruby on rails, for example I have this code;-
#cart = Cart.where(:user_id => current_user.id).first if user_signed_in?
#slots = #cart.slots.first
#slot_list = [#slots.slot_one, #slots.slot_two, #slots.slot_three, #slots.slot_four, #slots.slot_five,
#slots.slot_six, #slots.slot_seven, #slots.slot_eight, #slots.slot_nine, #slots.slot_ten]
#user_products = []
#product = []
#slot_list.each do |item|
if item.nil?
p 'Item empty'
else
#product << item
end
end
#product.each do |item|
items = Product.where(:product_id => item).first
#user_products << items
end
Written in multiple methods to get the #user_products, I was wondering if there was a way so I don't have to write this all the time and possibly run a method or use a partial?
Would it be worth creating a helper that does this and returns the #user_products variable?
I took my own advice and created two helpers, one to return the #user_products and another to return the #total.
I added the names of the methods to our helper_method
helper_method :user_is_admin?, :authenticate_admin!, :product_available?, :get_user_products!, :get_user_total!
then added these two methods at the bottom of the file;-
get_user_products!
def get_user_products!
#cart = Cart.where(:user_id => current_user.id).first if user_signed_in?
#slots = #cart.slots.first
#slot_list = [#slots.slot_one, #slots.slot_two, #slots.slot_three, #slots.slot_four, #slots.slot_five,
#slots.slot_six, #slots.slot_seven, #slots.slot_eight, #slots.slot_nine, #slots.slot_ten]
#user_products = []
#product = []
#slot_list.each do |item|
if item.nil?
p 'Item empty'
else
#product << item
end
end
#product.each do |item|
items = Product.where(:product_id => item).first
#user_products << items
end
return #user_products
end
get_user_total!
def get_user_total!
#total = 0
#cart = Cart.where(:user_id => current_user.id).first if user_signed_in?
#slots = #cart.slots.first
#slot_list = [#slots.slot_one, #slots.slot_two, #slots.slot_three, #slots.slot_four, #slots.slot_five,
#slots.slot_six, #slots.slot_seven, #slots.slot_eight, #slots.slot_nine, #slots.slot_ten]
#user_products = []
#product = []
#slot_list.each do |item|
if item.nil?
p 'Item empty'
else
#product << item
end
end
#product.each do |item|
items = Product.where(:product_id => item).first
#user_products << items
end
#user_products.each do |p|
#total += p.product_price
end
return #total
end
To use these methods inside whatever controller you then do the following;-
#user_products = get_user_products!
#total = get_user_total!
I assume this is in a controller?
What you want is to use plain old Ruby objects (POROs). So, you might have something like this:
class UserProducts
class << self
def get(options={})
#cart = Cart.where(:user_id => current_user.id).first if user_signed_in?
#slots = #cart.slots.first
#slot_list = [
#slots.slot_one,
#slots.slot_two,
#slots.slot_three,
#slots.slot_four,
#slots.slot_five,
#slots.slot_six,
#slots.slot_seven,
#slots.slot_eight,
#slots.slot_nine,
#slots.slot_ten
]
#user_products = []
#product = []
#slot_list.each do |item|
if item.nil?
p 'Item empty'
else
#product << item
end
end
#product.each do |item|
items = Product.where(:product_id => item).first
#user_products << items
end
end
end
Then, in your controller, you'd do something like:
class FooController < ApplicationController
def index
UserProducts.get(user_id: current_user.id)
end
end
So, UserProducts is essentially a service object. I think some people call them use cases. I tend to call them 'managers'. I put them in their own directory as app/managers/user_products.rb.
Related
I built a working shopping cart system the other day using Ruby on Rails, and with the guidance of following a tutorial. Now I would like to modify the existing shopping cart item to incorporate the size of a particular item being placed in the cart.
I created a migration file to add a size column to the Products table, and then I started modifying CartItem class, and the Cart class files respectively.
class CartItem
attr_reader :product_id, :quantity, :size
def initialize product_id, quantity = 1, size
#product_id = product_id
#quantity = quantity
#size = size
end
def increment
#quantity = #quantity + 1
end
def product
Product.find product_id
end
def total_price
# puts "Hello cart_item"
product.price * quantity
end
end
class Cart
attr_reader :items
def self.build_from_hash hash
items = if hash["cart"] then
hash["cart"]["items"].map do |item_data|
CartItem.new item_data["product_id"], item_data["quantity"], item_data["size"]
end
else
[]
end
new items
end
def initialize items = []
#items = items
end
def add_item product_id, size
item = #items.find { |item| item.product_id == product_id
item.size == size }
if item
item.increment
else
#items << CartItem.new(product_id, size)
end
end
def empty?
#items.empty?
end
def count
#items.length
end
def serialize
items = #items.map do |item|
{
"product_id" => item.product_id,
"quantity" => item.quantity,
"size" => item.size
}
end
{
"items" => items
}
end
def total_price(shipping_price = 0)
# puts "Hello cart"
#items.inject(0) { |sum, item| sum + item.total_price } + shipping_price
end
end
However, I'm getting the following error,
Because your add_item method should be into two parameter, but your params is hash can't use params[:id, :size] replace params[:id], params[:size] it work.
I am trying to search through my model using 3 columns. Also if the column is empty, it is valid. This is how I am doing it
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#todaysactivities = []
#activities=[]
#finaldata = []
#activities = Weatherclockactivity.all
#attemptactivities = []
#attemptactivities = #user.attempts
for activity in #activities do
logger.debug "activity: #{activity.attributes.inspect}"
if #temp.to_i < activity.temperatureMax.to_i && #temp.to_i > activity.temperatuureMin.to_i
if #sky == activity.sky || activity.sky == ""
if #day == activity.day
#todaysactivities << activity
end
end
end
end
for activity in #todaysactivities
for attempt in #attemptactivities
if attempt == activity
finaldata << {activity: activity, attempt: "yes"}
else
finaldata << {activity: activity, attempt: "no"}
end
end
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
The response I get is an empty array but I should be getting 3 rows as a response.
spelling mistake here
activity.temperatuureMin.to_i
And
finaldata << {activity: activity, attempt: "yes"}
should be
#finaldata << {activity: activity, attempt: "yes"}
Also you could be more concise
def getactivityfortoday
#temp = params[:temp]
logger.debug "params temp:#{#temp.inspect}"
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
#finaldata = #activities.map do |activity|
if (activity.temperatureMin.to_i + 1...activity.temperatureMax.to_i).include?(#temp.to_i) && ( #sky == activity.sky || activity.sky == "") && #day
#attemptactivities.include?(activity) ? {activity: activity, attempt: "yes"} : {activity: activity, attempt: "no"}
end
end.compact
respond_to do |format|
format.html { render action: "new" }
format.json { render json: #finaldata }
end
end
How about something like this?
I tried to make it a balance of readability and conciseness. First we filter for the desired activities. Then we structure the output. This should be easier to debug.
def getactivityfortoday
#temp = params[:temp].to_i
#sky = params[:sky]
#day = params[:day]
#activities = Weatherclockactivity.all
#attemptactivities = #user.attempts
selected_activities = #activities.select do |activity|
# Make sure it's the right temperaure
return false unless (activity.temperatureMin.to_i + 1 ... activity.temperatureMax.to_i).include? #temp
# Make sure the sky matches, or the sky is blank
return false unless (#sky.blank? || #sky.activity == activity.sky)
# Make sure the day matches
return false unless #day == activity.day
# Otherwise, it's good!
return true
end
selected_attempted_activities = selected_activities.map do|activity|
ret = {activity: activity}
ret[:attempt] = #attemptactivities.include?(activity) ? "yes" : "no"
ret
end
respond_to do |format|
format.html { render action: "new" }
format.json { render json: selected_attempted_activities }
end
end
There are a few typos in your original (for instance, #finaldata not finaldata). Make sure that you spell instance variables (things starting with #, like #sky) correctly, since if you try to access an undefined instance variable, it'll silently default to nil.
The best and flexible way is to use ActiveModel::Model
It allows you to use many more useful methods.
it will seems like:
app/models/activity_report.rb
Class ActivityReport
include ActiveModel::Model
attr_accessor :day, :activity # and etc.
validates :day, presence: true
def day
#day.to_s # for example
end
def day=(value)
#day = value - 1.month # for example every date which user set will set on one month ago
end
# and etc
end
app/controllers/posts_controller.rb
...
def index
#activity = ActivityReport.new(params[:activity])
end
def create
#activity.create!
end
...
app/views/posts/index.html.haml
= form_for #activity do |f|
= f.day
For more information you could take a look at:
http://edgeapi.rubyonrails.org/classes/ActiveModel/Model.html
http://railscasts.com/episodes/219-active-model (old)
http://railscasts.com/episodes/416-form-objects (newer, but a little complex)
I created a array in start for displaying the IDs one by one, and I want to the same array used in another action called next. In start I created a array called ques. I want to use ques in next.
START:
class AnswersController < ApplicationController
def start
#user = current_user
#student = Student.find_by_admission_no(#user.username)
#exam_group = ExamGroup.find_by_id(params[:exam_group_id])
#answer = Answer.new(params[:ans])
#module = params[:student_additional_field]
#questions = shuf_order(#exam_group,#module)
ques = []
#questions.each do |a|
ques.push q.id unless q.id.nil?
end
a = ques.first
#s = 1
#ans = Question.find_by_id(a)
render(:update) do |page|
page.replace_html 'main', :partial => 'ans', :object => #ans
page.replace_html 'quespan', :partial => 'ques'
end
end
Next:
def next
#user = current_user
#student = Student.find_by_admission_no(#user.username)
#exam_group = ExamGroup.find_by_id(params[:exam_group_id])
#answer = Answer.new(params[:ans])
#answer.answer = params[:answer]
#answer.exam_group_id = #exam_group.id
#answer.user_id = #user.id
passed_question = params[:passed_question]
#answer.questions_id = passed_question
#question = Question.find_by_id(passed_question)
#module = Question.find_by_sql ["SELECT student_additional_field_id FROM questions WHERE id=#{passed_question}"]
student_additional_field_id = #module[0].student_additional_field_id
#questions = shuf_order(#exam_group,student_additional_field_id)
a = #questions.first
#answer.modules_id = student_additional_field_id
if #answer.save
#ans = Question.find_by_id(a, :conditions => [' id not in (?)',answered])
unless #ans.nil?
render(:update) do |page|
page.replace_html 'main', :partial => 'ans', :object => #ans
end
else
render(:update) do |page|
page.replace_html 'main', :partial => 'ans2'
end
end
end
end
The usual way to pass variables between actions is the flash variable.
In start:
flash[:ques] = []
flash[:ques].push 'whatever'
In next:
flash[:ques]
to access the saved var.
But in your case maybe you want to save your ques in session or DB to use ques in more than these 2 actions:
In start:
session[:ques] = []
session[:ques].push 'whatever'
In next:
session[:ques]
Or in DB, you maybe would need a new table.
I am studying pragmatic bookshelf lessons. I try to make a session counter.
my store controller is
class StoreController < ApplicationController
def increment_counter
if session[:counter].nil?
session[:counter] = 0
end
session[:counter] += 1
end
def index
#count = increment_counter
#products = Product.all
#cart = current_cart
#time = Time.now
#shown_message = "You've been here #{#count} times" if increment_counter >5
end
end
and my view is
<h5><p><%= #shown_message %></p></h5>..
until 5 times it does not work . but after it starts to count as 5,7,9,11. . what is wrong with my session[:counter]?
You call increment_counter twice in your action : first when setting #count, and then again in your condition for #shown_message.
In complement to ksol answer. Use #count in the last call.
def index
#count = increment_counter
#products = Product.all
#cart = current_cart
#time = Time.now
#shown_message = "You've been here #{#count} times" if #count >5
end
In your store_controller.rb better code like that:
before_action :session_counter
def session_counter
if session[:counter].nil?
session[:counter] = 0
end
session[:counter] += 1
#session_counter = session[:counter]
end
In store.html.erb like that:
<p>
<% if #counter>5 %>
<%= "U have visit the store #{#session_counter} times!" %>
<% end %>
</p>
looks a pretty nice and works easy.
In my controller I have:
def search
#sog = Konkurrencer.where("titel like ?", "%#{params[:q]}%")
#kate = []
#sog.each do |kat|
h = {}
kat.attributes.each{|k,v| h[k] = v.respond_to?(:force_encoding) ? v.dup.force_encoding("UTF-8") : v }
#kate << h
end
respond_to do |format|
format.html
format.json { render :json => #kate }
end
The problem is that the JSON contains all the attributes for the model. How do I create a JSON that have only ID, url and titel?
The JSON should also contain the key "url" which key should be the URL for the associated photo. I use paperclip. The path is: #konkurrencer.photo.image.url
UPDATE:
My search.json.erb:
[
<% #sog.each do |kon| %>
{"id":"<%= kon.id %>","titel":"<%= kon.titel %>"},
<% end %>
]
How do I remove the , for the last loop?
Create an array with the list of attributes you want to display. Use select query method to get only this fields in the SQL request. And finally loop on this attributes to fill the JSON array:
def search
displayed_attributes = %w{id url titel}
#sog = Konkurrencer.select(displayed_attributes.join(',')).where("titel like ?", "%#{params[:q]}%")
#kate = []
#sog.each do |kat|
h = {}
displayed_attributes.each do |attribute|
v = kat[attribute]
h[attribute] = v.respond_to?(:force_encoding) ? v.dup.force_encoding("UTF-8") : v
end
#kate << h
end
respond_to do |format|
format.html
format.json { render :json => #kate }
end
end