Using the Net-SFTP gem, Ruby 2 and Rails 4
I wrote code that was working in pure ruby, but copied my code over to rails and now I get:
Encoding::UndefinedConversionError: "\xA8" from ASCII-8BIT to UTF-8
What can I change in my code to get this working?
def self.get_recent_file(ftp_file, local_file)
Net::SFTP.start(Config::A_FTP[:domain], Config::A_FTP[:username], :password => Config::A_FTP[:password]) do |sftp|
sftp.download!(ftp_file, local_file)
end
end
Log
Encoding::UndefinedConversionError: "\xA8" from ASCII-8BIT to UTF-8
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/download.rb:339:in `write'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/download.rb:339:in `write'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/operations/download.rb:339:in `on_read'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/request.rb:87:in `call'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/request.rb:87:in `respond_to'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:948:in `dispatch_request'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:911:in `when_channel_polled'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/channel.rb:311:in `call'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/channel.rb:311:in `process'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:222:in `block in preprocess'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:222:in `each'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:222:in `preprocess'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:205:in `process'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `block in loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
... 13 levels...
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:222:in `preprocess'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:205:in `process'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `block in loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-ssh-2.8.0/lib/net/ssh/connection/session.rb:169:in `loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:802:in `loop'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp/session.rb:787:in `connect!'
from /usr/local/opt/rbenv/versions/2.1.0-rc1/lib/ruby/gems/2.1.0/gems/net-sftp-2.1.2/lib/net/sftp.rb:32:in `start'
Code referenced in log from GEM:
https://github.com/net-ssh/net-sftp/blob/master/lib/net/sftp/operations/download.rb#L339
# Called when a read from a file finishes. If the read was successful
# and returned data, this will call #download_next_chunk to read the
# next bit from the file. Otherwise the file will be closed.
def on_read(response)
entry = response.request[:entry]
if response.eof?
update_progress(:close, entry)
entry.sink.close
request = sftp.close(entry.handle, &method(:on_close))
request[:entry] = entry
elsif !response.ok?
raise "read #{entry.remote}: #{response}"
else
entry.offset += response[:data].bytesize
update_progress(:get, entry, response.request[:offset], response[:data])
entry.sink.write(response[:data]) # <~~ Line#339
download_next_chunk(entry)
end
end
This helps me:
def self.get_recent_file(ftp_file, local_file)
local_io = File.new(local_file, mode: 'w', encoding: 'ASCII-8BIT')
Net::SFTP.start(Config::A_FTP[:domain], Config::A_FTP[:username], :password => Config::A_FTP[:password]) do |sftp|
sftp.download!(ftp_file, local_io)
end
local_io.close
end
A combination of user72136's answer and the answer to this question worked for me (my remote file wasn't even ASCII):
def self.get_recent_file(ftp_file, local_file)
local_io = File.new(local_file, mode: 'wb')
Net::SFTP.start(Config::A_FTP[:domain], Config::A_FTP[:username], :password => Config::A_FTP[:password]) do |sftp|
sftp.download!(ftp_file, local_io)
end
local_io.close
end
As line#339 is showing
entry.sink.write(response[:data])
Fix it as :
entry.sink.write(response[:data].force_encoding('ASCII-8BIT').encode('UTF-8'))
Change the line -
sftp.download!(ftp_file, local_file)
to say
sftp.download!(ftp_file, local_file).to_s.encode('UTF-8', {:invalid => :replace, :undef => :replace, :replace => '?'})
This problem is being produced by how Ruby opens text-files by default after Ruby 2.0 version with UTF-8 encoding. Where you open your file you can put:
local_file = Tempfile.new(encoding: 'ascii-8bit')
#or another thing to do is to switch to binary-mode
local_file = Tempfile.new
local_file.binmode
You can also open a binary-file like this:
local_file = File.open('/tmp/local_file', 'wb')
Another solution you can do is to pass to the gem-code the filepath, instead of an open file:
def self.get_recent_file(ftp_file, local_file)
Net::SFTP.start(Config::A_FTP[:domain], Config::A_FTP[:username], :password => Config::A_FTP[:password]) do |sftp|
sftp.download!(ftp_file, local_file.path)
end
end
Related
I generated a simple script that on other occasions has worked for me, but this is because the amount of information in the loop generates a NoMemoryError error.
I have 16 GB of memory and also a lot of virtual memory available. When I perform the test, the RAM memory is completely filled.
The script is:
require 'rest-client'
require 'json'
require 'open-uri'
require 'csv'
def self.qos7705egressdiscard_summary
xml = File.read("#{Rails.root}/public/Discard_qos7705egress.xml")
data = RestClient.post("http://10.140.255.1:8080/xmlapi/invoke", xml,{:"Content-Type" => 'application/soap+xml'})
data_parsed = Hash.from_xml(data)
return data_parsed
end
def self.samqos7705egressdiscardtotal_table
tabletotal = Hash.new
data_stats = qos7705egressdiscard_summary['Envelope']['Body']['findResponse']['result']['service.SapEgrQosQueueStatsLogRecord']
data_stats.map do |qosdiscard_device|
#devicetotal = qosdiscard_device["monitoredObjectSiteName"]
#servicetotal = qosdiscard_device["monitoredObjectPointer"]
#porttotal = qosdiscard_device["displayedName"]
#queueIdtotal = qosdiscard_device["queueId"]
#discardinproftotal = qosdiscard_device["droppedInProfOctets"].to_i
#discardoutproftotal = qosdiscard_device["droppedOutProfOctets"].to_i
time_unixtotal = (qosdiscard_device["timeCaptured"]).to_i/1000
#timeCapturedtotal = Time.at(time_unixtotal).strftime("%B %e, %Y at %I:%M %p")
#discardtotal = #discardinproftotal + #discardoutproftotal
#device_int_stats_total = (#devicetotal+#porttotal+#queueIdtotal).to_s
hash = Hash[devicetotal: #devicetotal, servicetotal: #servicetotal, porttotal: #porttotal, queueIdtotal: #queueIdtotal, discardtotal: #discardtotal, device_int_stats_total: #device_int_stats_total, timeCapturedtotal: #timeCapturedtotal, time_unixtotal: time_unixtotal]
tabletotal << hash
#tabletotal.write(hash)
end
end
The exact error is:
NoMemoryError: failed to allocate memory
from /usr/local/lib/ruby/2.2.0/irb/inspector.rb:108:in `inspect'
from /usr/local/lib/ruby/2.2.0/irb/inspector.rb:108:in `block in <module:IRB>'
from /usr/local/lib/ruby/2.2.0/irb/inspector.rb:101:in `call'
from /usr/local/lib/ruby/2.2.0/irb/inspector.rb:101:in `inspect_value'
from /usr/local/lib/ruby/2.2.0/irb/context.rb:383:in `inspect_last_value'
from /usr/local/lib/ruby/2.2.0/irb.rb:661:in `output_value'
from /usr/local/lib/ruby/2.2.0/irb.rb:490:in `block (2 levels) in eval_input'
from /usr/local/lib/ruby/2.2.0/irb.rb:623:in `signal_status'
from /usr/local/lib/ruby/2.2.0/irb.rb:486:in `block in eval_input'
from /usr/local/lib/ruby/2.2.0/irb/ruby-lex.rb:245:in `block (2 levels) in each_top_level_statement'
from /usr/local/lib/ruby/2.2.0/irb/ruby-lex.rb:231:in `loop'
from /usr/local/lib/ruby/2.2.0/irb/ruby-lex.rb:231:in `block in each_top_level_statement'
from /usr/local/lib/ruby/2.2.0/irb/ruby-lex.rb:230:in `catch'
from /usr/local/lib/ruby/2.2.0/irb/ruby-lex.rb:230:in `each_top_level_statement'
from /usr/local/lib/ruby/2.2.0/irb.rb:485:in `eval_input'
from /usr/local/lib/ruby/2.2.0/irb.rb:395:in `block in start'
from /usr/local/lib/ruby/2.2.0/irb.rb:394:in `catch'
from /usr/local/lib/ruby/2.2.0/irb.rb:394:in `start'
from /usr/local/lib/ruby/gems/2.2.0/gems/railties-4.2.0/lib/rails/commands/console.rb:110:in `start'
from /usr/local/lib/ruby/gems/2.2.0/gems/railties-4.2.0/lib/rails/commands/console.rb:9:in `start'
from /usr/local/lib/ruby/gems/2.2.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:68:in `console'
from /usr/local/lib/ruby/gems/2.2.0/gems/railties-4.2.0/lib/rails/commands/commands_tasks.rb:39:in `run_command!'
from /usr/local/lib/ruby/gems/2.2.0/gems/railties-4.2.0/lib/rails/commands.rb:17:in `<top (required)>'
from bin/rails:4:in `require'
from bin/rails:4:in `<main>'Maybe IRB bug!
On line 25 I added this tabletotal.write (hash) to be able to write it on disk and not in memory but I got the following error:
NoMethodError: undefined method `write' for {}:Hash
What is the problem here? Also how can I fix it?
Start by making it less cr*p:
def extract_data(input)
{
devicetotal: input["monitoredObjectSiteName"],
servicetotal: input["monitoredObjectPointer"],
porttotal: input["displayedName"],
queueIdtotal: input["queueId"],
discardinproftotal: input["droppedInProfOctets"].to_i,
discardoutproftotal: input["droppedOutProfOctets"].to_i,
time_unixtotal: input["timeCaptured"].to_i/1000
}.tap do |h|
h[:timeCapturedtotal] = Time.at(h[:time_unixtotal]).strftime("%B %e, %Y at %I:%M %p"),
h[:discardtotal] = h[:discardinproftotal] + h[:discardoutproftotal]
h[:device_int_stats_total] =(h[:devicetotal]+h[:porttotal]+h[:queueIdtotal]).to_s
end
end
This method is really easy to test since you just insert input and write assertions about the output.
If you want to map and apply this to the input array you would do:
data_stats.map(&:extract_data)
Your code tries to output a hash but uses the shovel operator like on a array. You need to decide if the appropiate output is an array or a hash.
I need all those variables because I use them in a html in table
format.
This won't work - the instance variables will only contain the values from the last element as they get overwritten in each iteration.
You instead need to iterate over an array of hashes or objects in the view:
<% #stats.each do |s| %>
<tr>
<td><%= s[:devicetotal] %></td>
# ...
</tr>
<% end %>
If you really want to use instance varibles you need to create a object instance for each element:
class DataStat
include ActiveModel::AttributeAssignment
attr_accessor :devicetotal, :servicetotal # ...
def self.from_import(input)
self.new(
devicetotal: input["monitoredObjectSiteName"],
servicetotal: input["monitoredObjectPointer"],
# ...
)
end
end
#stats = data_stats.map { |ds| DataStat.from_import(ds) }
You also need to deal with the issue that you´re running out of memory since you are just slop converting the whole XML document into a Ruby hash. You need to parse it with Nokogiri instead and extract what you actually need.
I'm getting:
ArgumentError invalid byte sequence in UTF-8
With my Resque job
Below is my stack trace:
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1780:in `sub!'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1780:in `block in shift'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1774:in `loop'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1774:in `shift'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1716:in `each'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1730:in `to_a'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1730:in `read'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/2.0.0/csv.rb:1291:in `parse'
C:/BitNami/rubystack-2.0.0-4/projects/virtual_exhibition/app/jobs/users.rb:14:in `parse_csv'
C:/BitNami/rubystack-2.0.0-4/projects/virtual_exhibition/app/jobs/users.rb:6:in `perform'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/gems/2.0.0/gems/resque-status-0.4.2/lib/resque/plugins/status.rb:161:in `safe_perform!'
C:/BitNami/rubystack-2.0.0-4/ruby/lib/ruby/gems/2.0.0/gems/resque-status-0.4.2/lib/resque/plugins/status.rb:137:in `perform'
Also below is my job getting called
class UserJob
include Resque::Plugins::Status
def perform
puts "Parsing CSV and updating..."
parse_csv
puts "Update finished..."
end
def parse_csv
#counter = 0
#row = []
csv_text = File.read("#{Rails.public_path}/careersfair.csv").encode('UTF-8')
csv = CSV.parse(csv_text, headers: false)
csv.each do |row|
user = User.find_by_email row[3]
puts user.inspect
if user.present?
user.update(:first_name => row[0], :last_name => row[1], :industry => row[2], :event_ids => 1, :skip_invitation => true)
puts #counter += 1
else
puts "Not found - #{row[3]}"
end
end
end
end
It seems CSV.parse is failing.
Is there a reason why this is happening?
I think your csv file has some invalid characters. Change "csv_text" into the follwing line.
csv_text = File.read("#{Rails.public_path}/careersfair.csv")encode("UTF-8", invalid: :replace, undef: :replace, replace: "?")
If you couldn't solve that by this way, I assume the csv file is not UTF-8.
If you're on Linux, try file -i filename.txt. You can see encoding of the file.
require "iconv"
conv = Iconv.new("UTF-8//IGNORE","ENCODING_OF_YOUR_FILE")
csv_text = File.read("#{Rails.public_path}/careersfair.csv")
text = conv.iconv(csv_text)
I have the following written in ruby
t = Time.now
t.strftime("%Y-%d-%m")
SCHEDULER.every '1m', :first_in => 0 do |job|
send_event('gmail_gh', {current: gmail.inbox.count(:after => Date.parse(t)), older: gmail.inbox.count})
But i get this error
scheduler caught exception:
can't convert Time into String
/var/dashing/cdmdash/jobs/gmail_gh.rb:21:in `parse'
/var/dashing/cdmdash/jobs/gmail_gh.rb:21:in `block in <top (required)>'
/usr/local/rvm/gems/ruby-1.9.3-p327/gems/rufus-scheduler-2.0.17/lib/rufus/sc/jobs.rb:231:in `call'
/usr/local/rvm/gems/ruby-1.9.3-p327/gems/rufus-scheduler-2.0.17/lib/rufus/sc/jobs.rb:231:in `trigger_block'
/usr/local/rvm/gems/ruby-1.9.3-p327/gems/rufus-scheduler-2.0.17/lib/rufus/sc/jobs.rb:191:in `block in trigger'
/usr/local/rvm/gems/ruby-1.9.3-p327/gems/rufus-scheduler-2.0.17/lib/rufus/sc/scheduler.rb:416:in `call'
/usr/local/rvm/gems/ruby-1.9.3-p327/gems/rufus-scheduler-2.0.17/lib/rufus/sc/scheduler.rb:416:in `block in trigger_job'
I think it has something to do with the t variable and it not being a truing, I am new to Ruby so I am abit stuck
If you look at the gem documentation, you will see that the :after and :before params take in a date in the format of YYYY-MM-DD.
From the gem Readme:
gmail.inbox.count(:after => Date.parse("2010-02-20"), :before => Date.parse("2010-03-20"))
gmail.inbox.count(:on => Date.parse("2010-04-15"))
Your code is passing in YYYY-DD-MM which is likely causing the error.
Edit
When you call strftime on a datetime object, it doesn't modify the object - only returns the string notation based on format you give.
As a result, the Date.parse(t) is still getting Time.now was a parameter.
Try this:
t = Time.now.strftime("%Y-%m-%d")
Date.parse(t)
I'm developing a plugin for a management framework and when I start the webrick server in the development mode, a strange error raises(stack level too deep). It happens when an action (e.g. the show action) starts to render a template. Unfortunately i have no idea why this is happen.
SystemStackError in Stories#show
Showing vendor/plugins/stories/app/views/stories/show.rhtml where line #5 raised:
stack level too deep
Extracted source (around line #5):
5: link_to_if_authorized 'aa', {:controller => "stories", :action => "index", :id => #story.id, :project_id => #story.project.id}, :title => l(:view_story), :class => 'icon icon-zoom-out'
6: link_to_if_authorized 'bb', {:controller => "stories", :action => "edit", :id => #story.id, :project_id => #story.project.id}, :title => l(:button_edit), :class => 'icon icon-edit'
7: link_to 'bb', {:id => #story.id, :project_id => #story.project.id}, :confirm => 'Really delete?', :method => :delete, :class => 'icon icon-del' if User.current.allowed_to? (:delete_stories, #project)
RAILS_ROOT: /home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN
Application Trace | Framework Trace | Full Trace
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/attribute_methods.rb:248:in method_missing'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/attribute_methods.rb:249:in method_missing'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/associations /association_proxy.rb:215:in send'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/associations/association_proxy.rb:215:in method_missing'
/home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/views/stories/show.rhtml:5:in _run_rhtml_vendor47plugins47stories47app47views47stories47show46rhtml'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:34:in send'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:34:in render'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:306:in with_template'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/renderable.rb:30:in render'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/template.rb:205:in render_template'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:265:in render'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:348:in _render_with_layout'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_view/base.rb:262:in render'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1252:in render_for_file'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:936:in render_without_benchmark'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:51:in render'
/var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
/usr/lib/ruby/1.8/benchmark.rb:308:in realtime'
/var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:51:in render'
/home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/controllers/stories_controller.rb:104:in show'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:135:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:135:in custom'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:179:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:179:in respond'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:173:in each'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:173:in respond'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/mime_responds.rb:107:in respond_to'
/home/haendwic/Documents/Aptana Studio 3 Workspace/1.4-stable-SVN/vendor/plugins/stories/app/controllers/stories_controller.rb:102:in show'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in send'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:1333:in perform_action_without_filters'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:617:in call_filters'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:610:in perform_action_without_benchmark'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue'
/var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
/usr/lib/ruby/1.8/benchmark.rb:308:in realtime'
/var/lib/gems/1.8/gems/activesupport-2.3.14/lib/active_support/core_ext/benchmark.rb:17:in ms'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/benchmarking.rb:68:in perform_action_without_rescue'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/rescue.rb:160:in perform_action_without_flash'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/flash.rb:151:in perform_action'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in send'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:532:in process_without_filters'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/filters.rb:606:in process'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:391:in process'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/base.rb:386:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/routing/route_set.rb:438:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:87:in dispatch'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:121:in _call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:130:in build_middleware_stack'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:29:in call'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:29:in call'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/connection_adapters/abstract/query_cache.rb:34:in cache'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:9:in cache'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/query_cache.rb:28:in call'
/var/lib/gems/1.8/gems/activerecord-2.3.14/lib/active_record/connection_adapters/abstract/connection_pool.rb:361:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/string_coercion.rb:25:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/head.rb:9:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/methodoverride.rb:24:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/params_parser.rb:15:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/session /cookie_store.rb:99:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/failsafe.rb:26:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in synchronize'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/lock.rb:11:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:114:in call'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/reloader.rb:34:in run'
/var/lib/gems/1.8/gems/actionpack-2.3.14/lib/action_controller/dispatcher.rb:108:in call'
/var/lib/gems/1.8/gems/rails-2.3.14/lib/rails/rack/static.rb:31:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:47:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:41:in each'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/urlmap.rb:41:in call'
/var/lib/gems/1.8/gems/rails-2.3.14/lib/rails/rack/log_tailer.rb:17:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/content_length.rb:13:in call'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/handler/webrick.rb:48:in service'
/usr/lib/ruby/1.8/webrick/httpserver.rb:104:in service'
/usr/lib/ruby/1.8/webrick/httpserver.rb:65:in run'
/usr/lib/ruby/1.8/webrick/server.rb:173:in start_thread'
/usr/lib/ruby/1.8/webrick/server.rb:162:in start'
/usr/lib/ruby/1.8/webrick/server.rb:162:in start_thread'
/usr/lib/ruby/1.8/webrick/server.rb:95:in start'
/usr/lib/ruby/1.8/webrick/server.rb:92:in each'
/usr/lib/ruby/1.8/webrick/server.rb:92:in start'
/usr/lib/ruby/1.8/webrick/server.rb:23:in start'
/usr/lib/ruby/1.8/webrick/server.rb:82:in start'
/var/lib/gems/1.8/gems/rack-1.1.3/lib/rack/handler/webrick.rb:14:in run'
/var/lib/gems/1.8/gems/rails-2.3.14/lib/commands/server.rb:111
script/server:3:in require'
script/server:3
Request
Parameters:
{"project_id"=>"1",
"id"=>"2"}
Show session dump
Response
Headers:
{"Cache-Control"=>"no-cache",
"Content-Type"=>"text/html"}
and here is the action from the controller
def show
#edit_allowed = User.current.allowed_to?(:edit_stories, #project)
respond_to do |format|
format.html {
render :template => 'stories/show'
}
format.api
format.pdf { send_data(story_to_pdf(#story), :type => 'application/pdf', :filename => "#{#project.identifier}-#{#story.id}.pdf") }
end
end
and at last a part of the view
link_to_if_authorized 'aa', {:controller => "stories", :action => "index", :id => #story.id, :project_id => #story.project.id}, :title => l(:view_story), :class => 'icon icon-zoom-out'
link_to_if_authorized 'bb', {:controller => "stories", :action => "edit", :id => #story.id, :project_id => #story.project.id}, :title => l(:button_edit), :class => 'icon icon-edit'
link_to 'bb', {:id => #story.id, :project_id => #story.project.id}, :confirm => 'Really delete?', :method => :delete, :class => 'icon icon-del' if User.current.allowed_to?(:delete_stories, #project)
Maybe it's important to say, that in the production mode the plugin is stable and routes the actions (incl. show) correctly
This is typically caused by you (or others) patching core models (probably the Project model) from a plugin without taking the rails reloader into account.
If you override methods (e.g. using alias_method_chain) and your patch is loaded twice, you can easily create an infinite loop when calling the old method.
# This is our initial class
class MyClass
def foo
puts "original foo"
end
end
module Patch
def self.included(base)
base.alias_method_chain :foo, :feature
end
def foo_with_feature
foo_without_feature # call the "original" method
puts "foo with feature"
end
end
# patch the class
MyClass.send(:include, Patch)
# Now call the patched method
MyClass.new.foo
# prints:
# original foo
# foo with feature
foo now refers to the method foo_with_feature while the original foo method now is accessible from foo_without_feature
All right, until now everything looked good. Now let's see what happens, if we load our patch again
# patch again
MyClass.send(:include, Patch)
# And call the method again
MyClass.new.foo
# SystemStackError: stack level too deep
# from (irb):7:in `foo_without_feature'
# from (irb):7:in `foo'
# from (irb):27
You see a SystemStackError caused by an infinite loop. This is because after the second loading of the patch, foo_without_feature now refers to the foo_with_feature method from the first patch. When calling it, it will over and over again call foo_without_feature until the stack is full.
You said that it only crashes on the second request. This is exactly the typical behavior when something is odd on the class reloader. By default, Rails will reload all classes on every request in development mode but only once in production mode.
Taming the rails reloader is a bit tricky sometimes. As some general guidelines, you should
use require_dependency instead of require when loading patches
load your patches using Dispatcher.to_prepare
always declare the patched classes unloadable
The most critical part is using Dispatcher.to_prepare. It is a callback which is called once in production mode and before each request in development mode and is thus the ideal place for loading patches.
As a side note though: When using Redmine 2 (or the upcoming ChiliProject 4), i.e. Rails 3, the class patching will be rather different from this approach - most probably easier.
The "stack level too deep" error means that you have a stack overflow (it happens often enough to have a site named after it). That occurs when a function calls itself indefinitely or when two functions call each other indefinitely.
Your error happens on line 5, so I'd check the source for link_to_if_authorized. Something in there is causing an infinite loop.
I haven't used rails since version 1.2 or so and a few things have changed. I have an issue where I am trying to save an empty model to get validation errors on attributes using :validates_presence_of and instead I am getting the error 'can't convert HashWithIndifferentAccess into String'. I will attempt to simplify my code to get the point across as tersely as possible...
my model:
class Project < ActiveRecord::Base
validates_presence_of :title, :description
validates_uniqueness_of :title
has_one :address
accepts_nested_attributes_for :address, :allow_destroy => true
end
Child model:
class Address < ActiveRecord::Base
validates_presence_of :title, :street
belongs_to :project
end
controller:
class ProjectsController < ApplicationController
def create
#project = Project.new(params[:project])
if #project.save
flash[:notice] = #project.title + ' successfully created'
redirect_to projects_path
else
render :action => 'new'
end
end
end
view:
<%= error_messages_for 'project' %>
<% form_for #project do |f| %>
<table width="100%" cellpadding="3" cellspacing="0">
<tr>
<td class="adminlabel">
<label for="Title">Title</label>
</td>
<td class="adminbody">
<%= f.text_field :title %>
</td>
</tr>
.....
<% f.fields_for :address do |address| %>
....
This code adds and updates just fine as long as I fill in all the required fields, if I leave any blank I get the error mentioned above, not the most graceful way of alerting users there is an issue ;)
Request params:
{"commit"=>"Save",
"project"=>{"title"=>"",
"notes"=>"",
"description"=>"",
"address_attributes"=>{"city"=>"",
"zip"=>"",
"title"=>"",
"country"=>"",
"suite"=>"",
"street"=>"",
"state"=>""}},
"authenticity_token"=>"iNPQZrf/oBv22vaI0toTGhknwx0aAU3BSvnIh6qgYQ8="}
I have been searching for days and am at my wits end, if anyone can shed a little light on this for me I would greatly appreciate it.
Thanks in advance!
Brendan
PS - the stack trace as requested:
.../app/controllers/projects_controller.rb:27:in `+'
.../app/controllers/projects_controller.rb:27:in `create'
../actionpack-2.3.4/lib/action_controller/base.rb:1331:in `send'
../actionpack-2.3.4/lib/action_controller/base.rb:1331:in `perform_action_without_filters'
../actionpack-2.3.4/lib/action_controller/filters.rb:617:in `call_filters'
../actionpack-2.3.4/lib/action_controller/filters.rb:610:in `perform_action_without_benchmark'
../actionpack-2.3.4/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:17:in `ms'
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:10:in `realtime'
../activesupport-2.3.4/lib/active_support/core_ext/benchmark.rb:17:in `ms'
../actionpack-2.3.4/lib/action_controller/benchmarking.rb:68:in `perform_action_without_rescue'
../actionpack-2.3.4/lib/action_controller/rescue.rb:160:in `perform_action_without_flash'
../actionpack-2.3.4/lib/action_controller/flash.rb:146:in `perform_action'
../actionpack-2.3.4/lib/action_controller/base.rb:532:in `send'
../actionpack-2.3.4/lib/action_controller/base.rb:532:in `process_without_filters'
../actionpack-2.3.4/lib/action_controller/filters.rb:606:in `process'
../actionpack-2.3.4/lib/action_controller/base.rb:391:in `process'
../actionpack-2.3.4/lib/action_controller/base.rb:386:in `call'
../actionpack-2.3.4/lib/action_controller/routing/route_set.rb:437:in `call'
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:87:in `dispatch'
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:121:in `_call'
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:130:in `build_middleware_stack'
../activerecord-2.3.4/lib/active_record/query_cache.rb:29:in `call'
../activerecord-2.3.4/lib/active_record/query_cache.rb:29:in `call'
../activerecord-2.3.4/lib/active_record/connection_adapters/abstract/query_cache.rb:34:in `cache'
..activerecord-2.3.4/lib/active_record/query_cache.rb:9:in `cache'
../activerecord-2.3.4/lib/active_record/query_cache.rb:28:in `call'
../activerecord-2.3.4/lib/active_record/connection_adapters/abstract/connection_pool.rb:361:in `call'
../rack-1.0.1/lib/rack/head.rb:9:in `call'
../rack-1.0.1/lib/rack/methodoverride.rb:24:in `call'
../actionpack-2.3.4/lib/action_controller/params_parser.rb:15:in `call'
../actionpack-2.3.4/lib/action_controller/session/cookie_store.rb:93:in `call'
../actionpack-2.3.4/lib/action_controller/failsafe.rb:26:in `call'
../rack-1.0.1/lib/rack/lock.rb:11:in `call'
../rack-1.0.1/lib/rack/lock.rb:11:in `synchronize'
../rack-1.0.1/lib/rack/lock.rb:11:in `call'
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:114:in `call'
../actionpack-2.3.4/lib/action_controller/reloader.rb:34:in `run'
../actionpack-2.3.4/lib/action_controller/dispatcher.rb:108:in `call'
../rails-2.3.4/lib/rails/rack/static.rb:31:in `call'
../rack-1.0.1/lib/rack/urlmap.rb:46:in `call'
../rack-1.0.1/lib/rack/urlmap.rb:40:in `each'
../rack-1.0.1/lib/rack/urlmap.rb:40:in `call'
../rails-2.3.4/lib/rails/rack/log_tailer.rb:17:in `call'
../rack-1.0.1/lib/rack/content_length.rb:13:in `call'
../rack-1.0.1/lib/rack/chunked.rb:15:in `call'
../rack-1.0.1/lib/rack/handler/mongrel.rb:64:in `process'
..... etc.
A bit lengthy, sorry ;)
Try adding
#project.address.build
to your controller. You need to instantiate the address object.
The trace doesn't lie. You're having issues with a line 27. The code you posted doesn't have 37 lines, but based on the trace I'm willing to bet it's this line. Because none others have have a '+'. If there was an implicit + its caller would've been listed in the trace.
flash[:notice] = #project.title + ' successfully created'
It's odd that it's reaching that point based on your validations. validates_presence_of should add errors on attributes that are either nil, false or "". Does the validation fail as expected when run in the console?
Here's a console friendly version of your code that could be used to track down your issue. You should be able to just paste it into the console to help track down your issue.
params = {
"project"=> {
"title"=>"", "notes"=>"","description"=>"",
"address_attributes"=>{
"city"=>"", "zip"=>"","title"=>"","country"=>"",
"suite"=>"","street"=>"","state"=>""
}
}
flash = {}
#project = Project.new(params[:project])
if #project.save
flash[:notice] = #project.title + ' successfully created'
puts "Saved. Flash: #{flash[:notice]}"
else
puts "validations failed:"
puts #project.errors.full_messages.map{|m| "\t#{m}"}.join("\n")
end