Roo with rails4 giving undefined method `[]' for nil:NilClass - ruby-on-rails-4

I'm trying to import CSV and Excel files into a rails 4 project (with validation) using the Roo gem, based on http://railscasts.com/episodes/396-importing-csv-and-excel.
I've made some changes to account for Rails4 instead of Rails3 and for changes to Roo, and my ProjectImporter model now looks like:
class ProductImport
include ActiveModel::Model
attr_accessor :file
def initialize(attributes = {})
attributes.each { |name, value| send("#{name}=", value) }
end
def persisted?
false
end
def save
if imported_products.map(&:valid?).all?
imported_products.each(&:save!)
true
else
imported_products.each_with_index do |product, index|
product.errors.full_messages.each do |message|
errors.add :base, "Row #{index + 2}: #{message}"
end
end
false
end
end
def imported_products
#imported_products ||= load_imported_products
end
def load_imported_products
spreadsheet = open_spreadsheet
spreadsheet.default_sheet = spreadsheet.sheets.first
puts "!!! Spreadsheet: #{spreadsheet}"
header = spreadsheet.row(1)
(2..spreadsheet.last_row).map do |i|
row = Hash[[header, spreadsheet.row(i)].transpose]
product = Product.find_by(id: row['id']) || Product.new
product.attributes = row.to_hash.slice(*['name', 'released_on', 'price'])
product
end
end
def open_spreadsheet
case File.extname(file.original_filename)
when ".csv" then
Roo::CSV.new(file.path, nil)
when '.tsv' then
Roo::CSV.new(file.path, csv_options: { col_sep: "\t" })
when '.xls' then
Roo::Excel.new(file.path, nil, :ignore)
when '.xlsx' then
Roo::Excelx.new(file.path, nil, :ignore)
when '.ods' then
Roo::OpenOffice.new(file.path, nil, :ignore)
else
raise "Unknown file type #{file.original_filename}"
end
end
end
When I try to run an import (using test CSV data), it fails on header = spreadsheet.row(1) with the error undefined method '[]' for nil:NilClass. The extra puts statement I've included confirms that spreadsheet itself isn't nil: it gives !!! Spreadsheet: #<Roo::CSV:0x44c2c98>. But if I try to call almost any of the expected methods on it, such as #last_row, it gives me the same undefined method error.
So what am I doing wrong?

I had the same problem, it seems a problem about file enconding, I used this code and it was fixed.
def open_spreadsheet
case File.extname(file.original_filename)
when ".csv" then Roo::CSV.new(file.path, csv_options: {encoding: "iso-8859-1:utf-8"})
when ".xls" then Roo::Excel.new(file.path, nil, :ignore)
when ".xlsx" then Roo::Excelx.new(file.path, nil, :ignore)
else raise "Unknown file type: #{file.original_filename}"
end
end
I hope that helps for you.

Related

Getting uninitialized constant Twilio::REST::RequestError

I keep getting the same error since I upgraded to:
gem 'twilio-ruby', '~> 5.0.0.rc4'
The call was successful set to Twilio, but the getting some error.
app/controllers/home_controller.rb:59:in `rescue in call'
require "rubygems"
require "twilio-ruby"
def call
#twilio = Twilio::REST::Client.new account_sid, auth_token
begin
#call = #twilio.account.calls.create({
:to => ,
:from => twilio_number,
:url => url,
:method => "GET",
:if_machine => "Hangup",
:timeout => "20"
})
# Getting current call status (seems like error here...!)
get_status(#call.sid)
rescue Twilio::REST::RequestError => error
#err_msg = error.message
puts #err_msg
#returned error is like below:
#NameError (uninitialized constant Twilio::REST::RequestError)
end
end
Code for getting current call status:
def get_status(sid)
#twilio = Twilio::REST::Client.new account_sid, auth_token
#call = #twilio.account.calls.get(sid)
puts "Process Status : " + #call.status
return #call.status
end
Please help to figure it out.
Thank you!
For version 5, Try Twilio::REST::RestError.
This is documented here:
There are new classes to rescue errors from. The new library now uses the Twilio::REST::RestError class.

How to Combine many similar methods and views into one

I want to combine similar methods and views into one, but still keep the url name, like the following:
Home/recommends/categories/shopping
Home/recommends/categories/nightview
Home/recommends/categories/food
Home/recommends/categories/area
I don't want to use params like "?something=xyz" in url.
In routes.rb:
resources :recommends, only: :index do
collection do
resources :categories, only: :show, controller: 'recommends' do
collection do
get :food
get :area
get :shopping
get :nightview
end
end
end
end
In controllers:
def food
set_paginator
#recommends = UserRecommend.where(category: "food").order('created_at desc').offset(#offset).limit(#limit).all
#number_of_recommends = UserRecommend.where(category: "food").count
end
def area
set_paginator
#recommends = UserRecommend.where(category: "area").order('created_at desc').offset(#offset).limit(#limit).all
#number_of_recommends = UserRecommend.where(category: "area").count
end
...
In views I have:
food.html.slim
area.html.slim
shopping.slim
nightview.slim
Which are using the same code, just different names in h1:
h1
| Shopping ( or Area or Food... )
= " (#{#number_of_recommends})"
= render partial: "layouts/paginator",
locals: { total_items: #number_of_recommends, per_page: #limit, current_page: #page }
= render partial: "table", locals: { recommends: #recommends }
Can anyone help me refactor this code?
You can (and should) have a single route, a single action, and a single view. The key is to make the variable portion of your URL into an actual variable. You do this using dynamic segments.
First, a single route. There is no need to use resources if you're not actually generating multiple RESTful actions:
get "/recommends/categories/:category" => "categories#show"
You can add criteria on what is allowed for the :category segment:
get "/recommends/categories/:category" => "categories#show", category: /food|area|shopping|nightview/
Next, a single action:
class CategoriesController < ApplicationController
before_action :set_paginator
def show
# params[:category] is "food"/"area"/etc
categories = UserRecommend.where(category: params[:category]).order('created_at desc')
#recommends = categories.offset(#offset).limit(#limit)
#number_of_recommends = categories.count
end
end
Finally, a single view:
# app/views/categories/show.slim
h1
= params[:category].capitalize
= " (#{#number_of_recommends})"
= render partial: "layouts/paginator",
locals: { total_items: #number_of_recommends, per_page: #limit, current_page: #page }
= render partial: "table", locals: { recommends: #recommends }
I would consider it better to use localization to turn the params[:category] into a title, which would give you more control, rather than relying on simple capitalization of the URL segment:
# app/views/categories/show.slim
h1
= t params[:category]
And:
# config/locals/en.yml
en:
categories:
show:
food: 'Food'
area: 'Area'
nightview: 'Night View'

Rails: Activerecord : How to send params for I18n message inside errors.add for custom validation

I use Rails 4 with the Rails-i18n Gem and i want replace my hard coded string "300px" with a placeholder in my language translation file like %{minimum_resolution} in config/locales/de.yml
activerecord:
errors:
models:
organisation:
attributes:
image:
resolution_too_small:"Image Resolution should be at least %{minimum_resolution}"
The value in %{minimum_resolution} should come from my custom validation in
app/models/organisation.rb
def validate_minimum_image_dimensions
if image.present?
logo = MiniMagick::Image.open(image.path)
minimum_resolution = 300
unless logo[:width] > minimum_resolution || logo[:height] > minimum_resolution
errors.add :image, :minimum_image_size
end
else
return false
end
end
How can i get the value from minimum_resolution into my yaml file?
Try this, and let me know
def validate_minimum_image_dimensions
if image.present?
logo = MiniMagick::Image.open(image.path)
minimum_resolution = 300
unless logo[:width] > minimum_resolution || logo[:height] > minimum_resolution
errors.add :image, :resolution_too_small, minimum_resolution: minimum_resolution
end
else
return false
end
end
Anyway, this is the syntax
errors.add :field_name, :message_key, {optional_param1: value1, optional_param2: value2}
and it has to be defined like this
activerecord:
errors:
models:
[your_model]:
attributes:
[field_name]:
[message_key]: "Image Resolution should be at least %{optional_param1} and %{optional_param2}"
Rails 6+ :
The syntax is
errors.add :field_name, :message_key, optional_param1: value1, optional_param2: value2

Rails 4 wicked_pdf generate blank pdf while generating from model

I'm trying to save pdf on server using rails model buts its generate a blank pdf. Earlier did it in controller without problem but now its creating a blank one. Any idea What's i did wrong?
def generate_bulk_pdf
view = ActionView::Base.new(ActionController::Base.view_paths, {})
view.extend(ApplicationHelper)
view.extend(AbstractController::Rendering)
view.extend(Rails.application.routes.url_helpers)
students = Student.all.order('id ASC')
students.each do | aStudent |
pdf = WickedPdf.new.pdf_from_string(
view.render_to_string(
:template => "#{Rails.root.join('templates/challen.pdf.erb')}",
:locals => { '#student' => aStudent }
)
)
save_path = Rails.root.join('pdfs','filename.pdf')
File.open(save_path, 'wb') do |file|
file << pdf
end
end
end
Any idea What's i did wrong? I can't find any solution
A good test is just put a simple line of text in your template and see if you get a PDF with that line. Strip everything back so you just generating a PDF with no coming locals, just that 1 string and let me know.
Here is how I set up mine and it works fine, it might click something :)
def generate_pdf_voucher(voucher, dir_name)
view = ActionView::Base.new(Rails.root.join('app/views'))
view.class.include ApplicationHelper
view.class.include Rails.application.routes.url_helpers
pdf = view.render :pdf => a_name,
:template => 'layouts/pdfs/voucher_pdf',
:layout => 'layouts/pdfs/pdf.html.erb',
:header => {:right => '[page] of [topage]'},
:locals => {:#voucher => voucher}
# then save to a file
pdf = WickedPdf.new.pdf_from_string(pdf)
save_path = Rails.root.join('public', 'pdfs', dir_name, "#{voucher[:user].id.to_s}.pdf")
File.open(save_path, 'wb') do |file|
file << pdf
end
end
pdf.html.erb is the structure of the PDF
voucher_pdf is all the dynamic stuff
If this wasn't helpful, then put a comment on and I will delete it.

CustomFieldFormat Redmine

I created my own CustomFieldFormat :
init.rb
Redmine::CustomFieldFormat.map do |fields|
fields.register
MyCustomFieldFormat.new('my', :label => :label_test, :order => 1 + 0.6)
end
lib/my_custom_field_format.rb
class MyCustomFieldFormat < Redmine::CustomFieldFormat
def format_value(value, field_format)
"test"
end
def show_value(custom_value)
"test"
end
end
I would like to modify the value of the field while updating. (For example, return "test" and not the value stored in the database). But nothing happens, there is always the true value in the field and not "test". Why ?
Thanks