I am using Rails 3.1.1 and added asset-pipelining to my app.
After a call to a method is done, example method1.
def method1
#locations = Location.name_like(params[:term].to_s) # returns a valid hash of values
respond_to do |format|
format.json { render :json => #locations.to_json }
end
end
another method(method2) is being called. As a result I am getting a nil class error.
I checked my logs and it does not show how or why method2 is getting being called. Also I checked if any javascript is calling method2 but there is no javascript for that.
I need help in debugging this situation.
Also I was wondering if asset-pipelining is causing this issue.
Thanks,
Vishal
The assets pipeline is mounted at /assets so I think this is unlikely.
It sounds as though the route for method1 is being captured and sent to method2.
This can happen if a broader route encompassing method1's route is higher up the file.
I would run the rake routes task, and also walk through the route file looking for broad routes the would match method1.
Related
I've put the following code into my config/environments/test.rb file:
config.action_mailer.default_url_options = { :host => "localhost:3000" }
however when I run my tests, all routes use http://test.host. I'm trying to work with an API that won't accept http://test.host as a valid callback URI, so I need to change this to properly receive the API response. Any idea why this isn't working? (I'm using RSpec, Guard, and Spork in my testing suite).
EDIT: Possibly relevant - this is being done inside of a controller spec.
EDIT2: It seems that it changes after a request is made via get, post, etc. Running the following code within the test:
Rails.logger.debug users_url
get 'http://google.com'
Rails.logger.debug users_url
would produce the following output:
http://localhost:3000/users
...get request related response here
http://google.com/users
Nowadays you can just set them in your test.rb like so:
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
Rails.application.routes.default_url_options[:host]= 'localhost:3000'
In the developemnt.rb / test.rb, can be more concise as following:
Rails.application.configure do
# ... other config ...
routes.default_url_options[:host] = 'localhost:3000'
end
From my experience, url_options will not be passed into tests without a bit of hacking.
See e.g.
How to set locale default_url_options for functional tests (Rails)
http://www.ruby-forum.com/topic/3448797
I've frequently encountered this problem when trying to set the locale in tests. I've never used action mailer, though, so there may be a simpler way to do it.
The solution I've found for setting the default locale in url_options is just to patch actiondispatch and force it to use whatever locale I want it to. You could adapt this to your case this way:
class ActionDispatch::Routing::RouteSet
def url_for_with_default_url_options(options)
url_for_without_default_url_options(options.merge(:host => "localhost:3000" ))
end
alias_method_chain :url_for, :default_url_options
end
I put that code in a file in spec/support so it is used in rspec tests and I also require it from my env.rb file so I can use it in cucumber tests as well.
Keep in mind that this will patch it everywhere, in both test code and in actual code running under tests, and it will override any other settings for the :host key that you try to pass in (since the patch merges the fix on top of the options passed into url_for). In your case I believe that shouldn't be a problem.
Monkey patching is not a very elegant solution, though, and I used this after everything else failed. You might find a simpler solution specific to action mailer.
How are you running your tests? Maybe appending RAILS_ENV=test might help.
I remember seeing this somewhere online but I know have trouble finding information about it. In Rails 3.1, I have a method at the beginning of a session controller, force_ssl, I only want it called in a production environment, how do I do that?
To clarify, the code looks something like this
class SessionsController < ApplicationController
force_ssl
end
I had a similar problem. force_ssl caused problems by testing with capybara and selenium. This here solved my problems and the tests are now running:
force_ssl if Rails.env.production?
I am using the line above in some Controllers. For example in the SessionController and UserController.
Rails.env.production? returns true if the current environment is 'production'. More generally, Rails.env.somestring? returns true if Rails.env == "somestring". From there you should be good.
** EDIT **
Well actually, there's an easier way to use ssl only in production. Check out this article
This is a question "why does it work this way", not "how do I make this work".
My app is calling a third party REST API that returns JSON, and returning the result as part of my own JSON API.
I was using the Rails 3 respond_to and respond_with methods; in the case of GET requests, this works as I expect, just passing through the JSON.
In the case of POST, it does more, including making a URL from the object returned to pass in a :location option. But since my object is just JSON (not ActiveRecord), I get an error.
For example...
# POST /api/products.json with params id=:id
def create
query_string = "#{user_id}&id=#{params[:id]}"
#products = third_party_api_wrapper.products(query_string, 'POST')
respond_with #products
end
My wrapper for the 3rd party API makes a POST request, which comes back fine, then Rails returns a 500 error which is logged like this:
NoMethodError (undefined method `{"response":{"message":"product 4e1712d9ec0f257c510013f8 selected"}}_url' for #<MyController>
Rails want's my #products object to know how to make a location URL.
CLARIFICATION: The #products object returned by the third party API is pure JSON -- a string, which you can see embedded in the error log message above. This error is occurring because Rails seems to want the object to be something more -- in the Rails internal API support, it is an ActiveRecord object.
If I replace the new respond_with with sytax with the old-style
respond_to do |format|
format.json { render :json => #products } # note, no :location or :status options
end
then everything works. And this is what I have done, so I don't have a "how" problem, instead have a "why" question.
Ryan Daigle's post on the introduction explains that what's happening is expected.
My question is: why does respond_with expect anything other than data (and the HTTP status?), and apparently just for POST.
I am not saying it's wrong, just trying to understand the rationale for the Rails implementation.
Summary: Rails gets its rationale from HTTP and REST.
(Thanks for your updated question. Now I understand your core question: "I am not saying it's wrong, just trying to understand the rationale for the Rails implementation.")
Now for the explanation. The rationale for how Rails behaves is rooted in embracing HTTP and REST conventions.
Just to bridge from what you've read to what I'm about to elaborate on, I want to mention the relevant parts from Ryan Daigle's article on Default RESTful Rendering:
If the :html format was requested:
[some text removed]
[after PUT or POST and no validation errors] redirect to the resource location (i.e. user_url)
(The text [in brackets] was added by me.)
If another format was requested, (i.e. :xml or :json)
[some text removed]
If it was a POST request, invoke the :to_format method on the resource and send that back with the :created status and the :location of the new created resource"
Let me put this in my words about what Rails believes is good practice:
For human content (e.g. HTML), after a POST or PUT, the server should tell the browser to redirect via a 303 to the newly created resource. This is common practice -- a very useful thing because a user wants to see the updates resulting from their edits.
For machine content (e.g. JSON, XML), after a PUT, the server should just render a 201. The client, in this case, a program consuming an API, might decide to stop there. (After all, the client specified the request and got a 201, so all is honky dory.) This is why 201 (success), not 303 (redirect), is used. If the client wants to request the newly created resource, it can look it up using the Location header -- but a redirect should not be forced.
In either case note that the location of the newly created resource is required. This is why #products in your example above needs to contain both the data and the location.
For background, I've shared a little from the W3C Page on 201 Created:
10.2.2 201 Created
The request has been fulfilled and resulted in a new resource being created. The newly created resource can be referenced by the URI(s) returned in the entity of the response, with the most specific URI for the resource given by a Location header field. The response SHOULD include an entity containing a list of resource characteristics and location(s) from which the user or user agent can choose the one most appropriate. The entity format is specified by the media type given in the Content-Type header field. The origin server MUST create the resource before returning the 201 status code. If the action cannot be carried out immediately, the server SHOULD respond with 202 (Accepted) response instead.
I hope this helps to explain the rationale. It is my (naive?) understanding that this rationale is well accepted across Web frameworks. Historically, I suspect that Rails was the fervent implementation-ground (new word alert!) for many fervent supporters of REST and the Resource Oriented Architecture.
The 'why' has been answered excellently by #david-james. This is just a short 'how' to answer via respond_with:
class Api::V1::UsersController < ApplicationController
respond_to :json
def create
#user = User.create(...)
respond_with #user, location: url_for([:api, :v1, #user])
end
end
To answer this question: "why should an API return anything other than data (and the HTTP status?). I am not saying it's wrong, just trying to understand the rationale."
I can think of no good rationale. More importantly, I can't see any way that an API could return anything except a data structure! (This question doesn't make sense to me!)
By definition, an API call must return a data structure. (It might be as simple as a string. It might be JSON. It might be XML.) It can use content negotiation to decide on the format. It may or may not be a strict schema, but at the very least a client library must be able to parse it. In any case, the API documentation should make this abundantly clear and stick to it. How else can client libraries expect to interoperate?
I think I'm missing the point here, this seems too obvious. (I suspect you are having another problem in your code above.)
So in Rails3 Engines come with their own models/controllers/views and of course routes. Now the question is: How do you ensure that Engine routes will be loaded before (or after) application routes and all other Engines that are present?
Here's an example of my Rails app routes:
match '*(path)', :to => 'foo_controller#bar_action'
And my Engine:
match '/news', :to => 'bar_controller#foo_action'
So by default Engines routes will be loaded after the application ones. This means that Engine routes are inaccessible due to that catch-all route in my app. How can force Engine routes to be loaded first (or last)?
What you're trying to do is kinda difficult. As has been mentioned engine routes are loaded after the app routes and overriding this behaviour may be problematic. I can think of several things that you can try.
Use An Initializer After Routing Paths Initializer
There is an initializer in engine.rb inside the rails source, one way to accomplish what you're after is to try to hook into the functionality that it deals with. The initializer looks like this by default:
initializer :add_routing_paths do |app|
paths.config.routes.to_a.each do |route|
app.routes_reloader.paths.unshift(route) if File.exists?(route)
end
end
Essentially, this should take the paths to all the routes files that Rails knows about and try and add them to the routes reloader (the thing that reloades your routes file automagically for you if it is changed). You can define another initializer to be executed right after this one, you will then inspect the paths stored in the routes reloader, pull out the path that belongs to your engine, remove it from the paths array and insert it back, but at the end of the paths array. So, in your config/application.rb:
class Application < Rails::Application
initializer :munge_routing_paths, :after => :add_routing_paths do |app|
engine_routes_path = app.routes_reloader.paths.select{|path| path =~ /<regex that matches path to my engine>/}.first
app.routes_reloader.paths.delete(engine_routes_path)
app.routes_reloader.paths << engine_routes_path
end
end
This may or may not work, either way I don't really recommend it, it's not particularly elegant (i.e. ugly hack playing with the guts of rails).
Use Rails 3.1
This may not be an option, but if it is, I'd probably go with this one. In Rails 3.1 you can have 2 different types of Engines, full and mountable (here is an SO question talking about some of the differences). But in essence you would alter your Engine to be a mountable engine, the routes in a mountable engine are namespaced and you can explicitly include them in the routes file of your main app e.g.:
Rails.application.routes.draw do
mount MyEngine::Engine => "/news"
end
You can also scope your mounted engine routes and do all sorts of other fancy routy things (more info here). Long story short, if you can go to 3.1 then this is the approach to use.
Dynamically Insert The Routes From Your Engine Into Your Main App
One of the most well known Rails engines around at the moment is Devise. Now, devise is an engine that will potentially add quite a number of routes to your app, but if you'll look at the devise source you'll see that it doesn't actually have a config/routes.rb file at all! This is because devise dynamically adds its routing goodness to your main app's routes.rb file.
When you run the model generator that comes with devise, one of the things the generator will do is add a line such as devise_for :model at the top of your routes.rb file, right after the Rails.application.routes.draw do line. So your route.rb looks similar to this after you execute a generator to create a User model:
Rails.application.routes.draw do
devise_for :users
...
end
Now, devise_for is a magical method that comes as part of devise (in lib/devise/rails/routes.rb), but in essence it will create a bunch of regular routes that we all know based on the model that you generated.
The thing we need to know, is how devise insert this line in the apps routes.rb file, then we can write a generator in our engine that will insert any of our routes at the top of the main apps routes.rb file. For this we look at lib/generators/devise/devise_generator.rb. In the add_devise_routes method the last line is route devise_route. Route is a Thor action which inserts the string passed to it into the main app's routes.rb file. So we can write a generator of our own and do something similar e.g.:
class MyCrazyGenerator < Rails::Generators::NamedBase
...
def add_my_crazy_routes
my_route = "match '/news', :to => 'bar_controller#foo_action'"
route my_route
end
end
Of course you would need to make sure all the generator infrastructure is in place but that's the essence of it. Devise is written by some very smart rails dudes and used by quite a lot of people, emulating what they do is likely a pretty good way to go. Out of the 3 things that I suggested this one would be the way I would handle your issue (given that moving to rails 3.1 is probably not an option).
Having the same issue right now. One other not particularly elegant but safe solution I came up with is adding another engine gem at the end of your gem file, just containing the catch all route, nothing else.
Edit: Actually, counterintuitively, the routing gem needs to be listed before all other engine gems for its routes to be loaded last.
You can write a method in your engine that can be called to configure your engine's routes:
module MyEngine
class Engine < Rails::Engine
def self.configure_routes(mapper)
mapper.match '/news', :to => 'news_controller#index'
end
end
end
This allows you to insert your engine's routes before a catch-all in my_application/config/routes.rb:
Rails.application.routes.draw do
# routes
MyEngine::Engine.configure_routes(self)
# more routes
# catch-all route
end
Then in the engine's my_engine/config/routes.rb file:
unless Rails.application.routes.named_routes.key?(:news)
Rails.application.routes.draw do
MyEngine::Engine.configure_routes self
end
end
If you're mounting the engine and don't include the explicit route configuration method, the engine routes will be added as usual to the end of the application routes.
I'm not an expert, but I was playing with sferik/rails_admin yesterday when I came across their routing solution. With their rails_admin:install rake task, they directly modify your config/routes.rb file by adding mount RailsAdmin::Engine => '/admin', :as => 'rails_admin' at the beginning of the file so they are sure their routes always have the highest priority. That mount method refers to their own routes.rb :
RailsAdmin::Engine.routes.draw do
# Prefix route urls with "admin" and route names with "rails_admin_"
scope "history", :as => "history" do
controller "history" do
[...]
end
end
end
Could this be your solution?
I am using the following gem:
https://github.com/kjvarga/sitemap_generator
I have a posts model and have used post_path(post) in other parts of the application as it is based on Enki.
However in the sitemap file:
Post.all.each do |post|
sitemap.add post_path(post), :lastmod => post.updated_at
end
This returns the error when running the rake task rake sitemap:refresh:
rake aborted!
undefined method `post_path' for #<SitemapGenerator::Interpreter:0x279efd0>
And:
Post.all.each do |post|
sitemap.add posts_path(post), :lastmod => post.updated_at
end
Returns no errors. Can anyone shed any light on this or do I need to provide more of the code?
sitemap_generator actually includes all the helper methods in the create block, so you should be able to access posts_path.
I had a similar problem, and my answer also made me feel stupid. I was storing sitemap.rb in config/initializers, when it should be stored in the config/ directory. That meant it was running on startup and failing because the url helpers weren't properly loaded (and, incidentally, when I called rake sitemap:refresh, the sitemap was being generated twice - once in the initializer and again as the rake task!)
I feel a bit stupid on this one, it was simply because post_path was a helper and not accessible, so I simply had to move it into the method directly.