For each loop through API values - ruby-on-rails

I'm having trouble finding the correct way to list out all of my data here is my setup. #product.pic_url will return 5 images but only shows the first one how would I iterate through them all?
product.rb
class TaobaoProduct
attr_reader :desc,
:approve_status,
:title,
:item_url,
:nick,
:num_iid,
:pict_url,
:pic_url,
:title,
:volume,
:zk_final_price,
:reserve_price,
:price,
:detail_url,
:cid
attr_writer :title
def initialize(product)
product.each { |name, value| instance_variable_set("##{name}", value) }
end
# function to get individual taobao product
# product_id: Taobao product id
def self.find(product_id)
tb_hash = OpenTaobao.get(
:method => 'taobao.item.get',
:fields => 'num_iid,title,nick,desc,pic_url',
:num_iid => product_id
)
new(tb_hash["item_get_response"]["item"])
end
end
product.haml
.container
.row
.col-xs-12
%h1= #product.title
.row
.product-v2.col-md-9
.row
.col-md-6.col-xs-12
%img{:src => "#{#product.pic_url}" + "_400x400.jpg"}
.col-md-6.col-xs-12
.price-v2

Assuming Product#pic_url actually returns an array of image urls, you could do something like this:
.container
.row
.col-xs-12
%h1= #product.title
.row
.product-v2.col-md-9
.row
.col-md-6.col-xs-12
- #product.pic_url.each do |url|
%img{:src => "#{url}_400x400.jpg"}
.col-md-6.col-xs-12
.price-v2

Related

search items by category_id with select_tag drop down in rails

I have edited my first code and now it's better and cleaner thanks to #FunTimeFreddie, but the issue it's not yet properly solved. I'll come back with the right answer sooner.
In a search form I need to filter all menuitems:
1. per category
2. per category and search “query”
3. per min price && || max price
… and so on, with all possible combinations
I’ve manage to make a search in all menuitems with a “query”, min_price and max_price --all possible combinations from the search form. I can NOT manage to have the list of results of the chosen category, what am I doing wrong?
This is my model(edited):
class Menuitem < ActiveRecord::Base
belongs_to :menu_category
include Filterable
scope :newest_first, lambda { order('menuitems.created_at DESC') }
scope :last_one, lambda { order('menuitems.created_at ASC').last }
scope :search_keyword, lambda { |query|
where(["title LIKE ? or body LIKE ?", "%#{query}%", "%#{query}%"]) if query != ""
}
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id ) if menu_category_id != ""
}
scope :min_price, lambda { |price|
where("price > ?", price) if price != ""
}
scope :max_price, lambda { |price|
where("price < ?", price) if price != ""
}
end
This is my controller(edited):
class MenuitemsController < ApplicationController
def index
#menuitems = Menuitem.newest_first.filter(params.slice(:menu_category_id, :search_keyword, :min_price, :max_price))
end
And this is my view:
<%= simple_form_for :menuitem, :method => 'get', :url => {:action => 'index'} do |f| %>
<p>
<%= f.select :menu_category_id, options_for_select(#menucategories.map {|s| [s.title, s.id]}, params[:menu_category_id]), :selected => params[:menu_category_id], :onchange => "this.form.submit();", prompt: "Select category" %>
</p>
<p>
<%= f.input :search_keyword, input_html: { name: 'search_keyword', :value => params[:search_keyword]}, label: 'search recipe title', :required => false %>
<%= f.input :min_price, input_html: { name: 'min_price', :value => params[:min_price]}, label: 'min price:', :required => false %>
<%= f.input :max_price, input_html: { name: 'max_price', :value => params[:max_price]}, label: 'max price:', :required => false %>
<%= f.button :submit, "search" %>
</p>
<% end %>
You can save yourself the trouble of all the IF statements in your controller for all of the combinations by adding an IF statement within the scopes. For example, and this can similarly be applied to the four scopes associated to your form,
# menuitem model
scope :search_keyword, lambda { |query|
where(["name LIKE ?", "%#{query}%"]) if query != ""
}
This will allow to include only a single line in your controller, the line beneath your first IF statement, as this will handle the blank parameters.
There seems to be two issues with the category parameter. The first is it is a nested parameter within params[:menuitem], so in order to access it we need to call params[:menuitem][:menu_category_id]. Not too sure why this is happening to be honest, though I would recommend in this instance using a form_tag as opposed to form_for, given that we are not adding or editing the menuitems table itself.
The second issue is the category parameter is passed as a string, whereas we need it as an integer. We'll need to convert to parameter before applying it.
Now I'm not familiar with the .filters method (is this part of a gem?) but I got this to work the old fashioned way just by concatenating all the scopes together in one line as follows...
# menuitems controller
def index
#menuitems = Menuitem.newest_first.min_price(params[:min_price]).max_price(params[:max_price]).search_keyword(params[:search_keyword]).menu_category_id(params[:menuitem][:menu_category_id].to_i)
end
Note, another way of changing the data type would be to do so in the scope. You could do this as follows
# menuitem model
scope :menu_category_id, lambda { |menu_category_id|
where( "menu_category_id = ?", menu_category_id.to_i ) if menu_category_id != ""
}

How to add a field into a snippet in Comfortable Mexican Sofa

I plan to use snippets in Comfortable Mexican Sofa to store email templates. But is there a way to add a field into a snippet. I would use it to store the subject of the email. Would be good to have also the subject in the cms so that our editors can change it if they feel like it.
By default snippets seem to have only two fields 'Label' and 'Identifier'. And of course the 'Content'. I would like to add also a 'Subject' String into the fields.
Simple Three Step Process:
1:
rails g migration AddSubjectToComfyCmsSnippets subject:string
2:
rake db:migrate
3:
create app/views/comfy/admin/cms/snippets/_form.html.haml with the following content:
- content_for :right_column do
= render 'comfy/admin/cms/files/index'
= render :partial => 'comfy/admin/cms/partials/snippet_form_before', :object => form
= form.text_field :label, :data => {:slugify => #snippet.new_record?}
= form.text_field :identifier, :data => {:slug => true}
= form.text_field :subject
= form.text_area :content, :data => {'cms-rich-text' => true}
= render :partial => 'comfy/admin/cms/categories/form', :object => form
= render :partial => 'comfy/admin/cms/partials/snippet_form_after', :object => form
= form.form_group :class => 'form-actions' do
= form.submit t(#snippet.new_record?? '.create' : '.update'), :class => 'btn btn-primary'
= link_to t('.cancel'), comfy_admin_cms_site_snippets_path, :class => 'btn btn-link'
Now you can reference the subject in your app like so:
Subject: #{#snippet.subject}
Monkey patch to get fixtures working:
create config/initializers/cms_monkey_patch.rb with the following contents:
ComfortableMexicanSofa::Fixture::Snippet::Importer.class_eval do
def import!
Dir["#{self.path}*/"].each do |path|
identifier = path.split('/').last
snippet = self.site.snippets.find_or_initialize_by(:identifier => identifier)
# setting attributes
categories = []
if File.exists?(attrs_path = File.join(path, 'attributes.yml'))
if fresh_fixture?(snippet, attrs_path)
attrs = get_attributes(attrs_path)
snippet.label = attrs['label']
snippet.subject = attrs['subject']
categories = attrs['categories']
end
end
# setting content
%w(html haml).each do |extension|
if File.exists?(content_path = File.join(path, "content.#{extension}"))
if fresh_fixture?(snippet, content_path)
snippet.content = extension == "html" ?
::File.open(content_path).read :
Haml::Engine.new(::File.open(content_path).read).render.rstrip
end
end
end
# saving
if snippet.changed? || self.force_import
if snippet.save
save_categorizations!(snippet, categories)
ComfortableMexicanSofa.logger.info("[FIXTURES] Imported Snippet \t #{snippet.identifier}")
else
ComfortableMexicanSofa.logger.warn("[FIXTURES] Failed to import Snippet \n#{snippet.errors.inspect}")
end
end
self.fixture_ids << snippet.id
end
# cleaning up
self.site.snippets.where('id NOT IN (?)', fixture_ids).each{ |s| s.destroy }
end
end
end
ComfortableMexicanSofa::Fixture::Snippet::Exporter.class_eval do
def export!
prepare_folder!(self.path)
self.site.snippets.each do |snippet|
snippet_path = File.join(self.path, snippet.identifier)
FileUtils.mkdir_p(snippet_path)
# writing attributes
open(File.join(snippet_path, 'attributes.yml'), 'w') do |f|
f.write({
'label' => snippet.label,
'subject' => snippet.subject,
'categories' => snippet.categories.map{|c| c.label}
}.to_yaml)
end
# writing content
open(File.join(snippet_path, 'content.html'), 'w') do |f|
f.write(snippet.content)
end
ComfortableMexicanSofa.logger.info("[FIXTURES] Exported Snippet \t #{snippet.identifier}")
end
end
end

Spree ProductFilters do not work anymore

I got a problem defining new dynamic ProductFilters for Spree 2.0.4
My customer has got Categories (e.g. Taxons). A Product belongs to one category and has maximum 8 Properties BUT the Properties also depend on the Category each Product is in AND the Position of the Property is also important.
My Solution was to extend the Database in an unnormalized way:
module Spree
Taxon.class_eval do
belongs_to :prop1, class_name: "Spree::Property",
foreign_key: "p1_id"
belongs_to :prop2, class_name: "Spree::Property",
foreign_key: "p2_id"
belongs_to :prop3, class_name: "Spree::Property",
foreign_key: "p3_id"
belongs_to :prop4, class_name: "Spree::Property",
foreign_key: "p4_id"
belongs_to :prop5, class_name: "Spree::Property",
foreign_key: "p5_id"
belongs_to :prop6, class_name: "Spree::Property",
foreign_key: "p6_id"
belongs_to :prop7, class_name: "Spree::Property",
foreign_key: "p7_id"
belongs_to :prop8, class_name: "Spree::Property",
foreign_key: "p8_id"
attr_accessible :prop1, :prop2, :prop3, :prop4, :prop5, :prop6, :prop7, :prop8
def properties
prop = []
prop << prop1
prop << prop2
prop << prop3
prop << prop4
prop << prop5
prop << prop6
prop << prop7
prop << prop8
return prop
end
def applicable_filters
fs = []
fs << Spree::Core::ProductFilters.price_filter if Spree::Core::ProductFilters.respond_to?(:price_filter)
#fs << Spree::Core::ProductFilters.brand_filter if Spree::Core::ProductFilters.respond_to?(:brand_filter)
fs
end
end
end
So that I'm able to get the possible properties in a taxon for the corresponding product. I now made 8 Filters for each taxon property (prop1 .. prop8) because some values are numerical and should be handled different from text values, so even that's not completely DRY I came to the following solution:
module Spree
module Core
module ProductFilters
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop1_any do |*opts|
conds = opts.map {|o| ProductFilters.selective_prop1_filter[:conds][o]}.reject {|c| c.nil?}
scope = conds.shift
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Spree::Product.where(scope)
end
def ProductFilters.selective_prop1_filter(taxon = nil, locale = 'en')
return if taxon.nil? #||= Spree::Taxon.find_by_permalink!("categories")
property = taxon.prop1
scope = Spree::ProductProperty.where(:property_id => property, :locale => locale).
joins(:product => :taxons).
where("#{Spree::Taxon.table_name}.id" => [taxon] + taxon.descendants).
scoped
brands = scope.pluck(:value).uniq
{
:name => property.presentation,
:scope => :selective_prop1_any,
:labels => brands.sort.map { |k| [k,k] }
}
end
end
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop2_any do |*opts|
conds = opts.map {|o| ProductFilters.selective_prop2_filter[:conds][o]}.reject {|c| c.nil?}
Rails.logger.debug conds.inspect
scope = conds.shift
Rails.logger.debug scope.inspect
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Rails.logger.debug scope.inspect
Spree::Product.where(scope)
end
# ... other filters cut out for brevity
if Spree::Property.table_exists?
Spree::Product.add_search_scope :selective_prop8_any do |*opts|
[..]
end
def ProductFilters.selective_prop8_filter(taxon = nil, locale = 'en')
[..]
end
end
Spree::Product.add_search_scope :price_range_any do |*opts|
conds = opts.map {|o| Spree::Core::ProductFilters.price_filter[:conds][o]}.reject {|c| c.nil?}
scope = conds.shift
conds.each do |new_scope|
scope = scope.or(new_scope)
end
Spree::Product.joins(:master => :default_price).where(scope)
end
def ProductFilters.format_price(amount)
Spree::Money.new(amount)
end
def ProductFilters.price_filter
v = Spree::Price.arel_table
conds = [ [ Spree.t(:under_price, :price => format_price(10)) , v[:amount].lteq(10)],
[ "#{format_price(10)} - #{format_price(15)}" , v[:amount].in(10..15)],
[ "#{format_price(15)} - #{format_price(18)}" , v[:amount].in(15..18)],
[ "#{format_price(18)} - #{format_price(20)}" , v[:amount].in(18..20)],
[ Spree.t(:or_over_price, :price => format_price(20)) , v[:amount].gteq(20)]]
{ :name => Spree.t(:price_range),
:scope => :price_range_any,
:conds => Hash[*conds.flatten],
:labels => conds.map {|k,v| [k,k]}
}
end
end
end
end
Due to the fact, that even the values of the Properties should be localized, a made a column locale in the ProductProperties table. My selective filters pass the locale Variable to retrieve the correct ProductProperty.
Because of the MVC restrictions not being able to pass the locale from session[:locale] and the current taxon to the model I overwrote the original display logic which is using the applicable_filters method for the taxon in the CONTROLLER(!) like so:
TaxonsController.class_eval do
def show
#taxon = Taxon.find_by_permalink(params[:id])
return unless #taxon
if #taxon
#filters = []
#filters << Spree::Core::ProductFilters.selective_prop1_filter(#taxon, locale) unless #taxon.prop1.nil?
[...]
#filters << Spree::Core::ProductFilters.selective_prop8_filter(#taxon, locale) unless #taxon.prop8.nil?
#filters.concat(#taxon.applicable_filters)
else
#filters = Spree::Core::ProductFilters.all_taxons
end
p = params.merge(:taxon => #taxon.id)
#searcher = Spree::Config.searcher_class.new(params)
#searcher.current_user = try_spree_current_user
#searcher.current_currency = current_currency
#products = #searcher.retrieve_products
end
end
And here's the mainly left original view code:
<% unless #filters.empty? %>
<%= form_tag '', :method => :get, :id => 'sidebar_products_search' do %>
<% params[:search] ||= {} %>
<%= hidden_field_tag 'per_page', params[:per_page] %>
<% #filters.each do |filter| %>
<% labels = filter[:labels] || filter[:conds].map {|m,c| [m,m]} %>
<% next if labels.empty? %>
<div class="" data-hook="navigation">
<h5 class="filter-title"> <%= filter[:name] %> </h5>
<% labels.each do |nm,val| %>
<% label = "#{filter[:name]}_#{nm}".gsub(/\s+/,'_') %>
<label for="<%= label %>" class="checkbox" style="display:block;"><%= nm %><input type="checkbox"
id="<%= label %>"
name="search[<%= filter[:scope].to_s %>][]"
value="<%= val %>"
<%= params[:search][filter[:scope]] && params[:search][filter[:scope]].include?(val.to_s) ? "checked" : "" %> />
</label>
<% end %>
</div>
<% end %>
<%= submit_tag Spree.t(:search), :name => nil, :class => 'button' %>
<% end %>
<% end %>
The display works like it should: Depending on the taxon the user is in, the values from ProductProperties in the session[:locale] are fetched and displayed correctly, but now there comes the Problem:
The Search doesn't work work anymore. Even the not modified :price_range_any filter does not work. All Products belonging to the current taxon are always displayed. The search hash in params hash is built correct by the form search => {"price_range_any":["10.00 € EUR - 15.00 € EUR"]}
I've no idea. If I switch back to the original files by removing the overwrites everything is working.
How can I get my filters to run correctly?

belongs_to add a relationship?

these are my models
class Apartment
belongs_to :house
end
class House
has_many :apartments
end
apartment_controller;
def index
#apartments = Appartment.all
end
apartment index view
.span9
#container
- #appartments.each do |apartment|
.item{:class => apartment.features_to_html_class }
%article.info.t_xs
.article-base
%section
.span3
%h2 #{link_to apartment.name, appartment_path(apartment)}
%p
= raw truncate(apartment.property_description, :length => 375, :omission => '...')
%footer
%ul.meta
%li.comments
#{apartment.count_ratings}
= t('apartment.details.reviews')
%li.notes
#{apartment.guests}
= t('apartment.details.persons')
%li.notes
#{apartment.bedrooms}
= t('apartment.details.bedrooms')
%li.notes
#{apartment.bathrooms}
= t('apartment.details.bathrooms')
%ul.thumbnails
%li.span5
= image_tag(apartment.attachments.last.file.url, :class => 'thumbnail')
- apartment.attachments.limit(4).each do |a|
%li.span1
%a{:href => "#"}
= image_tag(a.file.url(:thumb), :class => "thumbnail")
.span8
%footer
#more
#{link_to t('apartments.summary.button'), appartment_path(apartment), :class => 'btn btn-primary'}
i get all the apartments from the DB. But now i want to add a link (belongs_to) to the house in at the apartment summary. how can i do this...thanks..remco
Did you try
link_to 'House', house_path(apartment.house) ?
Try this:
<%= link_to 'House', house_path(apartment.house) %>
or
<%= link_to 'House', house_url(apartment.house) %>
Regards!
You got all the apartments in the database.
Now you run the sql to get the apartments object.
Then iterate each apartment and link it to house with the association.
This is done as follows:
def index
#apartments = Appartment.all
#apartments.each do |apartment|
#this is giving you the link to house via association defined.
apartment.house
#this is giving you the value of the field say `house_name` of house table that is linked to apartment.
#house_name = apartment.house.house_name
.
.
.
end
end

In Rails 3.1, how can I create an HTML table generator that uses block style formatting

I'm developing an application that displays tabular data in many different areas and I find myself constantly using the same HTML table structure over and over. For example a particular table looks like this:
%table.zebra-striped#user-table{ :cellspacing => "0" }
%colgroup
%col{:id => "email"}
%col{:id => "username"}
%col{:id => "sign-in-count"}
%col{:id => "last-sign-in-at"}
%thead
%tr
%th{:id => "email-head", :scope => "col"} E-mail
%th{:id => "username-head", :scope => "col"} Username
%th{:id => "sign-in-count-head", :scope => "col"} Sign Ins
%th{:id => "last-sign-in-at-head", :scope => "col"} Last Sign In
%tbody
- #users.each do |user|
%tr{ :class => zebra }
%td
=h user.email
%td
=h user.username
%td
=h user.sign_in_count
%td
=h user.last_sign_in_at
Ideally, I would like to create some kind of helper method where I could do something like:
= custom_table_for #users do
= column :email
= column :username do |user|
= link_to user.username, user_path(user)
= column "Sign Ins", :sign_in_count
= column :last_sign_in_at
This way I can change the formatting of the data in the columns and the column header names if I'm not happy with default values, but have the table generated for me.
I suppose I could create a normal helper, but I'd have to use arrays and I have no idea how I could include custom data formatting per column.
active_admin has something similar to this which you can see here: http://activeadmin.info/docs/3-index-pages/index-as-table.html
Any leads or ideas would be greatly appreciated.
I just came up with this:
A few points:
The line #columns = [] is a reset so you can call it more than once.
The yield in the custom_table_for calls the block that you pass it.
The block in the column method is stored and called in custom_table_for if it is set.
I included a sample class to show the usage too.
please note I did this outside of a rails app and you almost certainly want to use http://api.rubyonrails.org/classes/ActionView/Helpers/TagHelper.html#method-i-content_tag instead of the p "<table>" this is merely for sample purposes when you run it in the console.
module TableHelper
def custom_table_for(items)
#columns = []
yield
p "<table>"
#columns.each do |c|
p "<th>#{c[:value]}</th>"
end
items.each do |e|
p "<tr>"
#columns.each do |c|
e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
p "<td>#{e[c[:name]]}</td>"
end
p "</tr>"
end
p "</table>"
end
def column(name, value = nil, &block)
value = name unless value
#columns << {:name => name, :value => value, :block => block}
end
end
class ExampleTable
include TableHelper
def test
#users = [{:email => "Email 1", :username => "Test User"}, {:email => "Email 2", :username => "Test User 2"}]
custom_table_for #users do
column :email, "Email"
column :username do |user|
user.upcase
end
end
end
end
et = ExampleTable.new
et.test
UPDATE
I migrated this to rails to use content_tags
module TableHelper
def custom_table_for(items)
#columns = []
yield
content_tag :table do
thead + tbody(items)
end
end
def thead
content_tag :thead do
content_tag :tr do
#columns.each do |c|
concat(content_tag(:th, c[:value]))
end
end
end
end
def tbody(items)
content_tag :tbody do
items.each { |e|
concat(content_tag(:tr){
#columns.each { |c|
e[c[:name]] = c[:block].call(e[c[:name]]) if c[:block]
concat(content_tag(:td, e[c[:name]]))
}
})
}
end
end
def column(name, value = nil, &block)
value = name unless value
#columns << {:name => name, :value => value, :block => block}
end
end
To compliment #gazler's response, here's a way to make a table of a single resource-- column one for attribute names, column two for their values:
module TableHelper
#resource = nil
def simple_table_for(resource)
#resource = resource
content_tag :table do
content_tag :tbody do
yield
end
end
end
def row(key, label = nil, &block)
if key.is_a? String
label = key
end
content_tag(:tr) {
concat content_tag :td, label || key.capitalize
concat content_tag(:td ){
if block_given?
yield
else
#resource.send(key)
end
}
}
end
end

Resources