Rails 3 AJAX: wrong constant name - ajax

I am trying to do Ajax login with Devise, as explained here: http://jessehowarth.com/2011/04/27/ajax-login-with-devise#comment-5 (see comment from jBeasley).
My controller is attempting to return
class Users::SessionsController < Devise::SessionsController
def failure
render :json => {:success => false, :errors => ["Login failed."]}
end
end
which results in this error:
NameError (wrong constant name ["{\"success\":false,\"errors\":[\"Login failed.\"]}"]Controller):
and Firebug showing [500 Internal Server Error].
How can I fix this? I am running Rails 3.1 and devise 1.4.5.
Thanks!!

Did you do the step recommended by Jeff Poulton in comment #4? The :recall option in 1.4.5 looks to be completely incompatible to older versions. It now requires you send the controller, whereas in the tutorial you're following he just sends the action (the old way).
In your case, :recall => :failure must be changed to :recall => "users/sessions#failure" in Devise 1.4.5.
This is because of the way the controller for the failure action is determined. In older versions, it was simply pulled from the params.
def recall_controller
"#{params[:controller]}.camelize}Controller".constantize
end
# called via recall_controller.action(warden_options[:recall]).call(env)
In 1.4.5, it expects a string specifying the controller and action, in the style of routes:
def recall_app(app)
controller, action = app.split('#')
controller_name = ActiveSupport::Inflector.camelize(controller)
controlller_klass = ActiveSupport::Inflector.constantize("#{controller_name}Controller")
controller_klass.action(action)
end
# called via recall_app(warden_options[:recall]).call(env)
It would seem as though your app is actually passing the JSONified hash of options to recall_app, which, lacking a '#', isn't being split, and the entire string is concatenated to "Controller" to attempt to ascertain the failure controller's class.

You are missing the return in
def failure
return render:json => {:success => false, :errors => ["Login failed."]}
end
Does that make a difference?

Related

How can I mock something that "does not implement" a particular method?

The Background:
I'm trying to use cucumber to do some test-driven (or behavior-driven) development around an interface to AWS, in ruby.
So, I have a step definition that looks like this:
Then(/^the mock object should have had :(.*?) called, setting "(.*?)" to "(.*?)"$/) do |method, param, value|
expect(#mock).to receive(method.to_sym).with(hash_including(param, value))
end
Where #mock was previously set using:
#mock = instance_double(AWS::AutoScaling::Client)
And where I invoke this step definition with a feature line like:
And the mock object should have had :update_auto_scaling_group called, setting "auto_scaling_group_name" to "Some-test-value"
When that step gets run, it gets the following error (leaving out the full error, as I believe this is the most relevant part):
AWS::AutoScaling::Client does not implement: update_auto_scaling_group (RSpec::Mocks::MockExpectationError)
I see that indeed, the checks that RSpec runs (as traced back from where the RSpec::Mocks::MockExpectationError gets thrown) are at least correctly reporting the information that they get from the class:
[1] pry(main)> require 'aws-sdk'
=> true
[2] pry(main)> klass = AWS::AutoScaling::Client
=> AWS::AutoScaling::Client
[3] pry(main)> klass.public_method_defined? "update_auto_scaling_group"
=> false
[4] pry(main)> klass.private_method_defined? "update_auto_scaling_group"
=> false
[5] pry(main)> klass.protected_method_defined? "update_auto_scaling_group"
=> false
And yet, if we ask an actual instance, it lets us know that this is a method it would respond to:
[6] pry(main)> x = klass.new
=> #<AWS::AutoScaling::Client::V20110101>
[7] pry(main)> x.respond_to? "update_auto_scaling_group"
=> true
Even while it doesn't say that about just anything:
[8] pry(main)> x.respond_to? "bogus"
=> false
First questions:
So... is this a bug in the AWS::AutoScaling::Client code (or really, probably here), for not defining the methods in a way that the extant checks ({public,private,protected}_method_defined?) would come back true?
Or perhaps a bug in RSpec's "doubles", for not doing all the checking it could do to try to find out that this is indeed a method that's callable in an instance of that class?
Or perhaps it's simply something that I'm doing wrong here? Other?
More generally:
How can I write tests for the code I'm writing, to ensure that it's making calls to what will be an AWS::AutoScaling::Client instance, with the correct parameters (as defined in several checks that I have)? Are there alternate ways I can write my step definitions that would make this work? Alternative ways to create my mock objects? Other?
I've found a way to dynamically mix in the methods I needed to mock
You could do this with empty methods and then stub them, or just include the stubs in the mixin
require 'rails_helper'
RSpec.describe "users/sessions/new.html.erb", :type => :view do
it "displays login form" do
module DeviseUserBits
def resource
#_DeviseUserBitsUser ||= User.new
end
def resource_name
:user
end
def devise_mapping
Devise.mappings[:user]
end
end
view.class.include DeviseUserBits
render
expect(rendered).to match /form/
end
end
It just adds methods on/after instantiating. It's pretty legal, all ruby classes/objects are open.
Proper answer - you do not want to test what you are trying to test in duck-typed language with open classes and objects. It just does not make sense.
The version 1 AWS SDK for Ruby uses #method_missing as a delegate for building and sending requests. The methods a client responds to are defined in an API definition. This eliminates boiler-plate code, but causes problems if you are trying to reflect the available methods at runtime.
Option A: Use a regular double and apply your assertions on the test double.
Option B: Use the mocking feature of the SDK via AWS.stub! When stubbing is enabled, all clients constructed will respond to their regular methods, but will return dummy responses (empty hashes and arrays). This approach provides the useful ability to specify the data to return from a stub. You can even create a stub response for the express purpose of returning from an assertion.
Going with Option B:
# use `:stub_requests` or call Aws.stub!
as = AWS::AutoScaling::Client.new(:stub_requests: true)
# validates parameters as normal, but returns empty response data
as.update_auto_scaling_group(auto_scaling_group_name: 'name')
#=> {}
# You can access the stub response for any operation by name:
stub = as.stub_for(:describe_auto_scaling_groups)
stub.data[:auto_scaling_group_names] = ["Group1", "Group2"]
# Now calling that operation will return the stubbed data
resp = as.describe_auto_scaling_groups
resp.auto_scaling_group_names
#=> ['Group1', 'Group2']
If you need to assert a method is called against the client, you can do so normally, returning the stubbed response:
expect(#client).to receive(:describe_auto_scaling_groups).
with(hash_including(param, value)).
and_return(#client.stub_for(:describe_auto_scaling_groups))

Paperclip in Rails 4 - Strong Parameters Forbidden Attributes Error

Having a problem with a Paperclip upload in Rails 4 - failing on ForbiddenAttributesError (strong parameters validation). Have the latest paperclip gem and latest rails 4 gems.
I have a model "Image" with an attached file "upload" in the model:
has_attached_file :upload, :styles => { :review => ["1000x1200>", :png], :thumb => ["100x100>", :png]}, :default_url => "/images/:style/missing.png"
The image model was created with a scaffold, and I added paperclip migrations. The form partial was updated to use
f.file_field :upload
the form generates what appears to be a typical set of paperclip params, with the image param containing the upload. I am also passing a transaction_id in the image model, so it should be permitted. But that's it - the image and the transaction ID.
I expected to be able to write the following in my controller to whitelist my post - but it failed:
def image_params
params.require(:image).permit(:transaction_id, :upload)
end
So I got more explicit - but that failed too:
def image_params
params.require(:image).permit(:transaction_id, :upload => [:tempfile, :original_filename, :content_type, :headers])
end
I'm a bit frustrated that Rails 4 is not showing me what ForbiddenAttributesError is failing on in a development environment - it is supposed to be showing the error but it does not - would be a nice patch to ease development. Or perhaps everyone else is getting something that I am missing! Thanks much for the help.
I understand what happened here now - and will leave this up in the hope it helps someone else. I was porting code from a rails 3 project and missed the line that created the image:
#image = current_user.images.new(params[:image])
In rails 4 this is incorrect (I beleive). I updated to
#image = current_user.images.new(image_params)
and that solved my problem.
It looks like your first one should have worked. This is what I use for my projects.
class GalleriesController < ApplicationController
def new
#gallery = Gallery.new
end
def create
#user.galleries.new(gallery_params)
end
private
#note cover_image is the name of paperclips attachment filetype(s)
def gallery_params
params.require(:gallery).permit(:cover_image)
end
end

ruby on rails 3.1 global exception handler

I'm developing an app with Rails 3.1.2 but I can't find some documentation that works with errors / exception (like 404) on this version of rails.
i have tried things like:
In application controller
rescue_from ActiveRecord::RecordNotFound,ActionController::RoutingError,
ActionController::UnknownController, ActionController::UnknownAction, :NoMethodError, :with => :handle_exception
def handle_exception
render :template => 'error_pages/error'
end
environment/development.rb
config.consider_all_requests_local = false
Where can I find a solution?
Thanks in advance...
This should work:
In application controller
class NotFound < StandardError; end
rescue_from NotFound, :with => :handle_exception
def handle_exception
render :template => 'error_pages/error'
end
Look at action_dispatch/middleware/show_exceptions.
From the documentation in the source:
# This middleware rescues any exception returned by the application
# and wraps them in a format for the end user.
Short story short, it renders ActionDispatch::ShowExceptions.render_exception when the wrapped application (Rails, in your case), encounters an unrescued exception.
If you look through the default implementation, it ends up rendering something like public/500.html, which is what you see in the production environment. Overwrite the method or method chain it as you see fit to add your own implementation.

Rails 3 - custom ajax action is not working

I want to create a custom action in Rails which will update views and print some info on div.
I use that gem for file upload:
https://github.com/valums/file-uploader/blob/master/client/fileuploader.js
After successful upload I want to update with ajax page how many miliseconds it takes.
In old Rails I would write that with:
def set_tab
#diff = count_miliseconds_method
render :update do |page|
page.replace_html "place_menu", render( :partial => 'place_menu')
end
end
But I cant figure out how to do that in Rails 3.1.
My custom action controller code:
def custom
[...] # Here everything works OK
start_time = Time.now
Some_method
end_time = Time.now
#diff = ((end_time - start_time)*100).to_i # counted miliseconds
respond_to do |format|
format.json {render :json => {:success => true, :time => #diff}, :status => :created, :location => custom_words_path}
end
end
My custom.js.erb code
var el = $('#upload-log');
el.append("#{#diff} ms");
Unfortunately this doesnt work. I get response e.g.
{"success":true, "time":324}
but js.erb file doesnt get executed and page doesnt containt information about miliseconds.
Any idea how to fix that?
Update
Github repo:
https://github.com/there-is-no-spoon/Anagram
To execute js.erb file you have to pass
:format => :js
to your path generating method - for example:
link_to "My custom action", my_action_path(:format => :js)
You're returning JSON now (in Rails 3.1) not a chunk of string containing Javascript (as was the case before).
You need to write your code which handles your result where you make the Ajax Call. I assume you're using jQuery. So where you make the Ajax call, implement the success handler and do the
var el = $("#upload-log");
...
stuff there.
Basically server does not return Javascript anymore, it is completely on the client side only.
You need to implement the onComplete method of your file upload plugin. Read the manual of your js plugin, its mentioned clearly.

Calling Sinatra from within Sinatra

I have a Sinatra based REST service app and I would like to call one of the resources from within one of the routes, effectively composing one resource from another. E.g.
get '/someresource' do
otherresource = get '/otherresource'
# do something with otherresource, return a new resource
end
get '/otherresource' do
# etc.
end
A redirect will not work since I need to do some processing on the second resource and create a new one from it. Obviously I could a) use RestClient or some other client framework or b) structure my code so all of the logic for otherresource is in a method and just call that, however, it feels like it would be much cleaner if I could just re-use my resources from within Sinatra using their DSL.
Another option (I know this isn't answering your actual question) is to put your common code (even the template render) within a helper method, for example:
helpers do
def common_code( layout = true )
#title = 'common'
erb :common, :layout => layout
end
end
get '/foo' do
#subtitle = 'foo'
common_code
end
get '/bar' do
#subtitle = 'bar'
common_code
end
get '/baz' do
#subtitle = 'baz'
#common_snippet = common_code( false )
erb :large_page_with_common_snippet_injected
end
Sinatra's documentation covers this - essentially you use the underlying rack interface's call method:
http://www.sinatrarb.com/intro.html#Triggering%20Another%20Route
Triggering Another Route
Sometimes pass is not what you want, instead
you would like to get the result of calling another route. Simply use
call to achieve this:
get '/foo' do
status, headers, body = call env.merge("PATH_INFO" => '/bar')
[status, headers, body.map(&:upcase)]
end
get '/bar' do
"bar"
end
I was able to hack something up by making a quick and dirty rack request and calling the Sinatra (a rack app) application directly. It's not pretty, but it works. Note that it would probably be better to extract the code that generates this resource into a helper method instead of doing something like this. But it is possible, and there might be better, cleaner ways of doing it than this.
#!/usr/bin/env ruby
require 'rubygems'
require 'stringio'
require 'sinatra'
get '/someresource' do
resource = self.call(
'REQUEST_METHOD' => 'GET',
'PATH_INFO' => '/otherresource',
'rack.input' => StringIO.new
)[2].join('')
resource.upcase
end
get '/otherresource' do
"test"
end
If you want to know more about what's going on behind the scenes, I've written a few articles on the basics of Rack you can read. There is What is Rack? and Using Rack.
This may or may not apply in your case, but when I’ve needed to create routes like this, I usually try something along these lines:
%w(main other).each do |uri|
get "/#{uri}" do
#res = "hello"
#res.upcase! if uri == "other"
#res
end
end
Building on AboutRuby's answer, I needed to support fetching static files in lib/public as well as query paramters and cookies (for maintaining authenticated sessions.) I also chose to raise exceptions on non-200 responses (and handle them in the calling functions).
If you trace Sinatra's self.call method in sinatra/base.rb, it takes an env parameter and builds a Rack::Request with it, so you can dig in there to see what parameters are supported.
I don't recall all the conditions of the return statements (I think there were some Ruby 2 changes), so feel free to tune to your requirements.
Here's the function I'm using:
def get_route url
fn = File.join(File.dirname(__FILE__), 'public'+url)
return File.read(fn) if (File.exist?fn)
base_url, query = url.split('?')
begin
result = self.call('REQUEST_METHOD' => 'GET',
'PATH_INFO' => base_url,
'QUERY_STRING' => query,
'rack.input' => StringIO.new,
'HTTP_COOKIE' => #env['HTTP_COOKIE'] # Pass auth credentials
)
rescue Exception=>e
puts "Exception when fetching self route: #{url}"
raise e
end
raise "Error when fetching self route: #{url}" unless result[0]==200 # status
return File.read(result[2].path) if result[2].is_a? Rack::File
return result[2].join('') rescue result[2].to_json
end

Resources