activesupport-decorators 1.0.1 → 1.0.2

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: 7ac0e53aa4f0f2a5552ebdffb9bc6f8862bfb583
4
- data.tar.gz: 0bc4b6985d4c867c732345478627e3ed2c69be1f
3
+ metadata.gz: 68468583a43ef2fe9365a6d4f47f6e96a5cb2520
4
+ data.tar.gz: fa6523c3c0aafa1a4c5d669e415037a5fdd89bef
5
5
  SHA512:
6
- metadata.gz: a7ea6a25f3474820c952786c12d9c04d6062ee8bc398b2b003170432561ef479bc82d32a3367f86640349c5d204da5ca4832078e4eeccad10163fe1129b3bb46
7
- data.tar.gz: d68066a4e76725ffd03bfd4efa174f7fed47ebc686f006c9e458b551ef311a6d72bee37e8f039b492b942f87fdd612612d2df3154a4b8a8559bcd49d9a93c6a3
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 will need to manually load the additional (decorator) file. Usually you don't want to want to introduce
8
- hard dependencies such as require statements. You also don't want to preload a bunch of classes in a Rails initializer.
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
- ## Installation
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
- ## Example 1 - Engine extends application class.
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
- ## Example 2 - Application extends engine class.
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
- ## Example 3 - Engine extends another engine class.
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
- ## Debugging
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
- ## Decorator file pattern
84
-
85
- By default decorator files are matched with '_decorator' appended to the file name. You can remove this suffix
86
- completely or use your own one. Note the method signature of add:
87
-
88
- ```Ruby
89
- ActiveSupportDecorators.add(path, decorator_path, file_pattern = '_decorator')
90
- ```
91
-
92
- ## Decorating decorators
93
-
94
- Nested decorators are supported. Just set them to decorate a path where the dependant decorators are placed.
95
-
96
- ## Comparison to other gems
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
- Dependency = Struct.new(:path, :decorator_path, :file_pattern)
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, file_pattern = '_decorator')
15
- dependencies << Dependency.new(path, decorator_path, file_pattern)
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
- graph = Graph.new
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 |dep|
35
- # If an attempt is made to load the original file, ensure the decorators are loaded afterwards.
36
- if sanitized_file_name.starts_with?(dep.path)
37
- relative_name = sanitized_file_name.sub(dep.path, '')
38
- decorator_file = "#{dep.decorator_path}#{relative_name}#{dep.file_pattern}"
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
- if File.file?(decorator_file + '.rb')
41
- graph.add_with_edge(sanitized_file_name, decorator_file)
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
- # 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 dep.path && sanitized_file_name.starts_with?(dep.decorator_path)
48
- relative_name = sanitized_file_name.sub(dep.decorator_path, '')
49
- decorated_file = "#{dep.path}#{relative_name}".sub(/#{dep.file_pattern}$/, '')
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
- if File.file?(decorated_file + '.rb')
52
- graph.add_with_edge(decorated_file, sanitized_file_name)
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
- graph.list_by_order
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
@@ -1,3 +1,3 @@
1
1
  module ActiveSupportDecorators
2
- VERSION = '1.0.1'
2
+ VERSION = '1.0.2'
3
3
  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
@@ -0,0 +1,5 @@
1
+ Pet.class_eval do
2
+ def owner
3
+ 'Mr. Robinson'
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class Pet
2
+ def bark
3
+ 'Woof!'
4
+ end
5
+ end
@@ -0,0 +1,8 @@
1
+ require 'bundler/setup'
2
+ Bundler.setup
3
+
4
+ require 'activesupport-decorators'
5
+
6
+ RSpec.configure do |config|
7
+ # No extra config yet.
8
+ 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.1
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-01 00:00:00.000000000 Z
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