activesupport-decorators 1.0.3 → 2.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4cb9f32b392ce933e327b70be427aaa08237dc0d
4
- data.tar.gz: 7f91be81114a633358343e82ada2893f56490782
3
+ metadata.gz: ab36f5e952455f62d9a6ba3297a3b9ccf0a4a882
4
+ data.tar.gz: ec8df96f59cc48cefd330897491137a4b50fbbce
5
5
  SHA512:
6
- metadata.gz: 98a005bb62845d66948217da89ed5cb88ce8d2b17a827a7d231769045c7355ea4ba0acba741c427a938b685f91cf531137d77d9d8f2543953156d2fb5bd54efc
7
- data.tar.gz: d0d0b8847edfa8b2ed0d5b2601642860fd9d97bfc60115fd0280708ddc13ffbee4e65d15e322975adcc3456e28f4cb2e1319496ff3b85758d4002932ac2d682e
6
+ metadata.gz: ea6ccf8428d1eceb951baf6df41f69b9de6ae94410b108d5e475ef0b5a69b2d4be7e2edbb51a678c00f7c9cac5f08b41861a14e43239a03233396972e590b223
7
+ data.tar.gz: 64f9957ace96a5ac3186c06e66319d90b0e9190d564cc794c1279f38bb71c7bd72be0f45822643a8ac226f02ba1608a3acd886571c551f917c0e7abba23bd9cf
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  ActiveSupport Decorators
2
2
  ========================
3
3
 
4
+ [![Build Status](https://travis-ci.org/pierre-pretorius/activesupport-decorators.png?branch=master)]
5
+ (https://travis-ci.org/pierre-pretorius/activesupport-decorators)
6
+
4
7
  The decorator pattern is particularly useful when extending constants in rails engines or vice versa. To implement
5
8
  the decorator pattern, you need to load the decorator after the original file has been loaded. When you reference a
6
9
  class in a Rails application, ActiveSupport will only load the first file it finds that matches the class name. This
@@ -12,60 +15,53 @@ This is a tiny gem that provides you with a simple way to tell ActiveSupport to
12
15
  Add it to your Gemfile and run bundle install:
13
16
 
14
17
  ```Ruby
15
- gem 'activesupport-decorators', '~> 1.0'
18
+ gem 'activesupport-decorators', '~> 2.0'
16
19
  ```
17
20
 
18
- #### Example 1 - Engine extends application class.
21
+ ### Usage
22
+
23
+ #### Example 1 - Application extends engine (or any other) class.
19
24
 
20
- Your main rails application defines a model called Pet (in app/models/pet.rb):
25
+ Your Rails engine defines a model called Pet (in my_engine/app/models/pet.rb):
21
26
 
22
27
  ```Ruby
23
28
  class Pet < ActiveRecord::Base
24
29
  end
25
30
  ```
26
31
 
27
- Your rails engine adds the concept of pet owners to the application. You extend the Pet model in the engine with
28
- the following model decorator (in my_engine/app/models/pet_decorator.rb). Note that you could use 'class Pet' instead
29
- of 'Pet.class_eval do'.
32
+ Your Rails application now wants to adds the concept of pet owners. You extend the Pet model in the main application
33
+ with the following model decorator (in app/models/pet_decorator.rb). Note that you could use 'Pet.class_eval do'
34
+ instead of 'class Pet' if you want.
30
35
 
31
36
  ```Ruby
32
- Pet.class_eval do
37
+ class Pet
33
38
  belongs_to :owner
34
39
  end
35
40
  ```
36
41
 
37
- Now tell ActiveSupportDecorators to load any matching decorator file in my_engine/app when a file is loaded from
38
- app/. A convenient place to do this is in a Rails initializer in the engine:
42
+ Set your ActiveSupportDecorators paths similar to setting Rails autoload paths. This will load a decorator file if it
43
+ matches the original file's name/path and ends with '_decorator.rb'. In other words when the engine's app/pet.rb is
44
+ loaded, it will load the main applications app/pet_decorator.rb.
39
45
 
40
46
  ```Ruby
41
- module MyEngine
42
- module Rails
43
- class Engine < ::Rails::Engine
44
- initializer :set_decorator_dependencies do |app|
45
- ActiveSupportDecorators.add("#{app.root}/app", "#{config.root}/app")
46
- end
47
- end
48
- end
49
- end
47
+ ActiveSupportDecorators.paths << File.join(Rails.application.root, 'app/**')
50
48
  ```
51
49
 
52
- #### Example 2 - Application extends engine class.
50
+ Note that '**' is added to the end of the path because this is the pattern that Rails autoloads your app folder with.
51
+ It means that every folder inside app is regarded as a autoload path instead of app/ itself, so that you can call your
52
+ model 'Pet' and not 'Models::Pet'.
53
53
 
54
- Similar to the example above except the initializer is placed in the main application instead of the engine. Create a
55
- file called config/initializers/set_decorator_dependencies.rb (or any other name) with content:
56
-
57
- ```Ruby
58
- ActiveSupportDecorators.add("#{MyEngine::Rails::Engine.root}/app", "#{Rails.root}/app")
59
- ```
54
+ #### Example 2 - Engine extends application (or any other) class.
60
55
 
61
- #### Example 3 - Engine extends another engine class.
56
+ Similar to the example above except the initializer is placed in the engine instead of the main application. The example
57
+ below will cause any matching decorator file in my_engine/app to load when a file is loaded from app/.
62
58
 
63
59
  ```Ruby
64
60
  module MyEngine
65
61
  module Rails
66
62
  class Engine < ::Rails::Engine
67
- initializer :set_decorator_dependencies do |app|
68
- ActiveSupportDecorators.add("#{AnotherEngine::Rails::Engine.root}/app", "#{MyEngine::Rails::Engine.root}/app")
63
+ initializer :set_decorator_paths, :before => :load_environment_hook do |app|
64
+ ActiveSupportDecorators.paths << File.join(config.root, 'app/**')
69
65
  end
70
66
  end
71
67
  end
@@ -87,11 +83,12 @@ Other gems work by simply telling Rails to eager load all your decorators on app
87
83
  [here](https://github.com/parndt/decorators/blob/master/lib/decorators/railtie.rb). They expect your decorators to use
88
84
  'MyClass.class_eval do' to extend the original class as this is what triggers the original class to be loaded.
89
85
  Disadvantages of this approach include:
90
- * decorators may not expect other classes to be decorated already, it's a bad idea to depend on load order.
91
- * development mode is a bit slower since your decorator eager loading usually has a cascade effect on the application.
86
+ * if you decorate two classes and one uses decorated functionality of the other, you have to make sure that it is not
87
+ used during class loading since the other class might not be decorated yet.
88
+ * development mode is a bit slower since eager loading decorators usually has a cascade effect on the application.
92
89
  This is more noticeable when using JRuby as it will be a compile action instead of class load action.
93
90
  * using 'MyClass.class_eval do' instead of 'class MyClass' means you can not define constants.
94
91
 
95
92
  This gem works by hooking into ActiveSupport, which means that decorators are loaded as required instead of at
96
93
  application startup. You can use 'class MyClass' and expect that other classes are already decorated, since when you
97
- reference other classes they will be decorated on the fly when ActiveSupport loads them.
94
+ reference other classes they will be decorated on the fly when ActiveSupport loads them.
@@ -1,17 +1,18 @@
1
1
  module ActiveSupportDecorators
2
- DECORATOR_PATTERN = '_decorator'
2
+ def self.paths
3
+ @paths ||= []
4
+ end
3
5
 
4
- def self.clear
5
- dependencies.clear
6
+ def self.pattern
7
+ @pattern ||= '_decorator'
6
8
  end
7
9
 
8
- def self.dependencies
9
- @dependencies ||= {}
10
+ def self.pattern=(pattern)
11
+ @pattern = pattern
10
12
  end
11
13
 
12
- def self.add(path, decorator_path)
13
- dependencies[path] ||= []
14
- dependencies[path] << decorator_path
14
+ def self.expanded_paths
15
+ paths.map { |p| Dir[p] }.flatten
15
16
  end
16
17
 
17
18
  def self.debug
@@ -23,46 +24,33 @@ module ActiveSupportDecorators
23
24
  end
24
25
 
25
26
  private
26
- def self.load_path_order(file_name)
27
- # Do not process with .rb extension if provided.
28
- sanitized_file_name = file_name.sub(/\.rb$/, '')
29
- decorated_file = nil
30
- decorators = []
27
+ def self.all_autoload_paths
28
+ return [] unless defined?(Rails)
29
+ all_modules = [::Rails.application] + ::Rails::Engine.subclasses.map(&:instance)
30
+ all_modules.map { |mod| mod.send(:_all_autoload_paths) }.flatten
31
+ end
31
32
 
32
- dependencies.each do |path, decorator_paths|
33
- decorator_paths.each do |decorator_path|
34
- # If an attempt is made to load the original file, ensure the decorators are loaded afterwards.
35
- if sanitized_file_name.starts_with?(path)
36
- relative_name = sanitized_file_name.sub(path, '')
37
- candidate_file = "#{decorator_path}#{relative_name}#{DECORATOR_PATTERN}"
33
+ def self.relative_search_path(file_name, const_path = nil)
34
+ file = file_name
38
35
 
39
- if File.file?(candidate_file + '.rb')
40
- decorated_file = sanitized_file_name
41
- decorators << candidate_file
42
- end
43
- end
36
+ if const_path
37
+ file = const_path.underscore
38
+ else
39
+ sanitized_file_name = file_name.sub(/\.rb$/, '')
40
+ first_load_path_match = all_autoload_paths.find { |p| file_name.include?(p) }
41
+ file = sanitized_file_name.sub(first_load_path_match, '') if first_load_path_match
42
+ end
43
+ "#{file}#{pattern}.rb"
44
+ end
44
45
 
45
- # If an attempt is made to load a decorator file, ensure the original/decorated file is loaded first.
46
- # This is only supported when a decorator was not added with add_global.
47
- if sanitized_file_name.starts_with?(decorator_path)
48
- relative_name = sanitized_file_name.sub(decorator_path, '')
49
- candidate_file = "#{path}#{relative_name}".sub(/#{DECORATOR_PATTERN}$/, '')
46
+ def self.load_path_order(file_name, const_path = nil)
47
+ order = [file_name]
50
48
 
51
- if File.file?(candidate_file + '.rb')
52
- fail "File #{sanitized_file_name} is a decorator for #{candidate_file}, but this file is already
53
- decorating #{decorated_file}." unless decorated_file.nil? || decorated_file == candidate_file
54
- decorated_file = candidate_file
55
- decorators << sanitized_file_name
56
- end
57
- end
58
- end
49
+ expanded_paths.each do |path|
50
+ candidate_file = File.join(path, relative_search_path(file_name, const_path))
51
+ order << candidate_file if File.file?(candidate_file)
59
52
  end
60
53
 
61
- if decorated_file
62
- # Decorators are sorted to ensure the load order is always the same.
63
- [decorated_file] + decorators.sort
64
- else
65
- [sanitized_file_name]
66
- end
54
+ order
67
55
  end
68
56
  end
@@ -1,17 +1,19 @@
1
1
  require 'active_support/dependencies'
2
2
 
3
- module ActiveSupport::Dependencies
4
- alias_method :require_or_load_without_multiple, :require_or_load
3
+ module ActiveSupport
4
+ module Dependencies
5
+ alias_method :require_or_load_without_multiple, :require_or_load
5
6
 
6
- def require_or_load(file_name, const_path = nil)
7
- order = ActiveSupportDecorators.load_path_order(file_name)
7
+ def require_or_load(file_name, const_path = nil)
8
+ order = ActiveSupportDecorators.load_path_order(file_name, const_path)
8
9
 
9
- if ActiveSupportDecorators.debug && order.size > 1
10
- Rails.try(:logger).try(:debug, "ActiveSupportDecorators: Loading files in order #{order.join(', ')}.")
11
- end
10
+ if ActiveSupportDecorators.debug && order.size > 1
11
+ Rails.try(:logger).try(:debug, "ActiveSupportDecorators: Loading files in order #{order.join(', ')}.")
12
+ end
12
13
 
13
- order.each do |path|
14
- require_or_load_without_multiple(path, const_path)
14
+ order.each do |path|
15
+ require_or_load_without_multiple(path, const_path)
16
+ end
15
17
  end
16
18
  end
17
- end
19
+ end
@@ -1,3 +1,3 @@
1
1
  module ActiveSupportDecorators
2
- VERSION = '1.0.3'
2
+ VERSION = '2.0.1'
3
3
  end
@@ -3,55 +3,45 @@ require 'spec_helper'
3
3
  describe ActiveSupportDecorators do
4
4
 
5
5
  before :each do
6
- # Clear class from Ruby environment.
7
- ActiveSupport::Dependencies.clear
6
+ # Clear class from Ruby/Rails environment.
7
+ ActiveSupportDecorators.paths.clear
8
+ ActiveSupport::Dependencies.loaded.clear
8
9
  Object.send(:remove_const, :Pet) if defined?(Pet)
9
10
  end
10
11
 
11
- describe 'when configuring a decorator dependency' do
12
- before :all do
13
- original_path = File.join(File.dirname(__FILE__), 'support/originals')
14
- decorator_path = File.join(File.dirname(__FILE__), 'support/decorators')
15
- ActiveSupportDecorators.clear
16
- ActiveSupportDecorators.add(original_path, decorator_path)
17
- end
18
-
19
- context 'and requiring the original file as .rb' do
20
- it 'it loads the decorator file after loading the original' do
21
- path = File.join(File.dirname(__FILE__), 'support/originals/pet.rb')
22
- ActiveSupport.require_or_load(path)
23
-
24
- Pet.new.owner.should eq('Mr. Robinson')
12
+ describe 'when configuring a decorator path' do
13
+ before :each do
14
+ ActiveSupportDecorators.stub(:all_autoload_paths) do
15
+ [File.join(File.dirname(__FILE__), 'support', 'originals')]
25
16
  end
17
+
18
+ ActiveSupportDecorators.paths.clear
19
+ ActiveSupportDecorators.paths << File.join(File.dirname(__FILE__), 'support', 'decorators')
26
20
  end
27
21
 
28
- context 'and requiring the original file' do
29
- it 'it loads the decorator file after loading the original' do
30
- path = File.join(File.dirname(__FILE__), 'support/originals/pet')
31
- ActiveSupport.require_or_load(path)
22
+ it 'it loads the decorator file after loading the original without const_path' do
23
+ path = File.join(File.dirname(__FILE__), 'support', 'originals', 'pet')
24
+ ActiveSupport::Dependencies.require_or_load(path)
32
25
 
33
- Pet.new.owner.should eq('Mr. Robinson')
34
- end
26
+ Pet.new.owner.should eq('Mr. Robinson')
35
27
  end
36
28
 
37
- context 'and requiring the decorator file' do
38
- it 'it loads the original file first' do
39
- path = File.join(File.dirname(__FILE__), 'support/decorators/pet_decorator')
40
- ActiveSupport.require_or_load(path)
29
+ it 'it loads the decorator file after loading the original with const_path' do
30
+ path = File.join(File.dirname(__FILE__), 'support', 'originals', 'pet')
31
+ ActiveSupport::Dependencies.require_or_load(path, 'Pet')
41
32
 
42
- Pet.new.owner.should eq('Mr. Robinson')
43
- end
33
+ Pet.new.owner.should eq('Mr. Robinson')
44
34
  end
45
35
  end
46
36
 
47
37
  describe 'when not using activesupport-decorators' do
48
38
  before :all do
49
- ActiveSupportDecorators.clear
39
+ ActiveSupportDecorators.paths.clear
50
40
  end
51
41
 
52
42
  it 'should not automatically load decorators' do
53
- pet_path = File.join(File.dirname(__FILE__), 'support/originals/pet')
54
- ActiveSupport.require_or_load(pet_path)
43
+ path = File.join(File.dirname(__FILE__), 'support', 'originals', 'pet')
44
+ ActiveSupport::Dependencies.require_or_load(path)
55
45
 
56
46
  expect { Pet.new.owner }.to raise_error
57
47
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activesupport-decorators
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.3
4
+ version: 2.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pierre Pretorius
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-02 00:00:00.000000000 Z
11
+ date: 2014-07-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: railties
@@ -75,8 +75,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
75
75
  version: '0'
76
76
  requirements: []
77
77
  rubyforge_project:
78
- rubygems_version: 2.2.0
78
+ rubygems_version: 2.2.2
79
79
  signing_key:
80
80
  specification_version: 4
81
81
  summary: Adds the decorator pattern to activesupport class loading.
82
82
  test_files: []
83
+ has_rdoc: