Named scope with multiple values - ruby-on-rails

I'm having some trouble with my named scope.
def self.by_status(status)
arr = status.split(',').map{ |s| s }
logger.debug "RESULT: #{arr.inspect}"
where(status: arr)
end
When I call this scope with more than one value, the result of arr = ["New", "Open"]
This does not return any results, while it should. If I try this command in the console: Shipment.where(status: ['New', 'Open']) I get the results that I'm expecting.
Am I missing something here?
Edit (added the call of the class method ):
def self.to_csv(options = {}, vendor_id, status)
CSV.generate(options) do |csv|
csv << column_names
if !vendor_id.blank? && status.blank?
by_vendor_id(vendor_id).each do |product|
csv << product.attributes.values_at(*column_names)
end
elsif !vendor_id.blank? && !status.blank?
by_vendor_id(vendor_id).by_status(status).each do |product|
csv << product.attributes.values_at(*column_names)
end
elsif vendor_id.blank? && !status.blank?
logger.debug "by_status result: #{by_status(status).inspect}"
by_status(status).each do |product|
csv << product.attributes.values_at(*column_names)
end
else
all.each do |product|
csv << product.attributes.values_at(*column_names)
end
end
end
end

Try this in your model:
scope :by_status, ->(*statuses) { where(status: statuses) }
Then in your code you can call:
Shipment.by_status('New', 'Open')
This has the flexibility to just take one argument, too:
Shipment.by_status('New')

Related

Merge a hash inside array

I am new to Ruby and Rails, I have stuck in a situation that I need to create an array of hashes. Please see below the code:
def self.v_and_c items
result = []
items.try(:each) do |item|
result << item
if item.is_parent_variation
check_ancestor item
result << { :item_variation => #variations }
result << { :options => #options }
elsif item.is_parent_customization
check_ancestor item
result << { :customizations => #customizations }
result << { :ingredients => #ingredients }
end
end
result
end
Here is the output of the function:
{"items":[{"id":1,"name":"Cake"},{"item_variation":null},{"options":null}]}
But I wanted to do like this.
{"items":[{"id":1,"name":"Cake","item_variation":null, "options":null} ]}
You could try something like this:
def self.v_and_c items
result = []
items.try(:each) do |item|
item_hash = {}.merge(item)
if item.is_parent_variation
check_ancestor item
item_hash.merge!({ item_variation: #variations }).merge!({ options: #options})
elsif item.is_parent_customization
check_ancestor item
item_hash.merge!({ customizations: #customizations }).merge!({ ingredients: #ingredients})
end
result.push(item_hash)
end
result
end
Explanation:
For each iteration of loop, create an item_hash and merge all the requisite hashes in it and then push the resulting hash into the result array.
Few pointers:
Take care of new Ruby hash syntax
If check ancestor is needed in both if and else why not do it outside?
It should be simple like this, use .merge method
def self.v_and_c items
result = []
items.try(:each) do |item|
result << item
if item.is_parent_variation
check_ancestor item
result = result.merge { :item_variation => #variations }
result = result.merge { :options => #options }
elsif item.is_parent_customization
check_ancestor item
result = result.merge { :customizations => #customizations }
result = result.merge { :ingredients => #ingredients }
end
end
result
end
def self.v_and_c items
[].tap do |result|
items.try(:each) do |item|
result_hash = item.dup
if item.is_parent_variation
check_ancestor item
result_hash.merge!({ item_variation: #variations, options: #options })
elsif item.is_parent_customization
check_ancestor item
result_hash.merge!({ customizations: #customizations, ingredients: #ingredients })
end
result << result_hash
end
end
end

How to convert an array of objects to CSV in Rails

I have an array of objects. I am trying to create CSV data and allow the user to download that file but I get the following error:
Undefined method 'first_name' for Hash:0x007f946fc76590
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
This is the button that allows a user to download the CSV:
<%= link_to "Download Employee CSV", download_employee_csv_path %>
Controller:
def download_employee_csv
employee_csv_data = []
employees.each do |employee|
employee_csv_data << {
first_name: employee[:first_name],
last_name: employee[:last_name],
email: employee_email,
phone1: employee[:phone1],
gender: employee[:gender],
veteran: employee[:veteran].to_s,
dob: employee[:dob],
core_score: service_score,
performance_rank: rank,
industry_modules_passed: industry_modules_passed
}
end
respond_to do |format|
format.html
format.csv { send_data Employer.to_csv(employee_csv_data), filename: "download_employee_csv.csv" }
end
end
employee_csv_data:
=> [{:first_name=>"Christopher",
:last_name=>"Pelnar",
:email=>"pelnar#gmail.com",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1988-09-09",
:core_score=>"No Score",
:performance_rank=>"No Rank",
:industry_modules_passed=>"No Industry Modules Passed"},
{:first_name=>"chris",
:last_name=>"pelnar",
:email=>"chris#gmail.com",
:phone1=>"4072422433",
:gender=>"male",
:veteran=>"true",
:dob=>"1998-09-09",
:core_score=>"729",
:performance_rank=>"Good",
:industry_modules_passed=>"Entry-Service, Entry-Tech"}]
Model:
def self.to_csv(employee_csv_data)
attributes = %w(first_name last_name email phone gender veteran dob core_score performance_rank industry_modules_passed)
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map{ |attr| obj.send(attr) }
end
end
end
When I click the button, it takes me to the blank HTML page without any problem. When I add .csv to the filename in the URL on that page I get the error.
It looks like it's an array of Hashes. To access properties of a hash in Ruby you need to use brackets. Try updating your code to this:
csv << attributes.map{ |attr| obj.send([], attr) }
or more concisely:
csv << attributes.map{ |attr| obj[attr] }
One more thing, in the example you provided, the keys in the hash are symbols which means you may need to convert your attributes to symbols when trying to access them, like this:
csv << attributes.map{ |attr| obj[attr.to_sym] }
I adapted #Ctpelnar1988's answer to determine the attributes dynamically and allow each array item to have different columns:
def array_of_hashes_to_csv(array)
array_keys = array.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << array_keys
array.each do |obj|
csv << array_keys.map{ |attr| obj[attr] }
end
end
end
Example:
puts array_of_hashes_to_csv([
{attr_a: 1, attr_b: 2},
{attr_a: 3, attr_c: 4}
])
attr_a,attr_b,attr_c
1,2,
3,,4
In the more specific "employee_csv_data" context, I think it'd look like this:
def self.to_csv(employee_csv_data)
attributes = employee_csv_data.map(&:keys).flatten.uniq
CSV.generate(headers: true) do |csv|
csv << attributes
employee_csv_data.each do |obj|
csv << attributes.map { |attr| obj[attr] }
end
end
end

wrapping code that runs in Rails environment into a module

I have this code and it works perfectly
require "date"
#past = []
#future = []
#artist = Artist.find(2)
def sort_by_date(artist)
artist.events.each do |event|
if event.date < DateTime.now
#past << event.id
else
#future << event.id
end
end
end
def event_title(arr)
arr.each do |event_id|
e = Event.find(event_id)
artist_names = []
e.artists.each do |artist|
unless artist.name == #artist.name
artist_names << artist.name
end
end
puts "#{e.name} with #{artist_names.join(", ")} at #{(Venue.find(e.venue_id)).name}"
end
end
sort_by_date(#artist)
puts "Upcoming Events: "
event_title(#future)
puts "Past Events: "
event_title(#past)
I want to run wrap this operation into a module, but I'm having trouble understanding how to pass artist_id to it properly. With this command rails runner app/modules/artist_event_sort.rb, I'm getting this error: ``': undefined method sort_by_date' for SortedArtistEvents:Module (NoMethodError). The two methods sort_by_date and event_title worked as they should before I tried wrapping this whole operation up into a module, so that's where I know I've missed something.
module SortedArtistEvents
require "date"
attr_accessor :artist_id
def initialize(artist_id)
#past = []
#future = []
#artist = Artist.find(artist_id)
end
def sort_by_date(artist)
artist.events.each do |event|
if event.date < DateTime.now
#past << event.id
else
#future << event.id
end
end
end
def event_title(arr)
arr.each do |event_id|
e = Event.find(event_id)
artist_names = []
e.artists.each do |artist|
unless artist.name == #artist.name
artist_names << artist.name
end
end
puts "#{e.name} with #{artist_names.join(", ")} at #{(Venue.find(e.venue_id)).name}"
end
end
sort_by_date(#artist)
puts "Upcoming Events: "
self.event_title(#future)
puts "Past Events: "
event_title(#past)
end
class LetsSort
include SortedArtistEvents
end
test_artist_sort = LetsSort.new(2)
It looks like there are a couple things wrong here. You are trying to initialize a module, you can only initialize a class, e.g. class SortedArtistEvents.
If you have this:
module Foo
def bar; end
end
bar is only accessible by including or extending a module or class with Foo. With your error undefined method sort_by_date' for SortedArtistEvents:Module you would have to do
module SortedArtistsEvents
def self.sort_by_date; end
end
to get behavior like SortedArtistsEvents.sort_by_date

Undefined Method in Rails even though the method exist

I have a method in my view helper directory that I am trying to use within a model but i keep getting a undefined method error. I cannot figure out what i am doing wrong.This is my module.
module StbHelper
def gen_csv(stbs)
CSV.generate do |csv|
csv << [
'param1',
'param2'
]
stbs.each do |stb|
health_check = stb.stb_health_checks.last
csv << [
'value1',
'value2'
]
end
end
end
This is the class i want to use the method in.
require 'stb_helper'
class Stb < ActiveRecord::Base
def self.get_notes_data
.
.
.
end
def self.update
.
.
.
end
def self.report(options={})
csv_file = nil
if options == {}
########################################
# This is the line that throws the error
csv_file = StbHelper.gen_csv(Stb.all)
#######################################
else
stbs = []
customers = List.where(id: options[:list])[0].customers
customers.each do |customer|
customer.accounts.each do |account|
stbs += account.stbs
end
end
csv_file = StbHelper.gen_csv(stbs)
end
end
end
You've defined a module, that doesn't require instantiation. You should be able to use it without the StbHelper part (as long as you require the module in the document):
def self.report(options={})
csv_file = nil
if options == {}
########################################
# This is the line that throws the error
csv_file = gen_csv(Stb.all)
#######################################
else
stbs = []
customers = List.where(id: options[:list])[0].customers
customers.each do |customer|
customer.accounts.each do |account|
stbs += account.stbs
end
end
csv_file = gen_csv(stbs)
end
end
But you shouldn't use a helper for this, you can create a normal module and require it the same way.
Edit: Save the module in a new folder called app/modules (and restart the server), save a file called stb_helper.rb with the contents of your module:
module StbHelper
def gen_csv(stbs)
CSV.generate do |csv|
csv << [
'param1',
'param2'
]
stbs.each do |stb|
health_check = stb.stb_health_checks.last
csv << [
'value1',
'value2'
]
end
end
end

Rails 4 - exporting routes to CSV

I'm developing an ecommerce app and I have a csv export feature which exports all product details like name, price, etc. Each product is in one row with a column for each product attribute. I want to add a column to the file which will contain the url of each product. The reason I want this is so I can use this as a product feed that can be submitted to various shopping sites.
Here is my export code in the controller. How do I add a column called route to this? I don't have a route column in the model.
#controller
def productlist
#listings = Listing.all
respond_to do |format|
format.html
format.csv { send_data #listings.to_csv(#listings) }
end
end
#model
def self.to_csv(listings)
wanted_columns = [:sku, :name, :designer_or_brand, :description, :price, :saleprice, :inventory, :category]
CSV.generate do |csv|
csv << ['Product_ID', 'Product_title', 'Designer_or_Brand', 'Description', 'Price', 'SalePrice', 'Quantity_in_stock', 'Category'] + [:Image, :Image2, :Image3, :Image4]
listings.each do |listing|
attrs = listing.attributes.with_indifferent_access.values_at(*wanted_columns)
attrs.push(listing.image.url, listing.image2.try(:url), listing.image3.try(:url), listing.image4.try(:url))
csv << attrs
end
end
end
def self.to_csv(listings)
wanted_columns = [:sku, :name, :designer_or_brand, :description, :price,
:saleprice, :inventory, :category]
header = %w(Product_ID Product_title Designer_or_Brand Description Price
SalePrice Quantity_in_stock Category Image Image2 Image3 Image4 ProductUrl)
CSV.generate do |csv|
csv << header
listings.each do |listing|
attrs = listing.attributes.with_indifferent_access.values_at(*wanted_columns)
<< listing.image.url << listing.image2.try(:url)
<< listing.image3.try(:url) << listing.image4.try(:url)
<< Rails.application.routes.url_helpers.product_url(listing.Product_ID)
csv << attrs
end
end
end
Actually the only difference is last item of array: Rails.application.routes.url_helpers.product_url(listing.Product_ID), where product_url is your route to product#show

Resources