I'm using Spree Commerce for my Online Shop. I want to change some behaviour during the checkout process, that is defined in app/models/spree/order/checkout.rb inside the spree gem. So I made a checkout_decorator.rb at the same point in my application.
The problem is, that my changes aren't loaded. And another problem is, that everything inside the module is inside one method, the def self.included(klass) method. So I think I have to overwrite the whole file, instead of just one method. Here is what my decorator looks like:
checkout_decorator.rb
Spree::Order::Checkout.module_eval do
def self.included(klass)
klass.class_eval do
class_attribute :next_event_transitions
class_attribute :previous_states
class_attribute :checkout_flow
class_attribute :checkout_steps
def self.define_state_machine!
# here i want to make some changes
end
# and the other methods are also include here
# for readability, i don't show them here
end
end
end
The original file checkout.rb from the spree gem looks like this:
module Spree
class Order < ActiveRecord::Base
module Checkout
def self.included(klass)
klass.class_eval do
class_attribute :next_event_transitions
class_attribute :previous_states
class_attribute :checkout_flow
class_attribute :checkout_steps
def self.checkout_flow(&block)
if block_given?
#checkout_flow = block
define_state_machine!
else
#checkout_flow
end
end
def self.define_state_machine!
# some code
end
# and other methods that are not shown here
end
end
end
end
end
So my questions are: Why does this not work? Is module_eval the right way to do this? I tried class_eval but it doesn't work either. How can I solve this?
The module_eval method isn't going to work for you.
You should look at the Spree Checkout Flow Documentation for some good examples on how to customize the checkout flow. This is the recommended way for customizing the checkout flow as you won't need to copy/paste a whole bunch of code.
The namespacing isn't right.
Try Spree::Order::Checkout.class_eval do
tl;dr: Overwrite the method you want in the Spree::Order class instead of the Spree::Order::Checkout module.
You mentioned that in the original file (spree_core-3.2.0.rc3/app/models/spree/order/checkout.rb) there's a method wrapping the entire module.
def self.included(klass)
klass.class_eval do
This method is called when the module is included in a class, and does its own class_eval to add the module's methods to instances of the class including it.
So since (spree_core-3.2.0.rc3/app/models/spree/order.rb) has this line:
include Spree::Order::Checkout
We can add a decorator to the order class itself (app/models/spree/order_decorator.rb)
Related
What is the difference between adding instance methods whith active concern and through the normal ruby def keyword on the module?
module MonsterConcern
extend ActiveSupport::Concern
included do
def engage_rage
end
def chew_bones
end
end
end
and
module MonsterConcern
def engage_rage
end
def chew_bones
end
end
As far as I know there is no difference if the only thing you are interested in is instance methods.
The ActiveSupport::Concern advantage is the ability to define class methods, and to handle some nasty module interdependencies better (related to calling class methods in the included block).
You can read more here: http://api.rubyonrails.org/classes/ActiveSupport/Concern.html
In Rails, how do you use a specific method from a module. For eg,
# ./app/controllers/my_controller.rb
class MyController < ApplicationController
include MyModule
def action
MyModule.a_method
end
private
def a_method
...
end
end
# ------------------------------------------------ #
# ./app/helpers/my_module.rb
module MyModule
def a_method
...
end
end
MyController includes MyModule. And in action ,I want to use MyModule.a_method (Please note I also have a private a_method in MyController and I don't want to use this.)
Things I've tried :
1) Defining the method in the module as self.
def self.a_method
end
2) Using the :: notation in controller (MyModule::a_method)
The error that I keep getting is
Undefined method:a_method for MyModule:module
For now, I've resorted to using a different name for the modules method. But I'd like to know how to namespace the function with either the Module:: or Module. notation
[UPDATE - 11/24/2014]
adding file structure in code, since Rails heavily relies on convention.
So I am not really sure what you are trying to accomplish with your module but a quick solution to get it working is below.
Move my_module.rb out of helpers and into lib/my_module.rb. The helpers directory is for methods that you use in your views. The convention is to utilize helpers that are namespaced after their respective controller or the application_helper.rb for global methods for your views. Not sure if that's what you are trying to accomplish with your module but wanted to throw that out there.
Create an initializer (you can all it whatever) in config/initializers/custom_modules.rb and add require 'my_module'
Update the a_method back to be self.a_method
You can now call MyModule.a_method in your app
Don't forget to restart your server for changes to lib/my_module.rb to take effect.
Also, a lot of people reference this post by Yehuda Katz as guidance on where to store code for your app. Thought it might be a helpful reference.
if you include MyModule into MyController, all the "instance methods" of the first will be mixed-in into the 2nd.
So if you only want to call MyModule.a_method, no need to include your module.
Then you'd want to require (or better autoload) your module before using it. To do so place it in controllers/concerns/my_module.rb, rails (4 at least) should autoload it, otherwise require its file in an intializer
# my_module.rb
module MyModule
def self.a_method
...
end
end
should work, but doing
# my_module.rb
module MyModule
extend self
def a_method
...
end
end
is more clean to me. You'd like to have a look to rails active support concern to understand the "rails way" on this topic.
This extension create cache_find method for all models of app (I've create this using this post).
config/active_record_extension.rb
require 'active_support/concern'
module ActiveRecordExtension
extend ActiveSupport::Concern
# add your instance methods here
def flush_find
Rails.cache.delete([self.class.name, :cached_find, id])
end
included do
after_commit :flush_find
end
module ClassMethods
def cached_find id
Rails.cache.fetch([self.name, :cached_find, id]) { self.find(id) }
end
end
end
# include the extension
ActiveRecord::Base.send(:include, ActiveRecordExtension)
I turned this code into a gem and added to this repo.
So I want to add this methods dynamically, something like this:
class User << ActiveRecord::Base
# id, name, email, age...
cached :find, :find_by_name, :find_by_email
end
and the above code should generate cached_find, flush_find, cached_find_by_name, flush_find_by_name... You get it.
I need help to:
Test Rails.cache methods in model_caching gem.
Create code to dynamically add methods to app models based on cached method arguments.
Some links that helped me but do not meet all:
https://github.com/radar/guides/blob/master/extending-active-record.md
http://railscasts.com/episodes/245-new-gem-with-bundler
http://guides.rubyonrails.org/plugins.html
Fell free to clone and improve gem code.
You don't have to hack ActiveRecord::Base. You can add what Marc-Alexandre said right into your concern, like so:
module ActiveRecordExtension
extend ActiveSupport::Concern
...
module ClassMethods
def cached(*args)
define_method "cached_#{arg.to_s}" do
# do whatever you want to do inside cached_xx
end
define_method "flush_#{arg.to_s}" do
# do whatever you want to to inside flush_xx
end
end
end
end
Also, I would not auto include the extension directly in ActiveRecord, I think it's better to explicitly include it in the models you are going to use it.
To add code dynamically you need to hack the ActiveRecord::Base class. In another file (you usually put in lib/core_ext) you could do as follow :
ActiveRecord::Base.class_eval do
def self.cached(*args)
args.each do |arg|
define_method "cached_#{arg.to_s}" do
# do whatever you want to do inside cached_xx
end
define_method "flush_#{arg.to_s}" do
# do whatever you want to to inside flush_xx
end
end
end
end
What it does basically is takes all your arguments for cached (:find, :find_by_name, etc) and define the two methods (cache_find, cache_find_by_name) and flush_find, .. etc)
Hope this helps !
Ok so I am using some module/lib/plugin (not sure of the exact name), let's say its a authentication/authorization plugin like: http://github.com/technoweenie/restful-authentication
class HomeController < ApplicationController
some_module_name_here
end
Now just from adding the code above 'some_module_name_here', I can access to methods from that module.
What is that line of code doing that gives me access to methods/objects from the module?
Is that declaring a variable like in say java/c#:
public SomeModule _someModule;
I know that plugins/modules basically extend the class under the covers, but how does it do this with a single line of code?
Is it called in the constructor somehow?
When you create a ruby plugin, and load it into the rails app via environment.rb, bundler, or a require call, the methods are loaded as "modules" that can be called. The ones that act like you're talking about will have an extra method called acts_as_list or something similar. All that method does is include the methods of the module into the class where that line was called.
Here's an example, which you could include in your app's lib folder and play with:
module Bellmyer
module Pointless
def self.included(base)
base.extend PointlessMethods
end
module PointlessMethods
def acts_as_pointless
unless included_modules.include? InstanceMethods
extend ClassMethods
include InstanceMethods
end
end
end
module ClassMethods
def pointless_class?
true
end
end
module InstanceMethods
def pointless_instance?
true
end
end
end
end
The module is available to any ruby class in your app, but the methods don't actually get loaded until you call acts_as_pointless, which then includes and extends your class with the methods listed. Only the acts_as_pointless method is immediately available to the model. This is the standard pattern for an ActiveRecord plugin.
That's not how it works.
When the plugin or gem is loaded it adds a class method to, in this case, ApplicationController named some_module_name. When you call that methods, a bunch of other class and instance methods are included.
Check out your favourite gem or plugin to see how they do it exactly.
I am upgrading my Rails plugin to be an engine that works with the latest 3.0RC1 release and I'm having a bit of trouble figuring out the best (and most correct) way to extend ActionController. I've seen this post by DHH and this question here on SO, but my question is more about how to properly call code within the ActionController.
For instance, I need to call the following within my engine's controller:
class ApplicationController < ActionController::Base
helper :all
before_filter :require_one_user
after_filter :store_location
private
def require_one_user
# Code goes here
end
def store_location
# Code goes here
end
end
I know how to properly include my two private functions, but I can't find a way to get it to properly call helper, before_filter and after_filter.
I would greatly appreciate some links or a way to make this work. I have tried naming my controller something other than ApplicationController and having the real ApplicationController extend it, but that doesn't seem to work either. I'm really up for any solution that makes the life of the engine user as easy as possible. Ideally, they wouldn't have to extend my class, but they'd have all of the functionality built into their own ApplicationController.
You may also want to look into the initializers inside your engine subclass, so you don't have to include view helpers inside your controller class. And this will give you control over the load order of these modules.
Here is what I have been using:
module MyEngine
class Engine < Rails::Engine
initializer 'my_engine.helper' do |app|
ActionView::Base.send :include, MyEngineHelper
end
initializer 'my_engine.controller' do |app|
ActiveSupport.on_load(:action_controller) do
include MyEngineActionControllerExtension
end
end
end
end
Also, another option for the action controller extension is using a mixin module. This will let you use the before_filter, after_filter, etc..
module MyEngineActionControllerExtension
def self.included(base)
base.send(:include, InstanceMethods)
base.before_filter :my_method_1
base.after_filter :my_method_2
end
module InstanceMethods
#...........
end
end
One other thing... if you create the default rails directories at the top level of your gem, you don't have to worry about requiring the helpers or controllers. Your engine subclass has access to them. So I add my application controller and application helper extensions here:
/myengine/app/helpers/myengine_application_helper_extension.rb
/myengine/app/controllers/my_engine_action_controller_extension.rb
I like this setup because it looks similar to the application_controller and application_helper in your rails app. Again, this is just personal preference, but I try to keep anything that is directly rails related, such as controllers, helpers and models inside /my_engine/app and anything that is related to the plugin in general inside /my_engine/lib
Check out this tutorial by Jose Valim for more info on initializers:
https://gist.github.com/e139fa787aa882c0aa9c (engine_name is deprecated now, but most of this doc seems up-to-date)
So, I finally figured out the solution and I hope it helps someone else.
You need to create a file in your lib directory because you are actually going to extend the class. I did myplugin/lib/extensions/action_controller_base.rb.
Then, inside of your myplugin/lib/myplugin.rb file, do the following:
require 'extensions/action_controller_base.rb'
Inside of myplugin/lib/extensions/action_controller_base.rb put the following:
require 'action_controller' # Make sure ActionController::Base is defined
ActionController::Base.class_eval {
private
def my_method_1
# Code Goes Here
end
def my_method_2
# Code Goes Here
end
}
ActionController::Base.instance_eval {
helper_method :my_method_1, :my_method_2
before_filter :my_method_1
after_filter :my_method_2
}
If you need to have view helpers, create them in the myplugin/lib/helpers directory (or anything inside of lib, the name "helpers" doesn't matter) also and add the following to the bottom of myplugin/lib/extensions/action_controller_base.rb:
require 'helpers/helper_file_1'
require 'helpers/helper_file_2'
ActionView::Base.send :include, MyHelper1
ActionView::Base.send :include, MyHelper2