RubyAmf and Rails 3 - ruby-on-rails

I have recently been trying to upgrade my app form Rails 2.3.8 to newly-releases Rails 3.
After going through fixing some Rails 3 RubyAMF doesn't seem to work:
>>>>>>>> RubyAMF >>>>>>>>> #<RubyAMF::Actions::PrepareAction:0x1649924> took: 0.00017 secs
The action '#<ActionDispatch::Request:0x15c0cf0>' could not be found for DaysController
/Users/tammam56/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.0.0/lib/abstract_controller/base.rb:114:in `process'
/Users/tammam56/.rvm/gems/ruby-1.9.2-p0/gems/actionpack-3.0.0/lib/abstract_controller/rendering.rb:40:in `process'
It doesn't seem to be able to find the proper controller. Might have to do with new changes in Rails 3 Router. Do you know how to go about finding the root cause of the problem and/or trying to fix it?
I'm pasting code from RubyAMF where this is happening (Exception happens at the line: #service.process(req, res)):
#invoke the service call
def invoke
begin
# RequestStore.available_services[#amfbody.service_class_name] ||=
#service = #amfbody.service_class_name.constantize.new #handle on service
rescue Exception => e
puts e.message
puts e.backtrace
raise RUBYAMFException.new(RUBYAMFException.UNDEFINED_OBJECT_REFERENCE_ERROR, "There was an error loading the service class #{#amfbody.service_class_name}")
end
if #service.private_methods.include?(#amfbody.service_method_name.to_sym)
raise RUBYAMFExc
eption.new(RUBYAMFException.METHOD_ACCESS_ERROR, "The method {#{#amfbody.service_method_name}} in class {#{#amfbody.service_class_file_path}} is declared as private, it must be defined as public to access it.")
elsif !#service.public_methods.include?(#amfbody.service_method_name.to_sym)
raise RUBYAMFException.new(RUBYAMFException.METHOD_UNDEFINED_METHOD_ERROR, "The method {#{#amfbody.service_method_name}} in class {#{#amfbody.service_class_file_path}} is not declared.")
end
#clone the request and response and alter it for the target controller/method
req = RequestStore.rails_request.clone
res = RequestStore.rails_response.clone
#change the request controller/action targets and tell the service to process. THIS IS THE VOODOO. SWEET!
controller = #amfbody.service_class_name.gsub("Controller","").underscore
action = #amfbody.service_method_name
req.parameters['controller'] = req.request_parameters['controller'] = req.path_parameters['controller'] = controller
req.parameters['action'] = req.request_parameters['action'] = req.path_parameters['action'] = action
req.env['PATH_INFO'] = req.env['REQUEST_PATH'] = req.env['REQUEST_URI'] = "#{controller}/#{action}"
req.env['HTTP_ACCEPT'] = 'application/x-amf,' + req.env['HTTP_ACCEPT'].to_s
#set conditional helper
#service.is_amf = true
#service.is_rubyamf = true
#process the request
rubyamf_params = #service.rubyamf_params = {}
if #amfbody.value && !#amfbody.value.empty?
#amfbody.value.each_with_index do |item,i|
rubyamf_params[i] = item
end
end
# put them by default into the parameter hash if they opt for it
rubyamf_params.each{|k,v| req.parameters[k] = v} if ParameterMappings.always_add_to_params
begin
#One last update of the parameters hash, this will map custom mappings to the hash, and will override any conflicting from above
ParameterMappings.update_request_parameters(#amfbody.service_class_name, #amfbody.service_method_name, req.parameters, rubyamf_params, #amfbody.value)
rescue Exception => e
raise RUBYAMFException.new(RUBYAMFException.PARAMETER_MAPPING_ERROR, "There was an error with your parameter mappings: {#{e.message}}")
end
#service.process(req, res)
#unset conditional helper
#service.is_amf = false
#service.is_rubyamf = false
#service.rubyamf_params = rubyamf_params # add the rubyamf_args into the controller to be accessed
result = RequestStore.render_amf_results
#handle FaultObjects
if result.class.to_s == 'FaultObject' #catch returned FaultObjects - use this check so we don't have to include the fault object module
e = RUBYAMFException.new(result['code'], result['message'])
e.payload = result['payload']
raise e
end
#amf3
#amfbody.results = result
if #amfbody.special_handling == 'RemotingMessage'
#wrapper = generate_acknowledge_object(#amfbody.get_meta('messageId'), #amfbody.get_meta('clientId'))
#wrapper["body"] = result
#amfbody.results = #wrapper
end
#amfbody.success! #set the success response uri flag (/onResult)
end

The best suggestion is to try rails3-amf. It currently is severely lacking in features in comparison to RubyAMF, but it does work and I'm adding new features as soon as they are requested or I have time.

Related

Ruby webapplication, getting "wrong number of arguments (given 3, expected 2)" error

I made some changes in one of the controllers in our webapp. Essentially, the controller was sending out an email to the customer if the order was cancelled, and changed the status of the order in the database. This is how it originally the snippet looked like:
elsif #ac == "mino"
begin
#wifi_order = WifiOrder.find(params["id"])
ApplicationMailer.cancelled_mino(#wifi_order, WifiUser.find_by(email: #wifi_order.email), 10000).deliver_now
#wifi_order = WifiOrder.find(params["id"])
#wifi_order.order_status = "status_cancelled_pending_fees"
#wifi_order.order_status_sub = "status_cancelled_force"
#wifi_order.cancelled_at = DateTime.now
#wifi_order.payment_next = nil
#wifi_order.confirm = nil
#wifi_order.save!
rescue => e
p e.message
flash[:error] = e.message
return
end
This is what I changed it for, because I wanted to send two different kinds of emails depending on the payment method set:
elsif #ac == "mino"
begin
#wifi_order = WifiOrder.find(params["id"])
if #wifi_order.pay_type = "card"
ApplicationMailer.cancelled_mino(#wifi_order, WifiUser.find_by(email: #wifi_order.email), 10000).deliver_now
else
ApplicationMailer.cancelled_mino_paid(#wifi_order, WifiUser.find_by(email: #wifi_order.email), 10000).deliver_now
end
#wifi_order = WifiOrder.find(params["id"])
#wifi_order.order_status = "status_cancelled_pending_fees"
#wifi_order.order_status_sub = "status_cancelled_force"
#wifi_order.cancelled_at = DateTime.now
#wifi_order.payment_next = nil
#wifi_order.confirm = nil
#wifi_order.save!
rescue => e
p e.message
flash[:error] = e.message
return
end
Ever since the changes, when I try to test it, I get this error:
wrong number of arguments (given 3, expected 2)
What did I do wrong?
Why it is happening
The wrong number of arguments you are getting hints at the solution.
It details a method is expecting 2 arguments while you are sending 3.
def say_hello(first_name, last_name)
puts "#{first_name} #{last_name}"
end
say_hello("John", "Doe") # -> will work
say_hello("John", "Von", "Doe") # -> will raise wrong number of arguments error
How to fix it
At first sight, it seems that the method you've added cancelled_mino_paid is the cause of the wrong number of arguments error.
You can fix it in one of two ways:
when defining the cancelled_mino_paid method, make sure it can receive 3 arguments
only send 2 arguments when calling the cancelled_mino_paid method

Rails does not write column

i have a totally strange problem:
Rails 6.0.0
ruby 2.5.7
mysql 8.0.15
it saves the most columns, but not all.
p.errors.full_messages delivers emtpy array and p.valid? delivers true.
but, p.to_geocode does not be set to true in the database, p.location_string is also not saved.
But, all the values, which are advised by the loop (hash.keys.each {..) are correctly stored.
what may there be?
thanks in advance!
Chris
Code:
def write_project(hash)
# MAKE RECORD
p = Project.new
hash.keys.each {|k| p[k.to_sym] = hash[k.to_sym]}
p.created_by = session[:login_name]
p.group = 'antenna_project'
#default_values.each do |k, v|
unless p[k.to_sym].present?
p[k.to_sym] = v
end
end
p.import_id = #import.id
# ERROR HANDLING SAVE
p[:to_geocode] = true
p.location_string = [hash[:street].to_s, hash[:zip].to_s, hash[:city].to_s, 'switzerland'].reject(&:empty?).join(', ')
unless p.save(validate: false)
e = "COULD NOT SAVE RECORD"
log_import_error(nil, e)
return e
end
#import.block_others_until = (DateTime.now + 1.seconds)
#import.save
return "SUCCESS"
end

update function throwing erro(rails)

I am having a problem that when ever I call the update function it throws an error. I have tried to find the solution but I could'nt find it and I can't understad the error. Please tell me what is wrong with the code
The update function is called from this function
def bookmark_request
data = params[:d]
request_bookmarked = Request.getRequest(data)
bookmarked_against_Request = Request.first
request_bookmarked_2 = request_bookmarked
bookmarked_against_Request_2 = bookmarked_against_Request
if bookmarked_against_Request_2[:favourites]
bookmarked_against_Request_2[:favourites] << bookmarked_against_Request[:id]
else
bookmarked_against_Request_2[:favourites] = Array.new
bookmarked_against_Request_2[:favourites] << bookmarked_against_Request[:id]
end
Request.updateRequest(bookmarked_against_Request , bookmarked_against_Request_2)
redirect_to :action => "active"
end
and the update code is this
def updateRequest(request,req_data)
if request.update(req_data)
request
end
end
The error that I am getting is this
**NoMethodError at requests/bookmark_request
undefined method `empty?' for Request:0x007f3fa44c59b0**
The error always comes on the line if request.update(req_data)
Sice I do not have a reputation of 10 so I am posting links to screenshot of the error
http://tinypic.com/r/whbiv7/8
update() method's argument is expected to be a hash. But your req_data argument becomes actually a Request here:
def bookmark_request
bookmarked_against_Request = Request.first
...
bookmarked_against_Request_2 = bookmarked_against_Request
...
end
And Request class has no empty? method. Moreover it might become nil, if there are no Requests at all.

Rspec Ruby on Rails Test File System in model

I have a model that has a method that looks through the filesystem starting at a particular location for files that match a particular regex. This is executed in an after_save callback. I'm not sure how to test this using Rspec and FactoryGirl. I'm not sure how to use something like FakeFS with this because the method is in the model, not the test or the controller. I specify the location to start in my FactoryGirl factory, so I could change that to a fake directory created by the test in a set up clause? I could mock the directory? I think there are probably several different ways I could do this, but which makes the most sense?
Thanks!
def ensure_files_up_to_date
files = find_assembly_files
add_files = check_add_assembly_files(files)
errors = add_assembly_files(add_files)
if errors.size > 0 then
return errors
end
update_files = check_update_assembly_files(files)
errors = update_assembly_files(update_files)
if errors.size > 0 then
return errors
else
return []
end
end
def find_assembly_files
start_dir = self.location
files = Hash.new
if ! File.directory? start_dir then
errors.add(:location, "Directory #{start_dir} does not exist on the system.")
abort("Directory #{start_dir} does not exist on the system for #{self.inspect}")
end
Find.find(start_dir) do |path|
filename = File.basename(path).split("/").last
FILE_TYPES.each { |filepart, filehash|
type = filehash["type"]
vendor = filehash["vendor"]
if filename.match(filepart) then
files[type] = Hash.new
files[type]["path"] = path
files[type]["vendor"] = vendor
end
}
end
return files
end
def check_add_assembly_files(files=self.find_assembly_files)
add = Hash.new
files.each do |file_type, file_hash|
# returns an array
file_path = file_hash["path"]
file_vendor = file_hash["vendor"]
filename = File.basename(file_path)
af = AssemblyFile.where(:name => filename)
if af.size == 0 then
add[file_path] = Hash.new
add[file_path]["type"] = file_type
add[file_path]["vendor"] = file_vendor
end
end
if add.size == 0 then
logger.error("check_add_assembly_files did not find any files to add")
return []
end
return add
end
def check_update_assembly_files(files=self.find_assembly_files)
update = Hash.new
files.each do |file_type, file_hash|
file_path = file_hash["path"]
file_vendor = file_hash["vendor"]
# returns an array
filename = File.basename(file_path)
af = AssemblyFile.find_by_name(filename)
if !af.nil? then
if af.location != file_path or af.file_type != file_type then
update[af.id] = Hash.new
update[af.id]['path'] = file_path
update[af.id]['type'] = file_type
update[af.id]['vendor'] = file_vendor
end
end
end
return update
end
def add_assembly_files(files=self.check_add_assembly_files)
if files.size == 0 then
logger.error("add_assembly_files didn't get any results from check_add_assembly_files")
return []
end
asm_file_errors = Array.new
files.each do |file_path, file_hash|
file_type = file_hash["type"]
file_vendor = file_hash["vendor"]
logger.debug "file type is #{file_type} and path is #{file_path}"
logger.debug FileType.find_by_type_name(file_type)
file_type_id = FileType.find_by_type_name(file_type).id
header = file_header(file_path, file_vendor)
if file_vendor == "TBA" then
check = check_tba_header(header, file_type, file_path)
software = header[TBA_SOFTWARE_PROGRAM]
software_version = header[TBA_SOFTWARE_VERSION]
elsif file_vendor == "TBB" then
check = check_tbb_header(header, file_type, file_path)
if file_type == "TBB-ANNOTATION" then
software = header[TBB_SOURCE]
else
software = "Unified"
end
software_version = "UNKNOWN"
end
if check == 0 then
logger.error("skipping file #{file_path} because it contains incorrect values for this filetype")
asm_file_errors.push("#{file_path} cannot be added to assembly because it contains incorrect values for this filetype")
next
end
if file_vendor == "TBA" then
xml = header.to_xml(:root => "assembly-file")
elsif file_vendor == "TBB" then
xml = header.to_xml
else
xml = ''
end
filename = File.basename(file_path)
if filename.match(/~$/) then
logger.error("Skipping a file with a tilda when adding assembly files. filename #{filename}")
next
end
assembly_file = AssemblyFile.new(
:assembly_id => self.id,
:file_type_id => file_type_id,
:name => filename,
:location => file_path,
:file_date => creation_time(file_path),
:software => software,
:software_version => software_version,
:current => 1,
:metadata => xml
)
assembly_file.save! # exclamation point forces it to raise an error if the save fails
end # end files.each
return asm_file_errors
end
Quick answer: you can stub out model methods like any others. Either stub a specific instance of a model, and then stub find or whatever to return that, or stub out any_instance to if you don't want to worry about which model is involved. Something like:
it "does something" do
foo = Foo.create! some_attributes
foo.should_receive(:some_method).and_return(whatever)
Foo.stub(:find).and_return(foo)
end
The real answer is that your code is too complicated to test effectively. Your models should not even know that a filesystem exists. That behavior should be encapsulated in other classes, which you can test independently. Your model's after_save can then just call a single method on that class, and testing whether or not that single method gets called will be a lot easier.
Your methods are also very difficult to test, because they are trying to do too much. All that conditional logic and external dependencies means you'll have to do a whole lot of mocking to get to the various bits you might want to test.
This is a big topic and a good answer is well beyond the scope of this answer. Start with the Wikipedia article on SOLID and read from there for some of the reasoning behind separating concerns into individual classes and using tiny, composed methods. To give you a ballpark idea, a method with more than one branch or more than 10 lines of code is too big; a class that is more than about 100 lines of code is too big.

Rails - Triggering Flash Warning with method returning true

I'm trying to trigger a warning when a price is entered too low. But for some reason, it always returns true and I see the warning regardless. I'm sure there something wrong in the way I'm doing this as I'm really new to RoR.
In model:
def self.too_low(value)
res = Class.find_by_sql("SELECT price ……. WHERE value = '#{value}'")
res.each do |v|
if #{value} < v.price.round(2)
return true
else
return false
end
end
end
In controller:
#too_low = Class.too_low(params[:amount])
if #too_low == true
flash[:warning] = 'Price is too low.'
end
I would write it somewhat different. You iterate over all items, but you are only interested in the first element. You return from inside the iteration block, but for each element the block will be executed. In ruby 1.9.2 this gives an error.
Also i would propose using a different class-name (Class is used to define a class)
So my suggestion:
Class YourGoodClassName
def self.too_low(amount)
res = YourGoodClassName.find_by_sql(...)
if res.size > 0
res[0].price.round(2) < 1.00
else
true
end
end
end
You can see i test if any result is found, and if it is i just return the value of the test (which is true or false); and return true if no price was found.
In the controller you write something like
flash[:warning] = 'Price is too low' if YourGoodClassName.too_low(params[:amount])

Resources