I have a routing like that:
namespace :folio do
resources :portfolios do
resources :portfolio_items do
resources :images
end
end
end
Now please donĀ“t flame me because of the deep stacking. This is a mongo db persisted tree like object and those levels are all persisted in the root object.
What puzzles me is the fact that the generated routings read something like
folio_portfolio_portfolio_item
But when I ask for a url from urlhelper
url_for [#portfolio, #portfolio_item]
I get a nice exception telling me
undefined method `hash_for_folio_portfolio_folio_portfolio_item_path' for #<Module:0x0000000492fc30>
See the second "folio" in there? Any idea how I can get rid of that? Providing an :url => is not an option, unfortunately, because that would triplicate my form views and before that I'd rather ditch the namespace altogether. But unwillingly so: this is a rails engine and I would want to avoid clashes.
So, in other words...
I want
= form_for [#portfolio, #portfolio_item] do |form|
to "just" work :). Is this too much to ask?
Observation
a routing like that brings me a bit forward:
resources :folio_portfolio_items, :controller=>Folio::PortfolioItemsController do
while ugly as hell it generates good urls. Problem is, when I want to to visit one of them I get
ActionController::RoutingError (uninitialized constant Folio::Folio):
Whatever this means...
As a sidenote I think it is very odd that this happens at all. I think my mapping controllers to domains is the expected one...
I created a sample app that illustrates this on https://github.com/janlimpens/testroutes
Related
Moving over from django / python, I am having a little trouble getting the rails logger to log all the information I want. I am wondering how/if the following can be achieved:
Having in the log format(ter) include the specific file, function name and line where the logging statement itself was found. Essentially the equivalent of LOG_MSG_FORMAT = '%(asctime)s %(levelname)s %(filename)s:%(funcName)s(%(lineno)d) : %(message)s' in python logger?
Being able to log all requests, via something similar to a django request logging middleware. Particularly, being able to log the username (if logged in) of every request.
Am I missing something obvious? or does this require (lots of) custom code?
I just found this railtie gem that might help although I imagine it will take some "custom code" to append username to logs. See the Readme section on logging specific controllers and models.
logging-rails railtie
I don't know about getting the file, function, and line number, but it's pretty easy to log from application_controller:
class ApplicationController < ActionController::Base
before_filter :log_user
def log_user
if current_user
Rails.logger.info "Processing Request for #{current_user.name}"
end
end
end
Just to add a quick note in case this is useful for someone:
The lograge gem makes rails logs much similar to django's, plus allows very neat customization, adding parameters such as remote ip address, current_user etc.
It also reduces verbosity of rendered layouts, which I anyway found unnecessary for production. It also plays nicely with logging-rails railtie mentioned by #lukewendling.
Sadly, I still couldn't find anything that shows the file/function/line number like you can easily do with django, but I guess that's just too much to ask.
I'm using caching in a rails app, but there are times where I'd like to be able to turn off caching for a specific request or regenerate the cached data (in production). I'd want to do this for debugging but also just to see what the performance difference is between the two.
Is there a way to turn off caching (with something like &cache=false) across the board without adding things like this throughout my code:
<% cache(x) unless params[:cache] == "false" do %>
Is there a way to tell rails to invalidate all of its cached elements as it renders the page, regenerating them? This would work, but again, I'd have to do it everywhere:
<% Rails.cache.delete <key> if params[:clear_cache] == "true" %>
or is there a good reason why I should just never do this.
You could always write that up as a method that you use instead of "cache"
def my_cache(x)
return x if params[:cache].blank? || params[:cache] == true
cache(x)
end
It'd be a little less messy in your views...
Alternatively, you might (I'm guessing) be able to play with ActionController::Base 's cacheing internals... but it'd be meta-hacking on rails' base code.
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 have been using Mongoid for about 3 months now, and I have managed to get done pretty much anything I need thanks to the great document and resources out there.
But going back to improve some stuff I have made a few backs, I am definitely struggling a lot on embedded documents.
In a nutshell what I am trying to do, is to maintain versioning and timestamps on embedded documents, but that I cannot manage to do.
Here is the relevant part of my model:
class Content
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
embeds_many :localized_contents
accepts_nested_attributes_for :localized_contents
end
class LocalizedContent
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
include Mongoid::Versioning
embedded_in :content, :inverse_of => :localized_contents
end
Nothing really complicated here, everything works fine regarding the behavior of the Content model, however the LocalizedContent model is not behaving the way I am expecting to, so my expectations either needs to get straighten up, or I need help fixing what I am doing wrong.
To create a new embedded document I do the following:
my_content = Content.find(params[:id])
my_content.localized_contents.build(params[:localized_content])
if parent.save
#redirect, etc.
end
This works in the sense that it successfully creates a new embedded document in the correct Content, however the timestamps fields I left a nil
Now, if I try to update that localized_content:
my_content = Content.find(params[:content_id])
localized_content = my_content.localized_contents.find(params[:id])
Now, if I do: localized_content.update_attributes(params[:localized_content]) I get the following error:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
Fair enough, then I update atomically the fields on the localized content and save the parent:
localized_content.fieldA = "value"
localized_content.fieldB = "value"
localized_content.fieldC = "value"
my_content.save
This works in updating the localized content properly but:
- timesteamps (udpated_at and created_at) are still nil
- versions does not receive the a copy of the current localized_content and version does not get incremented !
So as I read in many occasion in this groups and on some forums on the web, the call backs are not triggered on the embedded document for performance reason, since I am calling save on the parent. Again, faire enough, but as suggested in those places, I should call save on the embedded docs instead... but how !?!?! because every time I do I get the dreaded:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
Even more so, I tried to manually call the call back for versioning on my embedded: localized_content.revise, and again same error:
=> Mongoid::Errors::InvalidCollection: Access to the collection for LocalizedContent is not allowed since it is an embedded document, please access a collection from the root document.
I am going nuts here ! Please help. What I am doing wrong ? How should an embedded document be create and updated so I can call (even manually I don't care) the proper callbacks to update the time stamps and versioning ?
Thanks,
Alex
ps: I am using rails 3.0.3 and mongoid 2.0.1
Just in case this answer is still useful to anyone, Mongoid has added a tag which makes callbacks run on embedded child objects when the parent object is saved.
Your parent object should now look like this:
class Content
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::Paranoia
embeds_many :localized_contents, cascade_callbacks: true
accepts_nested_attributes_for :localized_contents
end
That's it! Now, saving the parent object will run callbacks on the child objects (and Mongoid::Timestamps is smart enough to only run on the objects which were actually changed). This information is in the mongoid documentation, at the very bottom of the embedded documents page.
Try using create instead of build. EmbeddedDoc.build and EmbeddedDoc.new won't fire any save callbacks (because nothing's being saved yet), and saving the parent doc won't call the embedded children's callbacks (performance decision). EmbeddedDoc.create should fire the embedded docs callbacks though.
my_content = Content.find(params[:id])
puts my_content.localized_contents.create(params[:localized_content])
I need some help figuring out the best way to proceed with creating a Rails 3 engine(or plugin, and/or gem).
Apologies for the length of this question...here's part 1:
My company uses an email service provider to send all of our outbound customer emails. They have created a SOAP web service and I have incorporated it into a sample Rails 3 app. The goal of creating an app first was so that I could then take that code and turn it into a gem.
Here's some of the background: The SOAP service has 23 actions in all and, in creating my sample app, I grouped similar actions together. Some of these actions involve uploading/downloading mailing lists and HTML content via the SOAP WS and, as a result, there is a MySQL database with a few tables to store HTML content and lists as a sort of "staging area".
All in all, I have 5 models to contain the SOAP actions (they do not inherit from ActiveRecord::Base) and 3 models that interact with the MySQL database.
I also have a corresponding controller for each model and a view for each SOAP action that I used to help me test the actions as I implemented them.
So...I'm not sure where to go from here. My code needs a lot of DRY-ing up. For example, the WS requires that the user authentication info be sent in the envelope body of each request. So, that means each method in the model has the same auth info hard coded into it which is extremely repetitive; obviously I'd like for that to be cleaner. I also look back now through the code and see that the requests themselves are repetitive and could probably be consolidated.
All of that I think I can figure out on my own, but here is something that seems obvious but I can't figure out. How can I create methods that can be used in all of my models (thinking specifically of the user auth part of the equation).
Here's part 2:
My intention from the beginning has been to extract my code and package it into a gem incase any of my ESP's other clients could use it (plus I'll be using it in several different apps). However, I'd like for it to be very configurable. There should be a default minimal configuration (i.e. just models that wrap the SOAP actions) created just by adding the gem to a Gemfile. However, I'd also like for there to be some tools available (like generators or Rake tasks) to get a user started. What I have in mind is options to create migration files, models, controllers, or views (or the whole nine yards if they want).
So, here's where I'm stuck on knowing whether I should pursue the plugin or engine route. I read Jordan West's series on creating an engine and I really like the thought of that, but I'm not sure if that is the right route for me.
So if you've read this far and I haven't confused the hell out of you, I could use some guidance :)
Thanks
Let's answer your question in parts.
Part One
Ruby's flexibility means you can share code across all of your models extremely easily. Are they extending any sort of class? If they are, simply add the methods to the parent object like so:
class SOAPModel
def request(action, params)
# Request code goes in here
end
end
Then it's simply a case of calling request in your respective models. Alternatively, you could access this method statically with SOAPModel.request. It's really up to you. Otherwise, if (for some bizarre reason) you can't touch a parent object, you could define the methods dynamically:
[User, Post, Message, Comment, File].each do |model|
model.send :define_method, :request, proc { |action, params|
# Request code goes in here
}
end
It's Ruby, so there are tons of ways of doing it.
Part Two
Gems are more than flexible to handle your problem; both Rails and Rake are pretty smart and will look inside your gem (as long as it's in your environment file and Gemfile). Create a generators directory and a /name/name_generator.rb where name is the name of your generator. The just run rails g name and you're there. Same goes for Rake (tasks).
I hope that helps!