activesupport-decorators 1.0.1 → 1.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +25 -39
- data/lib/active_support_decorators/active_support_decorators.rb +35 -26
- data/lib/active_support_decorators/version.rb +1 -1
- data/spec/lib/active_support_decorators/active_support_decorator_spec.rb +60 -0
- data/spec/lib/active_support_decorators/support/decorators/pet_decorator.rb +5 -0
- data/spec/lib/active_support_decorators/support/originals/pet.rb +5 -0
- data/spec/spec_helper.rb +8 -0
- metadata +6 -3
- data/lib/active_support_decorators/graph.rb +0 -49
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 68468583a43ef2fe9365a6d4f47f6e96a5cb2520
|
|
4
|
+
data.tar.gz: fa6523c3c0aafa1a4c5d669e415037a5fdd89bef
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fc7f2882249afc13d84c940f50e2d3fb7d721d5d489b217cedf7a5d85b3c091597838e7d785c667995e94e11fff3f5a9c64872352dbef8ae6130bb0915dac20a
|
|
7
|
+
data.tar.gz: f1705d7f814348c16b4bd62506fc1f304351d6c2ab87f3a88dfe2cc4c1e8d374ff12917a43e85f690b76dbbe7080b621133f8736b49a850702374b03d5502c75
|
data/README.md
CHANGED
|
@@ -4,11 +4,10 @@ ActiveSupport Decorators
|
|
|
4
4
|
The decorator pattern is particularly useful when extending constants in rails engines or vice versa. To implement
|
|
5
5
|
the decorator pattern, you need to load the decorator after the original file has been loaded. When you reference a
|
|
6
6
|
class in a Rails application, ActiveSupport will only load the first file it finds that matches the class name. This
|
|
7
|
-
means that you
|
|
8
|
-
|
|
9
|
-
This is a tiny gem that provides you with a simple way to specify load file dependencies.
|
|
7
|
+
means that you need to manually load the additional (decorator) file or eager load all of them on application startup.
|
|
8
|
+
This is a tiny gem that provides you with a simple way to tell ActiveSupport to load your decorator files when needed.
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
### Installation
|
|
12
11
|
|
|
13
12
|
Add it to your Gemfile and run bundle install:
|
|
14
13
|
|
|
@@ -16,7 +15,7 @@ Add it to your Gemfile and run bundle install:
|
|
|
16
15
|
gem 'activesupport-decorators', '~> 1.0'
|
|
17
16
|
```
|
|
18
17
|
|
|
19
|
-
|
|
18
|
+
#### Example 1 - Engine extends application class.
|
|
20
19
|
|
|
21
20
|
Your main rails application defines a model called Pet (in app/models/pet.rb):
|
|
22
21
|
|
|
@@ -26,7 +25,8 @@ end
|
|
|
26
25
|
```
|
|
27
26
|
|
|
28
27
|
Your rails engine adds the concept of pet owners to the application. You extend the Pet model in the engine with
|
|
29
|
-
the following model decorator (in my_engine/app/models/pet_decorator.rb).
|
|
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'.
|
|
30
30
|
|
|
31
31
|
```Ruby
|
|
32
32
|
Pet.class_eval do
|
|
@@ -49,7 +49,7 @@ module MyEngine
|
|
|
49
49
|
end
|
|
50
50
|
```
|
|
51
51
|
|
|
52
|
-
|
|
52
|
+
#### Example 2 - Application extends engine class.
|
|
53
53
|
|
|
54
54
|
Similar to the example above except the initializer is placed in the main application instead of the engine. Create a
|
|
55
55
|
file called config/initializers/set_decorator_dependencies.rb (or any other name) with content:
|
|
@@ -58,21 +58,21 @@ file called config/initializers/set_decorator_dependencies.rb (or any other name
|
|
|
58
58
|
ActiveSupportDecorators.add("#{MyEngine::Rails::Engine.root}/app", "#{Rails.root}/app")
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
-
|
|
61
|
+
#### Example 3 - Engine extends another engine class.
|
|
62
62
|
|
|
63
63
|
```Ruby
|
|
64
64
|
module MyEngine
|
|
65
65
|
module Rails
|
|
66
66
|
class Engine < ::Rails::Engine
|
|
67
67
|
initializer :set_decorator_dependencies do |app|
|
|
68
|
-
ActiveSupportDecorators.add("#{AnotherEngine::Rails::Engine.root}/app", "#{Rails.root}/app")
|
|
68
|
+
ActiveSupportDecorators.add("#{AnotherEngine::Rails::Engine.root}/app", "#{MyEngine::Rails::Engine.root}/app")
|
|
69
69
|
end
|
|
70
70
|
end
|
|
71
71
|
end
|
|
72
72
|
end
|
|
73
73
|
```
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
### Debugging
|
|
76
76
|
|
|
77
77
|
Need to know which decorator files are loaded? Enable debug output:
|
|
78
78
|
|
|
@@ -80,32 +80,18 @@ Need to know which decorator files are loaded? Enable debug output:
|
|
|
80
80
|
ActiveSupportDecorators.debug = true
|
|
81
81
|
```
|
|
82
82
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
This is yet another decorator gem because it:
|
|
99
|
-
* allows you to specify where and how you name decorators.
|
|
100
|
-
* allows you to limit the paths you decorate.
|
|
101
|
-
* other gems tend to eager load as seen [here]
|
|
102
|
-
(https://github.com/atd/rails_engine_decorators/blob/master/lib/rails_engine_decorators/engine/configuration.rb)
|
|
103
|
-
and [here](https://github.com/parndt/decorators/blob/master/lib/decorators/railtie.rb).
|
|
104
|
-
* other gems assume you use 'MyClass.class_eval' to define decorators since it is how you trigger activesupport to load
|
|
105
|
-
the original file. This gem allows you to use the normal 'class MyClass' which means you can define constants in
|
|
106
|
-
decorators.
|
|
107
|
-
|
|
108
|
-
However if you do not want to specify which path's you allow to be decorated, you should use another gem. Instead of
|
|
109
|
-
single file look ups it will need to search your decorator directories for a matching file, which is not feasible
|
|
110
|
-
especially in a context where any ruby file may be decorated. The best solution for such a scenario would be to
|
|
111
|
-
eager load the decorators like other gems do.
|
|
83
|
+
### Comparison to other gems
|
|
84
|
+
|
|
85
|
+
Other gems work by simply telling Rails to eager load all your decorators on application startup as seen [here]
|
|
86
|
+
(https://github.com/atd/rails_engine_decorators/blob/master/lib/rails_engine_decorators/engine/configuration.rb) and
|
|
87
|
+
[here](https://github.com/parndt/decorators/blob/master/lib/decorators/railtie.rb). They expect your decorators to use
|
|
88
|
+
'MyClass.class_eval do' to extend the original class as this is what triggers the original class to be loaded.
|
|
89
|
+
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.
|
|
92
|
+
This is more noticeable when using JRuby as it will be a compile action instead of class load action.
|
|
93
|
+
* using 'MyClass.class_eval do' instead of 'class MyClass' means you can not define constants.
|
|
94
|
+
|
|
95
|
+
This gem works by hooking into ActiveSupport, which means that decorators are loaded as required instead of at
|
|
96
|
+
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.
|
|
@@ -1,18 +1,17 @@
|
|
|
1
|
-
require 'active_support_decorators/graph'
|
|
2
|
-
|
|
3
1
|
module ActiveSupportDecorators
|
|
4
|
-
|
|
2
|
+
DECORATOR_PATTERN = '_decorator'
|
|
5
3
|
|
|
6
4
|
def self.clear
|
|
7
5
|
dependencies.clear
|
|
8
6
|
end
|
|
9
7
|
|
|
10
8
|
def self.dependencies
|
|
11
|
-
@dependencies ||=
|
|
9
|
+
@dependencies ||= {}
|
|
12
10
|
end
|
|
13
11
|
|
|
14
|
-
def self.add(path, decorator_path
|
|
15
|
-
dependencies
|
|
12
|
+
def self.add(path, decorator_path)
|
|
13
|
+
dependencies[path] ||= []
|
|
14
|
+
dependencies[path] << decorator_path
|
|
16
15
|
end
|
|
17
16
|
|
|
18
17
|
def self.debug
|
|
@@ -26,34 +25,44 @@ module ActiveSupportDecorators
|
|
|
26
25
|
private
|
|
27
26
|
def self.load_path_order(file_name)
|
|
28
27
|
# Do not process with .rb extension if provided.
|
|
29
|
-
sanitized_file_name = file_name.sub(/\.rb$/,'')
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
graph.add(sanitized_file_name)
|
|
28
|
+
sanitized_file_name = file_name.sub(/\.rb$/, '')
|
|
29
|
+
decorated_file = nil
|
|
30
|
+
decorators = []
|
|
33
31
|
|
|
34
|
-
dependencies.each do |
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
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}"
|
|
39
38
|
|
|
40
|
-
|
|
41
|
-
|
|
39
|
+
if File.file?(candidate_file + '.rb')
|
|
40
|
+
decorated_file = sanitized_file_name
|
|
41
|
+
decorators << candidate_file
|
|
42
|
+
end
|
|
42
43
|
end
|
|
43
|
-
end
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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}$/, '')
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
53
57
|
end
|
|
54
58
|
end
|
|
55
59
|
end
|
|
56
60
|
|
|
57
|
-
|
|
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
|
|
58
67
|
end
|
|
59
68
|
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe ActiveSupportDecorators do
|
|
4
|
+
|
|
5
|
+
before :each do
|
|
6
|
+
# Clear class from Ruby environment.
|
|
7
|
+
ActiveSupport::Dependencies.clear
|
|
8
|
+
Object.send(:remove_const, :Pet) if defined?(Pet)
|
|
9
|
+
end
|
|
10
|
+
|
|
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')
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
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)
|
|
32
|
+
|
|
33
|
+
Pet.new.owner.should eq('Mr. Robinson')
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
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)
|
|
41
|
+
|
|
42
|
+
Pet.new.owner.should eq('Mr. Robinson')
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe 'when not using activesupport-decorators' do
|
|
48
|
+
before :all do
|
|
49
|
+
ActiveSupportDecorators.clear
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
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)
|
|
55
|
+
|
|
56
|
+
expect { Pet.new.owner }.to raise_error
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
data/spec/spec_helper.rb
ADDED
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.
|
|
4
|
+
version: 1.0.2
|
|
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-
|
|
11
|
+
date: 2014-03-02 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -49,9 +49,12 @@ files:
|
|
|
49
49
|
- Rakefile
|
|
50
50
|
- lib/active_support_decorators/active_support_decorators.rb
|
|
51
51
|
- lib/active_support_decorators/dependencies_patch.rb
|
|
52
|
-
- lib/active_support_decorators/graph.rb
|
|
53
52
|
- lib/active_support_decorators/version.rb
|
|
54
53
|
- lib/activesupport-decorators.rb
|
|
54
|
+
- spec/lib/active_support_decorators/active_support_decorator_spec.rb
|
|
55
|
+
- spec/lib/active_support_decorators/support/decorators/pet_decorator.rb
|
|
56
|
+
- spec/lib/active_support_decorators/support/originals/pet.rb
|
|
57
|
+
- spec/spec_helper.rb
|
|
55
58
|
homepage: https://github.com/pierre-pretorius/activesupport-decorators
|
|
56
59
|
licenses:
|
|
57
60
|
- MIT
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
class Graph
|
|
2
|
-
Node = Struct.new(:object) do
|
|
3
|
-
def depends_on
|
|
4
|
-
@depends_on ||= []
|
|
5
|
-
end
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def initialize
|
|
9
|
-
@nodes = []
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
def list_by_order
|
|
13
|
-
result = []
|
|
14
|
-
|
|
15
|
-
until @nodes.empty?
|
|
16
|
-
nodes_without_dependencies = @nodes.select { |n| n.depends_on.empty? }
|
|
17
|
-
result += nodes_without_dependencies.map { |n| n.object }.sort
|
|
18
|
-
|
|
19
|
-
nodes_without_dependencies.each do |to_delete|
|
|
20
|
-
@nodes.delete(to_delete)
|
|
21
|
-
@nodes.each { |n| n.depends_on.delete(to_delete) }
|
|
22
|
-
end
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
result
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def add(object)
|
|
29
|
-
find_or_add_node(object)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def add_with_edge(from_object, to_object)
|
|
33
|
-
fail 'Can not add edge from object to itself' if from_object == to_object
|
|
34
|
-
from_node = find_or_add_node(from_object)
|
|
35
|
-
to_node = find_or_add_node(to_object)
|
|
36
|
-
to_node.depends_on << from_node
|
|
37
|
-
fail 'EMPTY' if from_node.nil?
|
|
38
|
-
end
|
|
39
|
-
|
|
40
|
-
private
|
|
41
|
-
def find_or_add_node(object)
|
|
42
|
-
node = @nodes.find { |n| n.object == object }
|
|
43
|
-
unless node
|
|
44
|
-
node = Node.new(object)
|
|
45
|
-
@nodes.push(node)
|
|
46
|
-
end
|
|
47
|
-
node
|
|
48
|
-
end
|
|
49
|
-
end
|