activesupport-decorators 1.0.3 → 2.0.1
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 +28 -31
- data/lib/active_support_decorators/active_support_decorators.rb +31 -43
- data/lib/active_support_decorators/dependencies_patch.rb +12 -10
- data/lib/active_support_decorators/version.rb +1 -1
- data/spec/lib/active_support_decorators/active_support_decorator_spec.rb +21 -31
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ab36f5e952455f62d9a6ba3297a3b9ccf0a4a882
|
|
4
|
+
data.tar.gz: ec8df96f59cc48cefd330897491137a4b50fbbce
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
+
[]
|
|
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', '~>
|
|
18
|
+
gem 'activesupport-decorators', '~> 2.0'
|
|
16
19
|
```
|
|
17
20
|
|
|
18
|
-
|
|
21
|
+
### Usage
|
|
22
|
+
|
|
23
|
+
#### Example 1 - Application extends engine (or any other) class.
|
|
19
24
|
|
|
20
|
-
Your
|
|
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
|
|
28
|
-
the following model decorator (in
|
|
29
|
-
of 'Pet
|
|
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
|
|
37
|
+
class Pet
|
|
33
38
|
belongs_to :owner
|
|
34
39
|
end
|
|
35
40
|
```
|
|
36
41
|
|
|
37
|
-
|
|
38
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 :
|
|
68
|
-
ActiveSupportDecorators.
|
|
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
|
-
*
|
|
91
|
-
|
|
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
|
-
|
|
2
|
+
def self.paths
|
|
3
|
+
@paths ||= []
|
|
4
|
+
end
|
|
3
5
|
|
|
4
|
-
def self.
|
|
5
|
-
|
|
6
|
+
def self.pattern
|
|
7
|
+
@pattern ||= '_decorator'
|
|
6
8
|
end
|
|
7
9
|
|
|
8
|
-
def self.
|
|
9
|
-
@
|
|
10
|
+
def self.pattern=(pattern)
|
|
11
|
+
@pattern = pattern
|
|
10
12
|
end
|
|
11
13
|
|
|
12
|
-
def self.
|
|
13
|
-
|
|
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.
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
33
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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
|
-
|
|
46
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
-
|
|
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
|
|
4
|
-
|
|
3
|
+
module ActiveSupport
|
|
4
|
+
module Dependencies
|
|
5
|
+
alias_method :require_or_load_without_multiple, :require_or_load
|
|
5
6
|
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
def require_or_load(file_name, const_path = nil)
|
|
8
|
+
order = ActiveSupportDecorators.load_path_order(file_name, const_path)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
14
|
-
|
|
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
|
|
@@ -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
|
-
|
|
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
|
|
12
|
-
before :
|
|
13
|
-
|
|
14
|
-
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
end
|
|
26
|
+
Pet.new.owner.should eq('Mr. Robinson')
|
|
35
27
|
end
|
|
36
28
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
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
|
-
|
|
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
|
-
|
|
54
|
-
ActiveSupport.require_or_load(
|
|
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:
|
|
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-
|
|
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.
|
|
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:
|