Hot questions for Using Spree in metaprogramming

Top 10 Ruby Open Source / Spree / metaprogramming

Question:

I'd like to add the vendor? method to the Spree::User class.

Spree::User is defined in the solidus gem that my Rails app uses. I don't want to touch this code but rather open the class and append my methods to it in the main Rails app.

E.g: RAILS_ROOT/app/models/spree/user.rb

module Spree
  class User < Spree::Base

    include Spree::UserMethods

    def vendor?
      self.role_users.any? { |ru| ru.role.name == 'vendor' }
    end

  end    
end

This however does not include methods like admin? that were present before I created my patch.

Is there a way I can extend the existing class without manually copying all of it's methods manually?


Answer:

create a file under config/initializers directory:

# config/initializers/spree_user.rb

Spree::User.class_eval do
  def vendor?
    self.role_users.any? { |ru| ru.role.name == 'vendor' }
  end
end

It will just add your single method to the existing class.

Question:

I want to add a new 'vendor' role. A vendor can manage his own products and orders, but not see any orders or products belonging to an other vendor.

I was planning to implement this around the Stock Location. E.g: One user with the vendor role belongs to the Tokyo stock location. An other user with the vendor role belongs to an other stock location.

Which steps need to me taken to get this working?


Answer:

I believe in Solidus you would create a PermissionSet, and in your Spree/Solidus initializer you would assign that new role the permission set. You can avoid all the if/then/elses and easily store multiple roles with overlapping permission sets this way.

Question:

I have an existing decorator which looks like this:

app/controllers/products_controller_decorator.rb

  1 module Spree
  2   Admin::ProductsController.class_eval do
  3     def update_stock_location
            # my custom code

^ That works.

But when I'm trying to create a new decorator for the CheckoutsController, I get this error:

app/controllers/checkouts_controller_decorator.rb:2:in 
`<module:Spree>': uninitialized constant Spree::Admin::CheckoutsController (NameError)
        from /Users/martins/Work/SolidusApp/app/controllers/checkouts_controller_decorator.rb:1:in `<top (required)>'
        from /Users/martins/.rvm/gems/ruby-2.2.3@solidus/gems/polyglot-0.3.5/lib/polyglot.rb:65:in `require'
        from /Users/martins/.rvm/gems/ruby-2.2.3@solidus/gems/polyglot-0.3.5/lib/polyglot.rb:65:in `require'
        from /Users/martins/Work/SolidusApp/config/application.rb:24:in `block (2 levels) in <class:Application>'

This is what my Spree::Admin::CheckoutsController looks like: app/controllers/checkouts_controller_decorator.rb

  1 module Spree
  2   Admin::CheckoutsController.class_eval do
  3
  4     def complete
            # my custom code

I don't understand why it get an uninitialized constant Spree::Admin::CheckoutsController (NameError). The files looks identical to me.

This is the original class:

solidus/api/app/controllers/spree/api/checkouts_controller.rb

  1 module Spree
  2   module Api
  3     class CheckoutsController < Spree::Api::BaseController

Answer:

You want Api::CheckoutsController not Admin::CheckoutsController

module Spree
  Api::CheckoutsController.class_eval do
    # ...

Others who find this answer may be looking for Admin::CheckoutController (singular, no 's' after checkout)