Skip to content

Commit

Permalink
Add ClassConstantizer
Browse files Browse the repository at this point in the history
This class is responsible for holding as set of references to classes
(by their name). This is done instead of just having the classes to
avoid loading the classes too early as well as to allow code reloading
of these classes.
  • Loading branch information
John Hawthorn committed May 30, 2016
1 parent 257bc28 commit 0e2c9cd
Show file tree
Hide file tree
Showing 8 changed files with 125 additions and 17 deletions.
1 change: 1 addition & 0 deletions core/lib/spree/core.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class DestroyWithOrdersError < StandardError; end

require 'spree/core/version'

require 'spree/core/class_constantizer'
require 'spree/core/environment_extension'
require 'spree/core/environment/calculators'
require 'spree/core/environment'
Expand Down
31 changes: 31 additions & 0 deletions core/lib/spree/core/class_constantizer.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
module Spree
module Core
module ClassConstantizer
class Set
include Enumerable

def initialize
@collection = ::Set.new
end

def <<(klass)
@collection << klass.to_s
end

def concat(klasses)
klasses.each do |klass|
self << klass
end
end

delegate :clear, :empty?, to: :@collection

def each
@collection.each do |klass|
yield klass.constantize
end
end
end
end
end
end
1 change: 0 additions & 1 deletion core/lib/spree/core/engine.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,6 @@ class Engine < ::Rails::Engine
# We need to define promotions rules here so extensions and existing apps
# can add their custom classes on their initializer files
initializer 'spree.promo.environment', before: :load_config_initializers do |app|
app.config.spree.add_class('promotions')
app.config.spree.promotions = Spree::Promo::Environment.new
app.config.spree.promotions.rules = []
end
Expand Down
7 changes: 5 additions & 2 deletions core/lib/spree/core/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,15 @@ module Core
class Environment
include EnvironmentExtension

attr_accessor :calculators, :payment_methods, :preferences,
:stock_splitters
add_class_set :payment_methods
add_class_set :stock_splitters

attr_accessor :calculators, :preferences, :promotions

def initialize
@calculators = Calculators.new
@preferences = Spree::AppConfiguration.new
@promotions = Spree::Promo::Environment.new
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion core/lib/spree/core/environment/calculators.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ class Environment
class Calculators
include EnvironmentExtension

attr_accessor :shipping_methods, :tax_rates
add_class_set :shipping_methods
add_class_set :tax_rates
end
end
end
Expand Down
28 changes: 16 additions & 12 deletions core/lib/spree/core/environment_extension.rb
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
require 'spree/core/class_constantizer'

module Spree
module Core
module EnvironmentExtension
extend ActiveSupport::Concern

def add_class(name)
instance_variable_set "@#{name}", Set.new

create_method( "#{name}=".to_sym ) { |val|
instance_variable_set( "@" + name, val)
}
class_methods do
def add_class_set(name)
define_method(name) do
set = instance_variable_get("@#{name}")
send("#{name}=", []) unless set
set
end

This comment has been minimized.

Copy link
@flyfy1

flyfy1 Jun 3, 2016

Contributor

@jhawthorn It seems that if we do it this way, when the get method is called the first time, nil would be returned. (although [] would be defined to that property). That might cause the issue if the caller assume at least [] to be returned.


create_method(name.to_sym) do
instance_variable_get( "@" + name )
define_method("#{name}=") do |klasses|
set = ClassConstantizer::Set.new
set.concat(klasses)
instance_variable_set("@#{name}", set)
end
end
end

private

def create_method(name, &block)
self.class.send(:define_method, name, &block)
def add_class(name)
singleton_class.send(:add_class_set, name)
end
end
end
Expand Down
3 changes: 2 additions & 1 deletion core/lib/spree/promo/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ module Promo
class Environment
include Core::EnvironmentExtension

attr_accessor :rules, :actions
add_class_set :rules
add_class_set :actions
end
end
end
68 changes: 68 additions & 0 deletions core/spec/lib/spree/core/class_constantizer_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'spec_helper'

module ClassConstantizerTest
ClassA = Class.new
ClassB = Class.new

def self.reload
[:ClassA, :ClassB].each do |klass|
remove_const(klass)
const_set(klass, Class.new)
end
end
end

describe Spree::Core::ClassConstantizer::Set do
let(:set) { described_class.new }

describe "#concat" do
it "can add one item" do
set.concat(['ClassConstantizerTest::ClassA'])
expect(set).to include(ClassConstantizerTest::ClassA)
end

it "can add two items" do
set.concat(['ClassConstantizerTest::ClassA', ClassConstantizerTest::ClassB])
expect(set).to include(ClassConstantizerTest::ClassA)
expect(set).to include(ClassConstantizerTest::ClassB)
end
end

describe "<<" do
it "can add by string" do
set << "ClassConstantizerTest::ClassA"
expect(set).to include(ClassConstantizerTest::ClassA)
end

it "can add by class" do
set << ClassConstantizerTest::ClassA
expect(set).to include(ClassConstantizerTest::ClassA)
end

describe "class redefinition" do
shared_examples "working code reloading" do
it "works with a class" do
original = ClassConstantizerTest::ClassA

ClassConstantizerTest.reload

# Sanity check
expect(original).not_to eq(ClassConstantizerTest::ClassA)

expect(set).to include(ClassConstantizerTest::ClassA)
expect(set).to_not include(original)
end
end

context "with a class" do
before { set << ClassConstantizerTest::ClassA }
it_should_behave_like "working code reloading"
end

context "with a string" do
before { set << "ClassConstantizerTest::ClassA" }
it_should_behave_like "working code reloading"
end
end
end
end

0 comments on commit 0e2c9cd

Please sign in to comment.