I'm trying to move my full-stack rails app over to Angular a page at a time. I'm using ui-router (https://github.com/angular-ui/ui-router) and angular-rails-templates (https://github.com/pitr/angular-rails-templates). I assumed the nghaml extension would allow me to continue to use rails helpers such as link_to, paths, etc. in my haml so just copied and pasted my haml page into the template; in an ideal world I would now be at a point where one page was being served client-side and every other page (including the ones it's linked to) were still being served server-side. Instead, I'm getting errors such as:
undefined local variable or method `dashboard_patients_path' for #<Object:0x007fc87865cff0>
and link_to, etc.
I thought this (angularjs with client side haml) would be a solid solution, specifically sharpper's response since it seemed directly applicable.
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Rails.application.assets.register_engine '.haml', CustomHamlEngine::HamlTemplate
However, even after restarting the server, no dice.
Thoughts?
Got stuck with the same problem and found a solution after investigating angular-rails-templates, attentively reading their documentation and using the solution proposed by sharpper in angularjs with client side haml.
angular-rails-templates needs to recreate a mimeless version of the haml engine. So they extend the classes that are registered with Tilt instead of using the engines that were added to the asset pipelines. Therefore the new CustomHamlEngine that we create and register with the asset pipeline is never used by angular-rails-template. What we need to do instead is to register the engine with Tilt.
Create a file called angular_rails_templates.rb in the config/initializers folder and put this code in it.
# config/initializers/angular_rails_templates.rb
module CustomHamlEngine
class HamlTemplate < Tilt::HamlTemplate
def evaluate(scope, locals, &block)
scope.class_eval do
include Rails.application.routes.url_helpers
include Rails.application.routes.mounted_helpers
include ActionView::Helpers
end
super
end
end
end
Tilt.register CustomHamlEngine::HamlTemplate, '.haml'
That will override the usual .haml engine with the one we just created. Angular-rails-templates will then process your haml file and it will support the rails helpers as well as the path helpers.
Don't forget to restart the server after including the file.
Related
I have a Rails app that uses a gem called ActsAsTaggableOnSteroids, which is a Rails Engine. Specifically, I'm using PavelNartov's fork of the gem. But nevermind that.
I need to add specific functionality to the Tag model, which is supplied by the engine.
But, according to my understanding of Rails engines and the magical loading functionality in Rails, if I put a file called "tag.rb" in my models directory, then it will completely replace the one from the Engine.
Ideally, I would be able to do something like:
class Tag < ActsAsTaggable::Tag
# my stuff
end
...but alas, that doesn't work because the model supplied by the engine is not namespaced.
So, I came up with this nightmare, which I put in app/models/tag.rb:
path = ActsAsTaggable::Engine.config.eager_load_paths.grep(/models/).first
require File.join(path, 'tag')
Tag.class_eval { include TagConcern }
But there has to be a better way! I feel like I'm missing something. I'd prefer not to add this strangeness to my app if possible.
Just require the file by looking up the path of the gem's model:
require File.join(Gem::Specification.find_by_name("bborn-acts_as_taggable_on_steroids").gem_dir, 'app/models/tag')
Tag.class_eval do
# ...
end
I'm trying to get custom scaffolding working from my engine.
I followed some tutorial on customizing Rails 3.2 scaffolding in a normal Rails App and put my customized templates in the engines /lib/templates/erb/scaffold directory but they don't get picked up by the app that includes the engine. Any suggestions?
Update:
I also tried to override the Rails ScaffoldGenerator's source_path and tried some other paths to put my template in, like:
lib/rails/generators/erb/scaffold/templates
zarazan's answer got me most of the way there, but there are a couple of things wrong with it. Here's what worked for me:
class Engine < Rails::Engine
config.generators do |g|
g.templates.unshift File::expand_path('../../templates', __FILE__)
end
end
Note that this goes in the generators section, not app_generators, and that the path is slightly different.
Also, I think the correct path to store your templates is lib/templates/erb/scaffold, optionally replacing erb with whatever language you are using (like haml or slim.) I know this works for slim. The file names are {_form,edit,index,new,show}.html.erb.
In the file that you declare your engine use this command:
class Engine < Rails::Engine
config.app_generators do |g|
g.templates.unshift File::expand_path('../templates', __FILE__)
end
end
It should shift the preference of what template folder Rails uses by default.
Now just put the template files in lib/templates/erb/scaffold/template_name.erb
Where template_name is one of the following: _form.html.erb, edit.html.erb, index.html.erb, new.html.erb, show.html.erb
Once you include the gem you should be able to use the rails generate scaffold command as normal.
Here is an example of an engine that overrides the default scaffolding in rails:
https://github.com/brocktoncg/gemboree
This is where the template directory is located:
https://github.com/brocktoncg/gemboree/tree/master/lib/templates/erb/scaffold
Are you talking about a controller template? Than you are using the wrong directory. Save your template at
lib/templates/rails/scaffold_controller/controller.rb
Have a look at http://xyzpub.com/en/ruby-on-rails/3.2/templates.html for an example.
I'm upgrading a Rails 3 app to 3.2 and setting up the asset pipeline. It's great for css/js but I don't really see the point in using it for images, and unfortunately I have css with a ton of references /images/*.png and the like.
Is there a way to disable the asset pipeline just for images so image_tag("x.png") will go back to returning <img src="/images/x.png"> instead of <img src="/assets/x.png">? Thanks!
You can monkey-patch ActionView::Base, try this in rails console:
helper.image_path "foo" #=> "/assets/foo"
module OldImagePath
def image_path(source)
asset_paths.compute_public_path(source, 'images')
end
alias_method :path_to_image, :image_path
end
ActionView::Base.send :include, OldImagePath
helper.image_path "foo" #=> "/images/foo"
You can place this in an initializer for example. By default ActionView::Base includes ActionView::Helpers::AssetTagHelper and Sprockets::Helpers::RailsHelper which both define image_path but the latter take precedence. I'm including my own module which take precedence over all of them (the code inside is taken from ActionView::Helpers::AssetTagHelper).
Although, it makes sense to use asset pipeline for images too. They get hash sum in their filenames so that they can be cached forever on the client side without asking the server whether the file was changed.
Have a look at this gem:
https://github.com/spohlenz/digestion, it should do what you need :).
Otherwise you can move the assets you don't want included in the asset pipeline from app/assets back into public (e.g. public/images). Everything should work as you expect without the need for a gem.
I have created a simple Rails Engine to provide some general functionality(photo gallery) to an application. I want to be able to override the standard _header partial so that the menu for the gallery matches that of my main application. In my header view I call a helper that is part of application_helpers (main app), but I keep getting "undefined method" errors. From what I can tell the main app application_helpers are not being included (obviously) when I override the engines application layout or its partials.
So my question is, how do I override an engine view in the main application, and get access to the main application helper methods? I would still need access to the engine helpers as well as not to screw up the engine functionality.
Do I need to override the controllers as well? seem like a lot just to get some helpers.
Thanks
Rails 3.1.3
check out this blog post: http://www.candland.net/2012/04/17/rails-routes-used-in-an-isolated-engine/
The author adds a method_missing definition to the application helper in order to access the parent application's helpers.
/config/initializers/blogit.rb
module Blogit
module ApplicationHelper
def method_missing method, *args, &block
puts "LOOKING FOR ROUTES #{method}"
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
if main_app.respond_to?(method)
main_app.send(method, *args)
else
super
end
else
super
end
end
def respond_to?(method)
if method.to_s.end_with?('_path') or method.to_s.end_with?('_url')
if main_app.respond_to?(method)
true
else
super
end
else
super
end
end
end
end
Try including the main app helper methods. For instance:
class MyEngineClass
include ApplicationHelper
#...
end
You may possibly need to require the file first, though I would expect Rails to correctly find it in this case.
Once ApplicationHelper is included, you should be able to directly use those helpers in the controller.
It also looks like you can call ClassName.helper("application") for a lot of Rails classes -- not sure if that will work here.
try creating a helper in your application with the same name of the helper in your engine in order to override engine helper methods.
I found this discussion particularly insightful. There are also some interesting ideas in the Rails Engine API docs under Isolated engine helpers.
Engines are supposed to be independent from the main app, that is why you can't access its helpers from the Engine.
However, there are hack-ish ways for giving your engine access to the helpers of the main app. This is how I did it:
# In the main app
# initializers/share_helpers_path_with_engine.rb
PhotoGallery::Engine.class_eval do
paths["app/helpers"] << File.join(File.dirname(__FILE__), '../..', 'app/helpers')
end
You need of course to change PhotoGallery to the actual name of your engine class.
Feel free to take a look at the Engines documentation (section about the paths): http://edgeapi.rubyonrails.org/classes/Rails/Engine.html
Disclaimer: I've only used this solution in Rails 3.2 engines.
If, in your engine you have a standard header partial vendor/gems/my_gallery_engine/app/views/application/_header.html.erb.
Then, override it in your main app by creating a customized partial app/views/application/_header.html.erb.
The override works because Rails' view template search path (by default) starts with the main apps' app/views directory, and then searches through engines' app/views in load order.
All of your main app's Helpers will be available in the partial.
I'm trying to write a plugin that will extend InheritedResources.
Specifically I want to rewrite some default helpers.
And I'd like it to "just work" once installed, w/o any changes to application code.
The functionality is provided in a module which needs to be included in a right place. The question is where? :)
The first attempt was to do it in my plugin's init.rb:
InheritedResources::Base.send :include, MyModule
It works in production, but fails miserably in development since InheritedResource::Base declared as unloadable and so its code is reloaded on each request. So my module is there for the first request,
and then its gone.
InheritedResource::Base is 'pulled' in again by any controller that uses it:
Class SomeController < InheritedResource::Base
But no code is 'pulling in' my extension module since it is not referenced anywhere except init.rb which is not re-loaded on each request
So right now I'm just including the module manually in every controller that needs it which sucks.
I can't even include it once in ApplicationController because InheritedResources inherites from it and so it will override any changes back.
update
I'm not looking for advice on how to 'monkey patch'. The extension is working in production just great. my problem is how to catch moment exactly after InheritedResources loaded to stick my extension into it :)
update2
another attempt at clarification:
the sequence of events is
a) rails loads plugins. my plugin loads after inherited_resources and patches it.
b) a development mode request is served and works
c) rails unloads all the 'unloadable' code which includes all application code and also
inherited_resources
d) another request comes in
e) rails loads controller, which inherites from inherited resources
f) rails loads inherited resources which inherit from application_controller
g) rails loads application_contrller (or may be its already loaded at this stage, not sure)
g) request fails as no-one loaded my plugin to patch inherited_resources. plugin init.rb files are not reloaded
I need to catch the point in time between g and h
The Rails::Configuration, config in the environment files, allows registering a callback on the dispatcher that runs before each request in development mode, or once when in production mode.
config.to_prepare do
# do something here
end
The problem is, I don't think your plugin has access to config when the init.rb file is run. Here is a way to register your callback directly in the dispatcher. Just put this in the init.rb file.
require 'dispatcher'
::Dispatcher.to_prepare do
puts "hi there from a plugin"
end
Warning: I don't know what side effects this may have. If possible, try to get access to config and register the callback tha right way.
What you are attempting to do is usually called "MonkeyPatch" - changing the way one module or class is working by "overriding" methods.
It is a common practice in Rails, but it doesn't mean it is the best way to do things - when possible, it is better to use common inheritance (it is more explicit about the changes you make).
Regarding your questions about "where to put the files": it is usually the lib/ directory. This can mean the lib of the rails app, or a lib directory inside a gem or plugin, if you are into that sort of thing.
For example, if the file you want to change is lib/generators/rails/templates/controller.rb of inherited resources, the first thing you have to do is replicate that directory structure inside your lib/ folder ('lib/generators/rails/templates/controller.rb')
Inside that new file of yours, (empty at the beginning) you can override methods. However, you must also the modules/classes hierarchy. So if the original gem had this:
module foo
module bar
def f1
...
end
def f2
...
end
end
def f3
...
end
end
And you wanted to modify f1, you would have to respect the foo-bar modules.
module foo
module bar
def f1
... # your code here
end
end
end
Now the last thing you need is to make sure this code is executed at the right time. If you are using the application's lib/ folder, you will need to create an entry on the initializers/ folder and require your new file. If you are developing a gem/plugin, you will have a init.rb file on the "root" folder of that plugin. Put the 'require' there.
I'm not very familiar with this unloadable stuff; maybe I'm asking something obvious but- have you tried making your extension module unloadable, too? (You shouldn't need this if you monkeypatched the module instead of creating a new one)