Rails Migration - Spree Commmerce - ruby-on-rails-3

I want to add featured and latest products to my homepage, I am replicating the spree_fancy gem method and using the below migration to add taxons but not show the tags in the side bar but I am unsure what this part of the migrataion is doing -
products = Spree::Product.all
if products[6]
products[0..6].each do |product|
product.taxons << slider
end
end
if products[16]
products[4..16].each do |product|
product.taxons << featured
end
products[0..12].each do |product|
product.taxons << latest
end
Here is the full migation
class AddSliderTaxonsAndApplyThem < ActiveRecord::Migration
def up
tags = Spree::Taxonomy.create(:name => 'Tags')
slider = Spree::Taxon.create({:taxonomy_id => tags.id, :name => 'Slider'})
featured = Spree::Taxon.create({:taxonomy_id => tags.id, :name => 'Featured'})
latest = Spree::Taxon.create({:taxonomy_id => tags.id, :name => 'Latest'})
products = Spree::Product.all
if products[6]
products[0..6].each do |product|
product.taxons << slider
end
end
if products[16]
products[4..16].each do |product|
product.taxons << featured
end
products[0..12].each do |product|
product.taxons << latest
end
end
end
def down
Spree::Taxonomy.where(:name => 'Tags').first.destroy()
end
end

Dan,
That part of the migration is simply assigning products to each taxon. When you're actually using it, you don't need that, as you'll be picking and choosing what products to put in each category.

Related

Why isn't my search method working in Ruby on Rails?

In my Ruby on Rails application, I have a cinema system and am trying to return the screen a showing is in when a user searches for the showing.
To display the search drop down I am using this code in my _application.html.erb:
<%= render( :partial => '/screen_lookup', :locals => {:showings => #showings = Showing.all, :my_path => '/screens/display_screens_by_showing' })%>
Which renders the search from the _screen_lookup.html.erb:
<%= form_tag my_path, :method=>'post', :multipart => true do %>
<%= select_tag ('showings_id'),
options_from_collection_for_select(#showings, :id, :showing_times, 0 ),
:prompt => "Showings" %>
<%= submit_tag 'Search' %>
<% end %>
And uses the display_screens_by_showing in the screens_controller:
def display_screens_by_showing
#screens = Screen.showing_search(params[:showing_id])
if #screens.empty?
# assign a warning message to the flash hash to be displayed in
# the div "feedback-top"
flash.now[:alert] = "There are no films of that genre."
# return all products, in alphabetical order
#screens = Screen.all
end
render :action => "index"
end
And this searches using the method in the screen.rb model:
def self.showing_search(showing_id)
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
end
Now, the problem I am having is that because a showing belongs_to a screen, and a screen has_many showings, I need to be able to search for the showing, and store that showing's screen_id in a variable to search for the screen that showing is in with, which I have tried doing in the model:
screen = Showing.where("id = ?", showing_id).screen_id
self.where("id = ?", screen)
But the error I am getting is:
NoMethodError in ScreensController#display_screens_by_showing
undefined method `screen_id' for #<ActiveRecord::Relation []>
These are the model relationships:
showing.rb:
class Showing < ActiveRecord::Base
belongs_to :screen
end
screen.rb:
class Screen < ActiveRecord::Base
has_many :showings
end
What code will get my search working?
The problem is that where doesn't return a record, it returns a relation that can be enumerated or chained, instead you want to use find or find_by to return a single record, which is kind equivalent to where + first
screen = Showing.find(showing_id).screen_id
which is sort of like doing
screen = Showing.where(id: showing_id).first.screen_id
If you want to pass a hash you can use find_by which will be like this
screen = Showing.find_by(id: showing_id).screen_id
PS:
I'm not sure what you're doing exactly, but i think those two lines can be merged into a single query ( not sure what it should be returning, but I'm assuming a screen )
def self.showing_search(showing_id)
Showing.find(showing_id).screen
end

Rails WillPaginate::Collection not paginating Array

An existing array vparray is being generated, then sorted by a non-db column rate. It then needs to be paginated :
#vps = vparray.sort_by{|e| e.rate}
#vps = WillPaginate::Collection.create(1, 10, #vps.length) do |pager|
pager.replace #vps
end
The view;
<%= will_paginate #vps, :previous_label => "prev ", :next_label => " next" -%>
renders fine, the number of pages pans out and the page is apparently the first. However, upon: <% #vps.each do |product| %>, the entire sorted array is being rendered.
Apparently, the array can only be populated with values of current page. However
#vps = vparray.sort_by{|e| e.rate}
#vps = #vps.paginate(:page => params[:page], :per_page => 10)
#vps = WillPaginate::Collection.create(1, 10, #vps.length) do |pager|
pager.replace #vps
end
is incorrect. The paginate command actually reduces the found set to the same number as per_page and therefore == only 1 page!
So I'm assuming that line should not be there. The view should be calling the proper page of results
<% #vps.each do |product| %>
something better than
<% #vps.page(params[:page]).each do |product| %>
that generates undefined methodpage for WillPaginate::Collection`
context:
ruby 1.9.3,
rails 3.2.17,
will_paginate 3.0.5
Went and re-read the collection.rb and array.rb libraries.
With controller stating:
require "will_paginate/array"
#vgps = vgp.sort_by{|e| e.rate}
#vgps = #vgps.paginate(:page => params[:page], :per_page => 30)
This is all that is necessary for a sorted array.

Duplicating a record with associated images using Carrierwave

I have an app which you can store order/invoices in. I'm building a simple feature where you can duplicate invoices for my customers. I wrote this method in my Order.rb model which:
Takes the invoice, duplicates the associated lineitems, adds the new OrderID into them...and does the same for associated images.
def self.duplicate_it(invoice)
new_invoice = invoice.dup
new_invoice.save
invoice.lineitems.each do |l|
new_lineitem = l.dup
new_lineitem.order_id = new_invoice.id
new_lineitem.save
end
invoice.images.each do |i|
new_image = i.dup
new_image.order_id = new_invoice.id
new_image.save
end
return new_invoice
end
Unfortunately, you can't just .dup the image because there's all this associate expiration stuff since I'm storing images on S3. Is there a way to regenerate the image maybe using its image_url?
The error I get when running this is below. Which tells me not all the associated image information is dup'd correctly.
Showing /Users/bruceackerman/Dropbox/printavo/app/views/orders/_image-display.erb where line #3 raised:
undefined method `content_type' for nil:NilClass
Extracted source (around line #3):
1: <% #order.images.each do |image| %>
2: <% if image.image && image.image.file %>
3: <% if image.image.file.content_type == "application/pdf" %>
4: <%= link_to image_tag("/images/app/pdf.jpg",
5: :class => 'invoice-image'),
6: image.image_url,
i think you can do the following
invoice.images.each do |i|
new_image = new_invoice.images.new({ order_id: new_invoice.id })
new_image.image.download!(i.image_url)
new_image.store_image!
new_image.save!
end
This is actually how I did it for each lineitem on an order:
def self.duplicate_it(invoice)
new_invoice = invoice.dup :include => {:lineitems => :images} do |original, kopy|
kopy.image = original.image if kopy.is_a?(Image)
end
new_invoice.save!
return new_invoice
end
It is a bit late however this is my solution. I have had far too many problems with .download!
if #record.duplicable?
new_record = #record.dup
if new_record.save
#record.uploads.each do |upload|
new_image = new_record.uploads.new({ uploadable_id: new_record.id })
new_image.filename = Rails.root.join('public'+upload.filename_url).open
new_image.save!
end
end
Here is my upload.rb
class Upload < ActiveRecord::Base
belongs_to :uploadable, polymorphic: true
mount_uploader :filename, ImageUploader
end
Hope it helps!

rails OR query based on multiple checkbox selections

This seems like it should be a common problem but I'm having trouble finding an answer. Basically I want to have a form with 10 or so checkboxes which I'm creating with check_box_tag. When the form is submitted I want to generate a query that return all records that match ANY of the checked selections. So, the number of checked selections will vary.
So, for example, if I have
class Book < ActiveRecord::Base
belongs_to :author
end
I want to generate something like
Book.where("author_id = ? or author_id = ?", params[authors[0]], params[authors[1]]) if there are two boxes checked, etc.
Thanks for any insight.
Will this work for you?
Book.where(author_id: [array_of_author_ids])
You need to collect author_ids from params first
I recently had to do something similar, this is how I achieved this. It's pretty clever (at least I think so. :))
I created a query model that serializes the query column (text field) in JSON. I use a form to get the query data from the user with selection fields.
class BookQuery < ActiveRecord::Base
has_many :books
# loop through each foreign key of the Book table and create a hash with empty selection
def self.empty_query
q = {}
Book.column_names.each do |column_name|
next unless column_name.ends_with?("_id")
q.merge column_name => []
end
end
end
I'm using Author as an example below:
<%= form_for #book_query do |f| %>
<% for author in Author.all %>
<%= check_box_tag "book_query[query][author_ids][]", author.id, false%>
<%= author.name %>
<% end %>
<%= f.submit "Save Query" %>
<% end %>
When this form is submitted you ended up with parameters like this:
When the form is submitted it generates this parameter:
Parameters: {"utf8"=>"✓", "authenticity_token"=>"XXXXXXXXXXX", "book_query"=>{"query"=>{"author_ids"=>["2", "3"]}}, "commit"=>"Save Query"}
Now in the BookQuery controller's create action you can just do what create function always does:
def create
#book_query = BookQuery.build(params[:book_query])
if #book_query.save
flash[:success] = "Book query successfully saved."
redirect_to ...
else
flash[:error] = "Failed to save book query."
render :new
end
end
But by default rails serializes the data in hash type:
1.9.3p194 :015 > pp BookQuery.find(9).query
BookQuery Load (0.7ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 9]]
"--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
=> "--- !ruby/hash:ActiveSupport::HashWithIndifferentAccess\nauthor_ids:\n- '2'\n- '3'\n"
In BookQuery model, add the following:
serialize :query, JSON
But rail would change the IDs to string:
1.9.3p194 :018 > query = JSON.parse(BookQuery.find(10).query)
BookQuery Load (0.5ms) SELECT "book_queries".* FROM "book_queries" WHERE "book_queries"."id" = $1 LIMIT 1 [["id", 10]]
=> {"author_ids"=>["2", "3"]}
1.9.3p194 :019 > query["author_ids"]
=> ["2", "3"]
What I did then is override the attribute accessors in BookQuery model:
The below has to be done because the hash returns strings, not ids in integer.
def query=(query)
query.each_pair do |k, v|
if query[k].first.present?
query[k].map!(&:to_i)
else
query.except!(k)
end
end
write_attribute(:query, query)
end
# just want to avoid getting nil query's
def query
read_attribute(:query) || {}
end
To find book with this query, you can simply add this function to your Book model:
def self.find_by_book_query(book_query, options = {})
options[:conditions] = book_query.query
find(:all, options)
end
Now you get a customizable query string based on the model definition Book and everything works like the Rails way. :)

Nested content_tag throws undefined method `output_buffer=` in simple helper

I'm trying to create a simple view helper but as soon as I try nest a couple of content tags it will throw NoMethodError: undefined method `output_buffer=' for
def table_for(list, &proc)
t = Table.new
proc.call(t)
t.render_column(list)
end
class Table
include ActionView::Helpers::TagHelper
attr_accessor :columns, :block
def initialize
#columns = Array.new
end
def col(name)
#columns << name
end
def render_column(list)
content_tag :table do
list.each do |c|
content_tag :td, c
end
end
end
end
Any hints of what's wrong? I've also seen that there's a XmlBuilder is that one better for my purpose?
ActionView::Base has built into it the Context module, which provides the methods output_buffer() and output_buffer=().
So you can solve your problem either by having your class do:
include ActionView::Context
Or even more simply:
attr_accessor :output_buffer
I think there were some changes about this in 3.0, but in previous versions the trick was to pass self:
def table_for(list, &proc)
Table.new(self)
# ...
def initialize(binding)
#binding = binding
#...
def render_column
#binding.content_tag :table do
# ...
end
end
I'm not sure if this is still how it's done in rails 3.
Another thing to fix in ordere for the code to work is to save the output of the inner content_tag somewhere, as with each the content is generated and then discarded. One of the possible solutions:
def render_column(list)
#binding.content_tag :table do
list.inject "" do |out, c|
out << #binding.content_tag(:td, c)
end.html_safe
end
end
With help from Nested content_tag throws undefined method `output_buffer=` in simple helper I ended up with the following solution inspired by the API for Formtastic.
<%= table_for(#users) do |t| %>
<% t.col :name %>
<% t.col :email %>
<% t.col :test, :value => lambda { |u| u.email }, :th => 'Custom column name' %>
<% t.col :static, :value => 'static value' %>
<% end %>
Using the output_buffer directly and probably reinventing the wheel the code looks like
module ApplicationHelper
def table_for(list, &block)
table = Table.new(self)
block.call(table)
table.show(list)
end
class Column
include ActiveSupport::Inflector
attr_accessor :name, :options
def initialize(name, options = {})
#name = name
#options = options
end
def td_value(item)
value = options[:td]
if (value)
if (value.respond_to?('call'))
value.call(item)
else
value
end
else
item[name]
end
end
def th_value
options[:th] ||= humanize(name)
end
end
class Table
include ActionView::Helpers::TagHelper
attr_accessor :template, :columns
def initialize(temp)
#columns = Array.new
#template = temp
end
def col(name, options = {})
columns << Column.new(name, options)
end
def show(list)
template.content_tag(:table) do
template.output_buffer << template.content_tag(:tr) do
columns.collect do |c|
template.output_buffer << content_tag(:th, c.th_value)
end
end
list.collect do |item|
template.output_buffer << template.content_tag(:tr) do
columns.collect do |c|
template.output_buffer << template.content_tag(:td, c.td_value(item))
end
end
end
end
end
end
end