ruby/rails how to ignore a comma in when ordering - ruby-on-rails

I have a price field for a product in a catalog. Sometimes the admin user is putting a comma when dealing with thousands (ex: $10,000) and sometimes he is just doing $6000. While I would like to simply tell him to do it one way or the other, I would also like to solve the issue programmatically.
The #show action responsible is here:
def show
#category = Category.find_by_url_name(params[:category_id])
#brand = Brand.find(params[:id])
#search = Product.find(:all, :conditions => ['brand_id = ? and category_id = ?', #brand.id, #category.id],
:order=> params[:order] || 'price DESC')
#products = #search.paginate(:page => params[:page], :per_page => 12 )
#meta_title = "#{#brand.name}"
respond_to do |format|
format.html # show.html.erb
format.xml { render :xml => #brand }
end
end
I also have a sort_options helper in my application helper that is providing the ordering options to the site user:
def product_sort_options
options_for_select([
['', nil],
['Newest to Oldest', 'descend_by_date'],
['Oldest to Newest', 'ascend_by_date'],
['Price: Highest to Lowest', 'descend_by_price'],
['Price: Lowest to Highest', 'ascend_by_price'],
['Name', 'ascend_by_name']
])
end
any ideas?

To make it a full answer - price should not be a string. The fact that you have 300 products now is not a big deal.
Make a migration:
rails generate migration decimalise
Then edit it (db/migrate/*decimalise.rb), and write something like this:
class Decimalise < ActiveRecord::Migration
def up
connection = ActiveRecord::Base.connection()
# kill the weird chars in the string field
connection.execute("UPDATE products SET price = REPLACE(REPLACE(price, ',', ''), '$', '')")
# convert the string column into a decimal one
change_table :products do |t|
# adjust for your use case - this gives you values up to 9999999.99
# if you need more, increase the 10
t.column :price, :decimal, :precision => 10, :scale => 2
end
end
def down
change_table :products do |t|
t.column :price, :string, :limit => 10
end
end
end
then finally, run
rake db:migrate
(untested, you will probably need to tweak. also, back up your DB before any tinkering - I'll not be responsible for any data loss you suffer)
EDIT One thing I forgot: how to print it out.
<%= number_to_currency #product.price %>
should give you something like $1,999.99 for a price of 1999.99.

You can use String.gsub to search the commas and replace them by nothing.

Related

Rails RABL display collection as object and child in this object

I have such index.rabl:
collection #exchangers, :root => "bank", :object_root => false
extends "exchanger_lists/show"
and such show.rabl:
object #exchanger
attributes :id, :name, :address, :location_id, :latitude, :longitude, :exchanger_type_id
node(:location_name) {|exchanger_list| exchanger_list.location.name }
node(:exchanger_type_name) {"normal" }
child #currencies do
attribute :value, :direction_of_exchange_id, :exchanger_list_id
end
my contoller is such:
def index
#exchangers = ExchangerList.all
end
def show
#exchanger = ExchangerList.find(params[:id])
#currency_list = CurrencyList.all
#currencies = []
#currency_list.each do |c|
#currencies << CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id, :exchanger_list_id => #exchanger.id}, :order => :updated_at).last(2)
end
#currencies.flatten!
end
if i call in browser show method, i see child #currencies and it's data, but if i call index i see all (also i see nodes) but child i didn't see.... What's wrong? what i do bad?
Your architecture is a little bit messed up because in the show action you not only display an #exchanger but also the complete list of #currencies being nil when you render show in the index template. In general I would suggest you to think about the whole app architecture.
When I should give you a simple solution for you current problem I would extract the #currencies code from the show action into helper method in app/helpers/currencies_helper.rb and access it from the show template.
module CurrenciesHelper
def currencies(exchanger)
currencies = CurrencyList.all.map do |c|
CurrencyValue.find(:all, :conditions => {:currency_list_id => c.id, :exchanger_list_id => exchanger.id}, :order => :updated_at).last(2)
end
currencies.flatten!
end
end
By the way I replaced the each method with map because it suits better in this case.
Change the currencies part in the show template to
child currencies(#exchanger) do
attribute :value, :direction_of_exchange_id, :exchanger_list_id
end

Sunspot Solr, Rails and Ordering

I have a project model in my rails 3.1 application and I want to use Solr to run a search on it.
I defined the search like this:
searchable do
text :nr, :boost => 5 # nr is integer
text :name, :boost => 5
text :description, :boost => 2
text :client do
client.name
end
text :tasks do
tasks.map(&:name)
end
end
The project-nr, in my model just called nr, type integer, is the most used reference for finding a project.
Now besides having a search form I still want my projects ordered by the nr when no search was performed, but this does not work - my project seem to be in totally random order.
The code of my ProjectsController index action looks like this:
def index
#search = Project.search do
fulltext params[:search]
paginate :page => params[:page]
order_by :nr, :desc
end
#projects = #search.results
##projects = Project.active.visible.design.order("nr desc")
respond_to do |format|
format.html # index.html.erb
format.json { render json: #projects }
end
But when I visit then myapp/projects I get a
Sunspot::UnrecognizedFieldError in ProjectsController#index
No field configured for Project with name 'nr'
error...
any ideas what I need to do to order by nr. ?
thanks
Okay, I solved it by turning the nr field to an integer in my searchable:
searchable do
integer :nr
text :name, :boost => 5
text :description, :boost => 2
text :client do
client.name
end
text :tasks do
tasks.map(&:name)
end
end
Now I was able to order it nicely but I couldn't perform a text search on the project_nr anymore.
So I added a virtual attribute name_number to my Project model and instead searched on this field.
def name_number
"#{self.nr} - #{self.name[0..24]}"
end
Now I have ordering and searching in place... If there are other / better ideas, keep em coming!

While loop to create multiple records in rails

I'm building an application where users can purchase tracking numbers. I have an Order model and an Order Transaction model. If the Order Transaction returns from the gateway with success, I'm using an after_save callback to trigger a method that creates the tracking numbers and inserts them into the database. Sometimes a user just orders one, but if they order more than one, I can't seem to get rails to create and insert more than one record.
Here's what I'm using -- I've never had to user a loop like this, so I'm not sure what I'm doing wrong.
def create_trackables
if self.success == true
#order = Order.find(order_id)
#start = 0
while #start < #order.total_tokens
#trackable_token = Tracker.create_trackable_token
#start += 1
#trackable ||= Tracker.new(
:user_id => #current_user,
:token => #trackable_token,
:order_id => order_id
)
#trackable.save
end
end
end
dmarkow is right that you should use trackable instead of #trackable but you also should be using = instead of ||=. You also might as well just use create. Here's how I'd write it: def create_trackables
return unless self.success
order = Order.find(order_id) #you shouldn't need this line if it has_one :order
1.upto(order.total_tokens) do
Tracker.create!(
:user_id => #current_user,
:token => Tracker.create_trackable_token,
:order_id => order_id
)
end
end
Change #trackable to trackable to keep it scoped to the loop. Otherwise, the second time the loop runs, #trackable already has a value so the call to Tracker.new doesn't execute, and the #trackable.save line just keeps re-saving the same record. (Edit: Also remove the ||= and just use =).
def create_trackables
if self.success == true
#order = Order.find(order_id)
#start = 0
while #start < #order.total_tokens
#trackable_token = Tracker.create_trackable_token
#start += 1
trackable = Tracker.new(
:user_id => #current_user,
:token => #trackable_token,
:order_id => order_id
)
trackable.save
end
end
end

Globalize2 and migrations

I have used globalize2 to add i18n to an old site. There is already a lot of content in spanish, however it isn't stored in globalize2 tables. Is there a way to convert this content to globalize2 with a migration in rails?
The problem is I can't access the stored content:
>> Panel.first
=> #<Panel id: 1, name: "RT", description: "asd", proje....
>> Panel.first.name
=> nil
>> I18n.locale = nil
=> nil
>> Panel.first.name
=> nil
Any ideas?
I'm sure you solved this one way or another but here goes. You should be able to use the read_attribute method to dig out what you're looking for.
I just used the following to migrate content from the main table into a globalize2 translations table.
Add the appropriate translates line to your model.
Place the following in config/initializers/globalize2_data_migration.rb:
require 'globalize'
module Globalize
module ActiveRecord
module Migration
def move_data_to_translation_table
klass = self.class_name.constantize
return unless klass.count > 0
translated_attribute_columns = klass.first.translated_attributes.keys
klass.all.each do |p|
attribs = {}
translated_attribute_columns.each { |c| attribs[c] = p.read_attribute(c) }
p.update_attributes(attribs)
end
end
def move_data_to_model_table
# Find all of the translated attributes for all records in the model.
klass = self.class_name.constantize
return unless klass.count > 0
all_translated_attributes = klass.all.collect{|m| m.attributes}
all_translated_attributes.each do |translated_record|
# Create a hash containing the translated column names and their values.
translated_attribute_names.inject(fields_to_update={}) do |f, name|
f.update({name.to_sym => translated_record[name.to_s]})
end
# Now, update the actual model's record with the hash.
klass.update_all(fields_to_update, {:id => translated_record['id']})
end
end
end
end
end
Created a migration with the following:
class TranslateAndMigratePages < ActiveRecord::Migration
def self.up
Page.create_translation_table!({
:title => :string,
:custom_title => :string,
:meta_keywords => :string,
:meta_description => :text,
:browser_title => :string
})
say_with_time('Migrating Page data to translation tables') do
Page.move_data_to_translation_table
end
end
def self.down
say_with_time('Moving Page translated values into main table') do
Page.move_data_to_model_table
end
Page.drop_translation_table!
end
end
Borrows heavily from Globalize 3 and refinerycms.

Dynamic find conditions in active record

I have an index action in rails that can handle quite a few params eg:
params[:first_name] # can be nil or first_name
params[:age] # can be nil or age
params[:country] # can be nil or country
When finding users I would like to AND all the conditions that are not nil. This gives me 8 permutations of the find conditions.
How can I can I keep my code DRY and flexible and not end up with a bunch of if statements just to build the conditions for the find. Keep in mind that if no conditions are specified I just want to return User.all
How about something like:
conditions = params.only(:first_name, :age, :country)
conditions = conditions.delete_if {|key, value| value.blank?}
if conditions.empty?
User.all
else
User.all(:conditions => conditions)
end
I would normally use named scopes for something like this:
class User < ActiveRecord::Base
named_scope :name_like, lambda {|name| {:conditions => ["first_name LIKE ?", "#{name}%"]}}
named_scope :age, lambda {|age| {:conditions => {:age => age}}}
named_scope :in_country, lambda {|country| {:conditions => {:country => country}}}
end
class UsersController < ActionController
def index
root = User
root = root.name_like(params[:first_name]) unless params[:first_name].blank?
root = root.age(params[:age]) unless params[:age].blank?
root = root.country(params[:country]) unless params[:age].blank?
#users = root.paginate(params[:page], :order => "first_name")
end
end
That's what I normally do.
This seems to work quite nicely:
conditions = params.slice(:first_name, :age, :country)
hash = conditions.empty? ? {} : {:conditions => conditions}
#users = User.all hash
Using James Healy answer, I modify the code to be used in Rails 3.2 (in case anyone out there need this).
conditions = params.slice(:first_name, :age, :country)
conditions = conditions.delete_if {|key, value| value.blank?}
#users = User.where(conditions)
You could try Ambition, or a number of other ActiveRecord extensions.
This works for me too
conditions = params[:search] ? params[:search].keep_if{|key, value| !value.blank?} : {}
User.all(:conditions => conditions)
If you happen to be on an ancient project (Rails 2.x) and very messy, you could do something like the following for adding new fields to the original query.
Original code:
User.find(:all,
:conditions => ['first_name LIKE ? AND age=? AND country=?',
"#{name}%", age, country]
Adding a new dynamic condition on zip_code field:
zip_code = params[:zip_code] # Can be blank
zip_query = "AND zip_code = ?" unless zip_code.blank?
User.find(:all,
:conditions => ['first_name LIKE ? AND age=? AND country=? #{zip_query}',
"#{name}%", age, country, zip_code].reject(&:blank?)
Adding a reject(&:blank?) to the conditions arrays will filter the nil value.
Note: The other answers are much better if you are coding from zero, or refactoring.

Resources