context_exposer 0.1.0 → 0.3.0

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.
Files changed (41) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +13 -1
  3. data/README.md +186 -16
  4. data/lib/context_exposer.rb +15 -0
  5. data/lib/context_exposer/base_controller.rb +67 -47
  6. data/lib/context_exposer/cached_resource_controller.rb +10 -0
  7. data/lib/context_exposer/integrations.rb +8 -0
  8. data/lib/context_exposer/integrations/base.rb +15 -0
  9. data/lib/context_exposer/integrations/key_filter.rb +30 -0
  10. data/lib/context_exposer/integrations/with_decent_exposure.rb +19 -0
  11. data/lib/context_exposer/integrations/with_decorates_before.rb +31 -0
  12. data/lib/context_exposer/integrations/with_instance_vars.rb +19 -0
  13. data/lib/context_exposer/macros.rb +17 -0
  14. data/lib/context_exposer/patch/decorates_before_rendering.rb +179 -0
  15. data/lib/context_exposer/rails_config.rb +3 -0
  16. data/lib/context_exposer/resource_controller.rb +30 -8
  17. data/lib/context_exposer/version.rb +1 -1
  18. data/lib/context_exposer/view_context.rb +1 -1
  19. data/spec/app/items_spec.rb +36 -0
  20. data/spec/app/posts_spec.rb +29 -1
  21. data/spec/context_exposer/expose_resource_spec.rb +51 -38
  22. data/spec/context_exposer/expose_spec.rb +11 -10
  23. data/spec/dummy/app/controllers/items_controller.rb +12 -0
  24. data/spec/dummy/app/controllers/posts_controller.rb +3 -4
  25. data/spec/dummy/app/views/items/index.html.erb +2 -0
  26. data/spec/dummy/app/views/items/show.html.erb +2 -0
  27. data/spec/dummy/app/views/posts/index.html.erb +2 -1
  28. data/spec/dummy/app/views/posts/show.html.erb +2 -1
  29. data/spec/dummy/config/initializers/context_exposer.rb +1 -0
  30. data/spec/dummy/config/routes.rb +1 -57
  31. data/spec/dummy/log/test.log +483 -0
  32. data/spec/dummy_spec_helper.rb +82 -0
  33. data/spec/spec_helper.rb +13 -1
  34. data/spec/support/decorators/base_decorator.rb +22 -0
  35. data/spec/support/decorators/item_decorator.rb +11 -0
  36. data/spec/support/decorators/post_decorator.rb +5 -0
  37. data/spec/support/models/base.rb +18 -0
  38. data/spec/support/models/base/clazz_methods.rb +29 -0
  39. data/spec/support/models/item.rb +9 -0
  40. data/spec/support/models/post.rb +5 -0
  41. metadata +38 -2
@@ -0,0 +1,10 @@
1
+ module ContextExposer::CachedResourceController
2
+ extend ActiveSupport::Concern
3
+ include ContextExposer::ResourceController
4
+
5
+ module ClassMethods
6
+ def _exposing name, options = {}, &block
7
+ expose_cached name, options, &block
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,8 @@
1
+ module ContextExposer
2
+ module Integrations
3
+ end
4
+ end
5
+
6
+ %w{base key_filter with_decent_exposure with_decorates_before with_instance_vars}.each do |name|
7
+ require "context_exposer/integrations/#{name}"
8
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ ActiveSupport.on_load(:action_controller) do
2
+ extend ContextExposer::Macros
3
+ end
@@ -3,18 +3,35 @@ module ContextExposer::ResourceController
3
3
  include ContextExposer::BaseController
4
4
 
5
5
  included do
6
- puts "included: #{_normalized_resource_name}"
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
@@ -1,3 +1,3 @@
1
1
  module ContextExposer
2
- VERSION = "0.1.0"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -10,5 +10,5 @@ class ContextExposer::ViewContext
10
10
  def define_singleton_method(name, &block)
11
11
  eigenclass = class<<self; self end
12
12
  eigenclass.class_eval {define_method name, block}
13
- end
13
+ end
14
14
  end
@@ -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