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.
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