How to include Draper Decorators in Rails Console? - ruby-on-rails

I'd like to see the output of some of my Draper Decorators in Rails console (https://github.com/drapergem/draper and http://railscasts.com/episodes/286-draper). To do so, I was looking for a way to include a decorator and its methods similar to as we would an application helper:
include ActionView::ApplicationHelper
A draper decorator inherits from an ApplicationDecorator, which inherits from Draper::Base
class ApplicationDecorator < Draper::Base
end
class MaterialDecorator < ApplicationDecorator
decorates :material
#methods to decorate your material..
end
I've tried include Draper::Base::MaterialDecorator, include ApplicationDecorator::MaterialDecorator, and some other similar variations with no luck.
How can I include a decorator such as the Material Decorator above in rails console?

So, it turns out (unless someone can show otherwise) that this isn't the way you test draper decorator output in rails console..
Instead, you just do:
material = Material.first
material = MaterialDecorator.decorate(material)
material.some_decorator_method

Add app/decorators to the Rails autoload path:
in config/application.rb:
config.autoload_paths << Rails.root.join('app/decorators')
then, the decorator classes under that path should automatically be loaded, even in the rails console. Example:
Material.first.decorate.some_decorator_method # => "good stuff"

Related

How to add optional Rails view helpers to my gem?

I'm working on a gem that does some general string manipulations I'd like to expose as helper methods to rails 4+ apps.
Not all consumers of my gem are rails apps so i'd like to safely expose helper methods to rails apps.
Two questions:
How do I add view helper methods to Rails from a gem and where should it live within the gem directory structure?
What can i do to prevent a blow up when the consumer is NOT a rails app? i.e. the gem can't find rails when it's included
Thanks
In your lib/my_gem.rb, you typically want to do something along these lines:
require 'my_gem/action_methods' if defined? ActionView
And lib/my_gem/action_view_methods.rb would contain all if your methods that require Rails/ActionView.
You can add these helpers to Rails with:
module ActionMethods
# ...
end
ActionView::Base.send :include, ActionMethods
Also see this question, and this one.
The rails way is by creating an engine and as it gets loaded with your gem it gets processed by rails
module MyGem
class Engine < ::Rails::Engine
isolate_namespace MyGem
initializer "my_gem.include_view_helpers" do |app|
ActiveSupport.on_load :action_view do
include MyViewHelper
end
end
end
end
Another way you can go is to not include the helper by default so that consumers don't get unexpected side-effects from your gem. Create the helper as a module and document that it should be added to ApplicationController or any needed controller.

How can I decorate STI models with modularized decorators using Draper?

I'm using Draper for general view-layer decorators.
I have some console-related, human-readability functionality I'd like to pull into new decorators.
My first thought was to put them in a module, e.g., Human::XxxDecorator, keeping them isolated from my normal view-layer decorators: they're just for rails c debugging/hacking/testing work.
This works fine at the top level; I can explicitly decorate a model with the namespaced decorator.
Now I need to decorate a collection of STI vehicles. I'm not sure what the best way to create vehicle-type-specific decorators in the same module of decorators, e.g., I have:
Human::CarDecorator
Human::TruckDecorator
Human::MotorcycleDecorator
I'm not sure how to get from, e.g.,
pry ยป dfleet = Human::FleetDecorator.new(Fleet.find(1))
to its embedded collections of vehicles, each with an appropriate decorator from the Human module. The naive approach using decorates doesn't work; I get:
Draper::UninferrableDecoratorError: Could not infer a decorator for Vehicle
The combination of:
Decorators from a specific module, and
Appropriate decorators for the STI models
is throwing things off.
Before digging into the Draper decorator inference code (I'm only assuming that's the best place to start), is this a problem that's already been solved and I'm missing something?
As I wrote in comments, remove the builtin decoration of your vehicles, and code yours:
def vehicles
object.vehicles.map do |v|
# logic to instantiate proper decorator
end
end
Hack incoming:
module Human
class FleetDecorator < Draper::Decorator
decorates_association :vehicles, with: ::Human::VehicleDecoratorDispatcher
end
class VehicleDecoratorDispatcher < Draper::Decorator
def initialize(*args)
super
#object = ... # here you build the proper decorator based on the rules on #object
end
end
end
But I doubt this is clearer...
You could use constantize:
def dfleet
dfleet_decorator_class.new(object.dfleet)
end
def dfleet_decorator_class
"#{object.dfleet.class}Decorator".constantize
end
Use decorates: . Here is an example: CLICK

Rails: how to extend a generator?

I'm trying to extend the model generator in Rails ( rails g model ). Basically my generator should do the same thing as the model one, but copy 2 additional files. Simple as that.
I reviewed Railscast #218 ( http://railscasts.com/episodes/218-making-generators-in-rails-3 ) which was very informative but i couldn't find any info about extending generators.
Checking the source code of rails, it looks like the model generator is in lib/rails/generators/rails/model/model_generator.rb defined as Rails::Generators::ModelGenerator.
I tried to make my generator extend this class but it results in:
Error: uninitialized constant Rails::Generators::ModelGenerator.
And my attempts to require this file were not successful.
So I decided to stop and ask here. What is the proper way of extending a generator?
Take a look at hooks and invoke.
class MyGenerator < Rails::Generators::Base
def create_my_file
# Do your generators stuff
create_file "config/initializers/my.rb", "# Add content here"
# Create model
invoke("model", ["model_name", "arg1", "arg2"])
end
end
Hope this help.
Generate your custom generator:
rails generate generator my_model
Open lib/generators/my_model/my_model_generator.rb and change it to:
require 'rails/generators/active_record/model/model_generator'
class MyModelGenerator < ActiveRecord::Generators::ModelGenerator
source_root File.expand_path('../templates', __FILE__)
end
This works for rails engines. Don't forget to add required templates.

How to refactor "shared" methods?

I am using Ruby on Rails 3.2.2 and I would like to "extract" some methods from my models / classes. That is, in more than one class / model I have some methods (note: methods are related to user authorizations and are named the "CRUD way") that are and work practically the same; so I thought that a DRY approach is to put those methods in a "shared" module or something like that.
What is a common and right way to accomplish that? For example, where (in which directories and files) should I put the "shared" code? how can I include mentioned methods in my classes / models? what do you advice about?
Note: I am looking for a "Ruby on Rails Way to make things".
One popular approach is to use ActiveSupport concerns. You would then place the common logic typically under app/concerns/ or app/models/concerns/ directory (based on your preference). An illustrative example:
# app/concerns/mooable.rb
module Mooable
extend ActiveSupport::Concern
included do
before_create :say_moo
self.mooables
where(can_moo: true)
end
end
private
def say_moo
puts "Moo!"
end
end
And in the model:
# app/models/cow.rb
class Cow < ActiveRecord::Base
include Mooable
end
In order to make it work this way you have to add the following line to config/application.rb
config.autoload_paths += %W(#{config.root}/app/concerns)
More information:
http://chris-schmitz.com/extending-activemodel-via-activesupportconcern/
http://blog.waxman.me/extending-your-models-in-rails-3
http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
My answer has nothing to do with RoR directly but more with Ruby.
Shraing common code may be done in various ways in Ruby. In my opinion the most obvious way is to create Ruby Modules that contain the code and then include them inside your class/model. Those shared modules are frequently under the lib directory of your app root. For example:
# lib/authorizable.rb
module Authorizable
def method1
#some logic here
end
def method2
#some more logic here
end
end
# app/models/user.rb
class User < ActiveRecord::Base
include Authorizable
end
The User class may now invoke method1 and method2 which belong to the Authorizable module. You can include this module in any other Ruby class you'd like, this way you DRY your code.

Rails 3.1: Better way to expose an engine's helper within the client app

I have found a few articles addressing the issue of helpers within an engine not being accessible to the consuming (parent) application. To make sure we are all on the same page, let's say we have this:
module MyEngine
module ImportantHelper
def some_important_helper
...do something important...
end
end
end
If you look at the rails engine documentation in the "Isolated engine's helpers" (L293), it says:
# Sometimes you may want to isolate engine, but use helpers that are defined for it.
# If you want to share just a few specific helpers you can add them to application's
# helpers in ApplicationController:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::SharedEngineHelper
# end
#
# If you want to include all of the engine's helpers, you can use #helpers method on an engine's
# instance:
#
# class ApplicationController < ActionController::Base
# helper MyEngine::Engine.helpers
# end
So if I ask anybody consuming my engine to add this to their application_controller.rb, then they will get access to all my important helper methods:
class ApplicationController < ActionController::Base
helper MyEngine::ImportantHelper
end
This is what I want and it works, but that's kind of a pain, especially if, as is my use case, everything the engine exposes can/should be used anywhere in the consuming app. So I dug around a bit more and found a solution that suggested I do the following:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
ApplicationController.helper(ImportantHelper)
end
end
end
Now this is exactly what I want: to add all my ImportantHelper method to the parent app's application helper. However, it doesn't work. Can anybody help me figure out why this more-better solution does not work?
I am running ruby 1.8.7 with rails 3.1.3. Let me know if I missed any important info germane to the issue, and thanks in advance.
You can create an initializer to accomplish this like so:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.action_controller' do |app|
ActiveSupport.on_load :action_controller do
helper MyEngine::ImportantHelper
end
end
end
end
I have written two blog posts about creating engines from scratch, and following them everything should work as expected (without additional configurations needed). Maybe you are still interested in:
Rails 3.1 Engines: Part I โ€“ The engine
Rails 3.1 Engines: Part II โ€“ The gem
Rails 3.1 Engines: Part III โ€“ The environment
Update: It's three articles in the meantime, and there's still some info to come. Please give me feedback.
If you want to keep the code in the engine, instead of every implementing application, use this:
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
config.to_prepare do
MyEngine::ApplicationController.helper Rails.application.helpers
end
end
end
module YourEngine
module Helpers
def a_helper
end
...
end
end
ActionController::Base.send(:helper, YourEngine::Helpers)
Include this code in engine.rb is also be very helpful
config.before_initialize do
ActiveSupport.on_load :action_controller do
helper MyEngine::Engine.helpers
end
end
Basically your engine would look like
module MyEngine
class Engine < Rails::Engine
isolate_namespace MyEngine
# Here comes the code quoted above
end
end

Resources