context_exposer 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile +13 -1
- data/README.md +186 -16
- data/lib/context_exposer.rb +15 -0
- data/lib/context_exposer/base_controller.rb +67 -47
- data/lib/context_exposer/cached_resource_controller.rb +10 -0
- data/lib/context_exposer/integrations.rb +8 -0
- data/lib/context_exposer/integrations/base.rb +15 -0
- data/lib/context_exposer/integrations/key_filter.rb +30 -0
- data/lib/context_exposer/integrations/with_decent_exposure.rb +19 -0
- data/lib/context_exposer/integrations/with_decorates_before.rb +31 -0
- data/lib/context_exposer/integrations/with_instance_vars.rb +19 -0
- data/lib/context_exposer/macros.rb +17 -0
- data/lib/context_exposer/patch/decorates_before_rendering.rb +179 -0
- data/lib/context_exposer/rails_config.rb +3 -0
- data/lib/context_exposer/resource_controller.rb +30 -8
- data/lib/context_exposer/version.rb +1 -1
- data/lib/context_exposer/view_context.rb +1 -1
- data/spec/app/items_spec.rb +36 -0
- data/spec/app/posts_spec.rb +29 -1
- data/spec/context_exposer/expose_resource_spec.rb +51 -38
- data/spec/context_exposer/expose_spec.rb +11 -10
- data/spec/dummy/app/controllers/items_controller.rb +12 -0
- data/spec/dummy/app/controllers/posts_controller.rb +3 -4
- data/spec/dummy/app/views/items/index.html.erb +2 -0
- data/spec/dummy/app/views/items/show.html.erb +2 -0
- data/spec/dummy/app/views/posts/index.html.erb +2 -1
- data/spec/dummy/app/views/posts/show.html.erb +2 -1
- data/spec/dummy/config/initializers/context_exposer.rb +1 -0
- data/spec/dummy/config/routes.rb +1 -57
- data/spec/dummy/log/test.log +483 -0
- data/spec/dummy_spec_helper.rb +82 -0
- data/spec/spec_helper.rb +13 -1
- data/spec/support/decorators/base_decorator.rb +22 -0
- data/spec/support/decorators/item_decorator.rb +11 -0
- data/spec/support/decorators/post_decorator.rb +5 -0
- data/spec/support/models/base.rb +18 -0
- data/spec/support/models/base/clazz_methods.rb +29 -0
- data/spec/support/models/item.rb +9 -0
- data/spec/support/models/post.rb +5 -0
- metadata +38 -2
@@ -0,0 +1,15 @@
|
|
1
|
+
module ContextExposer
|
2
|
+
module Integrations
|
3
|
+
module Base
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
protected
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
def _exposure_filter keys, options = {}
|
10
|
+
::ContextExposer::Integrations::KeyFilter.new(keys, options).filter
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module ContextExposer
|
2
|
+
module Integrations
|
3
|
+
class KeyFilter
|
4
|
+
attr_reader :keys, :options
|
5
|
+
|
6
|
+
def initialize keys, options = {}
|
7
|
+
@keys = keys || []
|
8
|
+
@options = options
|
9
|
+
end
|
10
|
+
|
11
|
+
# expose all exposures exposed by decent_exposure to context
|
12
|
+
def filter
|
13
|
+
the_keys = keys - except
|
14
|
+
only.empty? ? the_keys : the_keys.select {|k| only.include? k.to_sym }
|
15
|
+
end
|
16
|
+
|
17
|
+
def except
|
18
|
+
@except ||= symbolize_opts :except
|
19
|
+
end
|
20
|
+
|
21
|
+
def only
|
22
|
+
@only ||= symbolize_opts :only
|
23
|
+
end
|
24
|
+
|
25
|
+
def symbolize_opts name
|
26
|
+
(options[name.to_sym] || {}).map(&:to_sym)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ContextExposer::Integrations
|
2
|
+
module WithDecentExposure
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# expose all exposures exposed by decent_exposure to context
|
7
|
+
def context_expose_decently options = {}
|
8
|
+
expose_keys = _exposures.keys
|
9
|
+
|
10
|
+
_exposure_filter(expose_keys, options).each do |exposure|
|
11
|
+
exposed exposure do
|
12
|
+
send(exposure)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :expose_decently, :context_expose_decently
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ContextExposer::Integrations
|
2
|
+
module WithDecoratesBefore
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# expose all exposures exposed by decent_exposure to context
|
7
|
+
def context_expose_decorated_instance_vars options = {}
|
8
|
+
coll_decorates = __decorates_collection__[0..-1]
|
9
|
+
basic_decorates = __decorates__[0..-1]
|
10
|
+
|
11
|
+
all_decorates = case options[:for]
|
12
|
+
when :collection
|
13
|
+
coll_decorates
|
14
|
+
when :non_collection
|
15
|
+
basic_decorates
|
16
|
+
else
|
17
|
+
coll_decorates + basic_decorates
|
18
|
+
end
|
19
|
+
|
20
|
+
expose_keys = all_decorates.map {|v| v[1..-1]}
|
21
|
+
|
22
|
+
_exposure_filter(keys, options).each do |exposure|
|
23
|
+
exposed exposure do
|
24
|
+
send("@#{exposure}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
alias_method :expose_decorated_instance_vars, :context_expose_decorated_instance_vars
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module ContextExposer::Integrations
|
2
|
+
module WithInstanceVars
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
module ClassMethods
|
6
|
+
# expose all exposures exposed by decent_exposure to context
|
7
|
+
def context_expose_instance_vars options = {}
|
8
|
+
expose_keys = self.instance_variables.map {|v| v[1..-1]}
|
9
|
+
|
10
|
+
_exposure_filter(expose_keys, options).each do |exposure|
|
11
|
+
exposed exposure do
|
12
|
+
send("@#{exposure}")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
alias_method :expose_instance_vars, :context_expose_instance_vars
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ContextExposer
|
2
|
+
module Macros
|
3
|
+
def context_exposer name = :base, options = {}
|
4
|
+
self.send :include, "ContextExposer::#{name.to_s.camelize}Controller".constantize
|
5
|
+
|
6
|
+
integrates_with [options[:with]].flatten if options[:with]
|
7
|
+
end
|
8
|
+
|
9
|
+
def decorates_before_rendering
|
10
|
+
unless defined? ::DecoratesBeforeRendering
|
11
|
+
raise "DecoratesBeforeRendering not found, please include the gem 'decorates_before_rendering'"
|
12
|
+
end
|
13
|
+
self.send :include, DecoratesBeforeRendering
|
14
|
+
end
|
15
|
+
alias_method :decorates_before_render, :decorates_before_rendering
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
module DecoratesBeforeRendering
|
2
|
+
class FindModelError < StandardError; end
|
3
|
+
class DecoratorError < StandardError; end
|
4
|
+
|
5
|
+
def render *args
|
6
|
+
__auto_decorate_exposed_ones_
|
7
|
+
super(*args)
|
8
|
+
end
|
9
|
+
|
10
|
+
def __auto_decorate_exposed_ones_
|
11
|
+
__decorate_ivars__
|
12
|
+
__decorate_exposed_ones_
|
13
|
+
__decorate_ctx_exposed_ones_
|
14
|
+
rescue StandardError => e
|
15
|
+
__handle_decorate_error_(e)
|
16
|
+
end
|
17
|
+
|
18
|
+
def __handle_decorate_error_ e
|
19
|
+
# puts "Error: #{e}"
|
20
|
+
|
21
|
+
if defined?(::Rails) && (Rails.respond_to? :logger)
|
22
|
+
Rails.logger.warn 'decorates_before_render: auto_decorate error: #{e}'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def __exposed_ones_
|
27
|
+
return [] unless self.class.respond_to? :_exposures
|
28
|
+
@__exposed_ones_ ||= self.class._exposures.keys
|
29
|
+
end
|
30
|
+
|
31
|
+
def __ctx_exposed_ones_
|
32
|
+
return [] unless self.class.respond_to? :_exposure_storage
|
33
|
+
@__ctx_exposed_ones_ ||= self.class._exposure_storage.keys
|
34
|
+
end
|
35
|
+
|
36
|
+
def __decorate_exposed_ones_
|
37
|
+
__exposed_ones_.each do |name|
|
38
|
+
ivar_name = "@#{name}"
|
39
|
+
obj = send(name)
|
40
|
+
decorated = __attempt_to_decorate_(obj)
|
41
|
+
|
42
|
+
decorated = case objs
|
43
|
+
when Array
|
44
|
+
__attempt_to_decorate_list_ objs
|
45
|
+
else
|
46
|
+
__attempt_to_decorate_ objs
|
47
|
+
end
|
48
|
+
|
49
|
+
send(:instance_variable_set, ivar_name, decorated) # if decorated
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def __decorate_ctx_exposed_ones_
|
54
|
+
__ctx_exposed_ones_.each do |name|
|
55
|
+
ivar_name = "@#{name}"
|
56
|
+
objs = ctx.send(name)
|
57
|
+
|
58
|
+
decorated = case objs
|
59
|
+
when Array
|
60
|
+
__attempt_to_decorate_list_ objs
|
61
|
+
else
|
62
|
+
__attempt_to_decorate_ objs
|
63
|
+
end
|
64
|
+
|
65
|
+
ctx.send(:instance_variable_set, ivar_name, decorated) # if decorated
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def __attempt_to_decorate_list_ objs
|
70
|
+
[objs].flatten.compact.map do |obj|
|
71
|
+
__attempt_to_decorate_(obj)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def __attempt_to_decorate_ obj
|
76
|
+
if obj
|
77
|
+
src = __src_for__(obj)
|
78
|
+
decorator = __normalized_decorator_for__(src)
|
79
|
+
__do_decoration_ decorator, obj
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def __do_decoration_ decorator, obj
|
84
|
+
return if !decorator || !obj
|
85
|
+
__validate_decorator!(decorator)
|
86
|
+
decorator.decorate(obj)
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def __validate_decorator! decorator
|
91
|
+
unless decorator.respond_to? :decorate
|
92
|
+
raise DecoratorError, "Decorator: #{decorator} must have a #decorate method"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def __decorate_error! decorator
|
97
|
+
raise DecoratorError, "Decorator: #{decorator} must have a #decorate method"
|
98
|
+
end
|
99
|
+
|
100
|
+
def __src_for__ obj
|
101
|
+
case obj
|
102
|
+
when Class
|
103
|
+
obj.class
|
104
|
+
else
|
105
|
+
obj
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def __normalized_decorator_for__(obj)
|
110
|
+
decorator = __decorator_for__(obj)
|
111
|
+
|
112
|
+
case decorator
|
113
|
+
when String
|
114
|
+
decorator.constantize.new obj
|
115
|
+
when Class
|
116
|
+
decorator.new obj
|
117
|
+
else
|
118
|
+
decorator
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def __decorator_for__(obj)
|
123
|
+
return obj.decorator(self) if obj.respond_to? :decorator
|
124
|
+
__decorator_name_for__(obj).constantize
|
125
|
+
rescue FindModelError => e
|
126
|
+
nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def __decorator_name_for__(obj)
|
130
|
+
"#{__model_name_for__(obj)}Decorator"
|
131
|
+
end
|
132
|
+
|
133
|
+
def __model_name_for__(obj)
|
134
|
+
return obj.model_name if obj.respond_to?(:model_name)
|
135
|
+
raise FindModelError, "#{obj} does not have an associated model"
|
136
|
+
end
|
137
|
+
|
138
|
+
def __decorate_ivars__
|
139
|
+
__validate_decorates_present_
|
140
|
+
return if __decorates_blank?
|
141
|
+
__decorates__ivars
|
142
|
+
__decorates_collection_ivars__
|
143
|
+
end
|
144
|
+
|
145
|
+
def __validate_decorates_present_
|
146
|
+
unless __has_decorates?
|
147
|
+
raise "Internal method '__decorates__' not found. You need to include the 'decorates_before_render' gem "
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def __has_decorates?
|
152
|
+
respond_to?(:__decorates__)
|
153
|
+
end
|
154
|
+
|
155
|
+
def __decorates_blank?
|
156
|
+
__decorates__.blank? and __decorates_collection__.blank?
|
157
|
+
end
|
158
|
+
|
159
|
+
def __decorates_collection_ivars__
|
160
|
+
if !__decorates_collection__.nil?
|
161
|
+
__decorate_ivar_names__(__decorates_collection__) do |ivar_name, ivar, options|
|
162
|
+
decorated = options.fetch(:with).decorate_collection(ivar)
|
163
|
+
instance_variable_set(ivar_name, decorated) if decorated
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def __decorates__ivars
|
169
|
+
if !__decorates__.nil?
|
170
|
+
__decorate_ivar_names__(__decorates__) do |ivar_name, ivar, options|
|
171
|
+
decorator = options.key?(:with) ? options.fetch(:with) : __decorator_for__(ivar)
|
172
|
+
if decorator
|
173
|
+
decorated = __do_decoration_(decorator, ivar)
|
174
|
+
instance_variable_set(ivar_name, decorated)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -3,18 +3,35 @@ module ContextExposer::ResourceController
|
|
3
3
|
include ContextExposer::BaseController
|
4
4
|
|
5
5
|
included do
|
6
|
-
|
7
|
-
|
8
|
-
exposed(_normalized_resource_name.singularize) do
|
9
|
-
find_single_resource
|
10
|
-
end
|
11
|
-
|
12
|
-
exposed(_normalized_resource_name.pluralize) { find_all_resources }
|
13
|
-
exposed(_normalized_resource_list) { find_all_resources.to_a }
|
6
|
+
expose_resources
|
14
7
|
end
|
15
8
|
|
16
9
|
protected
|
17
10
|
|
11
|
+
module ClassMethods
|
12
|
+
def expose_resources *types
|
13
|
+
types = types.flatten
|
14
|
+
types = types.empty? ? [:all] : types
|
15
|
+
|
16
|
+
unless expose_resource_method? :one, types
|
17
|
+
_exposing(_normalized_resource_name.singularize) { find_single_resource }
|
18
|
+
end
|
19
|
+
|
20
|
+
unless expose_resource_method? :many, types
|
21
|
+
_exposing(_normalized_resource_name.pluralize) { find_all_resources }
|
22
|
+
end
|
23
|
+
|
24
|
+
unless expose_resource_method? :list, types
|
25
|
+
_exposing(_normalized_resource_list) { find_all_resources.to_a }
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def expose_resource_method? type, types
|
30
|
+
([type, :all] & types).empty?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
|
18
35
|
def resource_id
|
19
36
|
params[:id]
|
20
37
|
end
|
@@ -28,6 +45,11 @@ module ContextExposer::ResourceController
|
|
28
45
|
end
|
29
46
|
|
30
47
|
module ClassMethods
|
48
|
+
# for use in ResourceController
|
49
|
+
def _exposing name, options = {}, &block
|
50
|
+
exposed name, options, &block
|
51
|
+
end
|
52
|
+
|
31
53
|
def _the_resource
|
32
54
|
clazz_name = self.to_s.sub(/Controller$/, '').singularize
|
33
55
|
clazz_name.constantize
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Run Capybara Spec on Dummy app PostsController
|
2
|
+
|
3
|
+
require 'dummy_spec_helper'
|
4
|
+
|
5
|
+
feature 'show list of items' do
|
6
|
+
let(:names) { ['Item 1', 'Item 2'] }
|
7
|
+
let(:name) { names.first }
|
8
|
+
|
9
|
+
|
10
|
+
before :each do
|
11
|
+
names.each{|n| Item.new n }
|
12
|
+
|
13
|
+
visit items_path
|
14
|
+
end
|
15
|
+
|
16
|
+
context '2 items' do
|
17
|
+
scenario 'display item 1 and 2' do
|
18
|
+
page.should have_content names.first
|
19
|
+
page.should have_content names.last
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
feature 'show item' do
|
25
|
+
let(:name) { 'Item 1' }
|
26
|
+
|
27
|
+
before do
|
28
|
+
Item.new name
|
29
|
+
end
|
30
|
+
|
31
|
+
scenario 'display one item' do
|
32
|
+
visit item_path(1)
|
33
|
+
|
34
|
+
page.should have_content "A very nice #{name}"
|
35
|
+
end
|
36
|
+
end
|