context_exposer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.rspec +1 -0
  4. data/Gemfile +22 -0
  5. data/LICENSE.txt +22 -0
  6. data/README.md +111 -0
  7. data/Rakefile +1 -0
  8. data/context_exposer.gemspec +23 -0
  9. data/lib/context_exposer.rb +8 -0
  10. data/lib/context_exposer/base_controller.rb +84 -0
  11. data/lib/context_exposer/version.rb +3 -0
  12. data/lib/context_exposer/view_context.rb +14 -0
  13. data/spec/app/posts_spec.rb +1 -0
  14. data/spec/context_exposer/expose_spec.rb +128 -0
  15. data/spec/dummy/README.rdoc +261 -0
  16. data/spec/dummy/Rakefile +7 -0
  17. data/spec/dummy/app/assets/javascripts/application.js +15 -0
  18. data/spec/dummy/app/assets/stylesheets/application.css +13 -0
  19. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  20. data/spec/dummy/app/controllers/posts_controller.rb +13 -0
  21. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  22. data/spec/dummy/app/mailers/.gitkeep +0 -0
  23. data/spec/dummy/app/models/.gitkeep +0 -0
  24. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  25. data/spec/dummy/app/views/posts/index.html.erb +1 -0
  26. data/spec/dummy/app/views/posts/show.html.erb +1 -0
  27. data/spec/dummy/config.ru +4 -0
  28. data/spec/dummy/config/application.rb +65 -0
  29. data/spec/dummy/config/boot.rb +10 -0
  30. data/spec/dummy/config/environment.rb +5 -0
  31. data/spec/dummy/config/environments/development.rb +30 -0
  32. data/spec/dummy/config/environments/production.rb +65 -0
  33. data/spec/dummy/config/environments/test.rb +34 -0
  34. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  35. data/spec/dummy/config/initializers/inflections.rb +15 -0
  36. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  37. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  38. data/spec/dummy/config/initializers/session_store.rb +8 -0
  39. data/spec/dummy/config/initializers/wrap_parameters.rb +10 -0
  40. data/spec/dummy/config/locales/en.yml +5 -0
  41. data/spec/dummy/config/routes.rb +60 -0
  42. data/spec/dummy/lib/assets/.gitkeep +0 -0
  43. data/spec/dummy/log/.gitkeep +0 -0
  44. data/spec/dummy/log/test.log +2163 -0
  45. data/spec/dummy/public/404.html +26 -0
  46. data/spec/dummy/public/422.html +26 -0
  47. data/spec/dummy/public/500.html +25 -0
  48. data/spec/dummy/public/favicon.ico +0 -0
  49. data/spec/dummy/script/rails +6 -0
  50. data/spec/spec_helper.rb +8 -0
  51. metadata +162 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 12da37ed9022c53dd944d8c73f53c0da2980a65f
4
+ data.tar.gz: 7cd49fd373d8ea82633f36064c21eb95303e3ab4
5
+ SHA512:
6
+ metadata.gz: 4cc5284dce7e9ac8c4d9afd9470cbd69e6f29f2bcc2c75749439ad195a86326e9848dd292db2cc1e8f2d31641eee89d28198e43620d295fe930a39bb631524a9
7
+ data.tar.gz: 92d81c8be3d0dd2dd4e067f3c3c1a73837319d01ffeaaba0a2c4f29890e89caaa75960b7650994ade13f545c0446339bcc69f3aa57428679500d14e94d293b61
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .DS_Store
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color --format nested
data/Gemfile ADDED
@@ -0,0 +1,22 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in context_exposer.gemspec
4
+ gemspec
5
+
6
+ gem 'pry'
7
+ gem 'rails', '>= 3.1'
8
+
9
+ gem "rspec-rails", :group => [:test, :development]
10
+
11
+ group :test do
12
+ # gem 'factory_girl_rails', :require => false
13
+ # gem "capybara"
14
+ # gem "capybara-webkit"
15
+ # gem 'growl'
16
+ # gem "spork"
17
+ # gem "guard"
18
+ # gem "guard-bundler"
19
+ # gem "guard-rspec"
20
+ # gem "guard-spork"
21
+ # gem "launchy"
22
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Kristian Mandrup
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,111 @@
1
+ # ContextExposer
2
+
3
+ Allows the Controller to exposes a Context object to the View.
4
+ This Context object alone contains all the information passed to the View from the controller.
5
+
6
+ No more pollution of the View with content helper methods or even worse, instance variables.
7
+
8
+ The Context object will by default be an instance of `ContextExposer::ViewContext`, but you can subclass this baseclass to add you own logic for more complex scenarios. This also allows for a more modular approach, where you can easily share or subclass logic between different view contexts. Nice!
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'context_exposer'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install context_exposer
23
+
24
+ ## Usage
25
+
26
+ Use the `exposed` method which takes a name of the method to be created on the ViewContext and a block with the logic.
27
+
28
+ Example:
29
+
30
+ ```ruby
31
+ class PostsController < ActionController::Base
32
+ include ContextExposer::BaseController
33
+
34
+ exposed(:post) { Post.find params[:id] }
35
+ exposed(:posts) { Post.find params[:id] }
36
+ end
37
+ ```
38
+
39
+ You can also define your own subclass of `ViewContext` and designate an instance of this custom class as your "exposed" target, via `view_context_class`method.
40
+
41
+ Example:
42
+
43
+ ```ruby
44
+ class PostsController < ActionController::Base
45
+ include ContextExposer::BaseController
46
+
47
+ view_context_class :posts_view_context
48
+
49
+ exposed(:post) { Post.find params[:id] }
50
+ exposed(:posts) { Post.find params[:id] }
51
+ end
52
+ ```
53
+
54
+ ```ruby
55
+ class PostsViewContext < ContextExposer::ViewContext
56
+ def initialize controller
57
+ super
58
+ end
59
+
60
+ def total
61
+ posts.size
62
+ end
63
+
64
+ def admin_posts
65
+ return [] unless admin?
66
+ posts.select {|post| post.admin? }
67
+ end
68
+
69
+ protected
70
+
71
+ def current_user
72
+ controller.current_user
73
+ end
74
+
75
+ def admin?
76
+ current_user.admin?
77
+ end
78
+ end
79
+ ```
80
+
81
+ This opens up some amazing possibilities to really put the logic where it belongs.The custom ViewContext would benefit from having the "admin" and "user" logic extracted either to separate modules or a custom ViewContext base class ;)
82
+
83
+ This approach opens up many new exciting ways to slice and dice your logic in a much better way, a new *MVC-C* architecture, the extra "C" for *Context*.
84
+
85
+
86
+ ## TODO
87
+
88
+ Add some useful subclasses of `BaseController`, that add some extra magic!
89
+ Also add some basic modules to integrate with typical authentication solutions etc. Get inspiration from other similar gems, fx `decent_exposure`. Allow for integration between these different solutions.
90
+
91
+ ### ResourceController
92
+
93
+ It sould be nice to have a `ResourceController` to automatically set up the typical singular and plural-form resource helpers.
94
+
95
+ This would simplify the above `PostsController` example to this:
96
+
97
+ ```ruby
98
+ class PostsController < ActionController::Base
99
+ include ContextExposer::ResourceController
100
+ end
101
+ ```
102
+
103
+ Please help out :)
104
+
105
+ ## Contributing
106
+
107
+ 1. Fork it
108
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
109
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
110
+ 4. Push to the branch (`git push origin my-new-feature`)
111
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'context_exposer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "context_exposer"
8
+ spec.version = ContextExposer::VERSION
9
+ spec.authors = ["Kristian Mandrup"]
10
+ spec.email = ["kmandrup@gmail.com"]
11
+ spec.description = %q{Exposes a ViewContext object to the View with all the data needed by the view}
12
+ spec.summary = %q{The Context object becomes the single communication point between View and Controller}
13
+ spec.homepage = "https://github.com/kristianmandrup/context_exposer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ end
@@ -0,0 +1,8 @@
1
+ require "context_exposer/version"
2
+
3
+ module ContextExposer
4
+ end
5
+
6
+ require "active_support"
7
+ require "context_exposer/base_controller"
8
+ require "context_exposer/view_context"
@@ -0,0 +1,84 @@
1
+ module ContextExposer::BaseController
2
+ extend ActiveSupport::Concern
3
+
4
+ included do
5
+ before_filter :configure_exposed_context
6
+
7
+ expose_context :context
8
+ end
9
+
10
+ def view_context
11
+ @view_context ||= build_view_context
12
+ end
13
+ alias_method :context, :view_context
14
+
15
+ module ClassMethods
16
+ def exposed name, &block
17
+ # puts "store: #{name} in hash storage for class #{self}"
18
+ exposure_storage[name.to_sym] = block
19
+ end
20
+
21
+ def view_context_class name
22
+ define_method :view_context_class do
23
+ @view_context_class ||= name.kind_of?(Class) ? name : name.to_s.camelize.constantize
24
+ end
25
+ end
26
+
27
+ protected
28
+
29
+ def expose_context name
30
+ return if exposed_view_context?
31
+ if ActionController::Base.instance_methods.include?(name.to_sym)
32
+ Kernel.warn "[WARNING] You are exposing the `#{name}` method, " \
33
+ "which overrides an existing ActionController method of the same name. " \
34
+ "Consider a different exposure name\n" \
35
+ "#{caller.first}"
36
+ end
37
+ helper_method name
38
+ hide_action name
39
+ @exposed_view_context = true
40
+ end
41
+
42
+ def exposed_view_context?
43
+ @exposed_view_context ||= false
44
+ end
45
+
46
+ def exposure_storage
47
+ exposure_hash[self.to_s] ||= {}
48
+ end
49
+
50
+ def exposure_hash
51
+ @exposure_hash ||= {}
52
+ end
53
+ end
54
+
55
+ # must be called after Controller is instantiated
56
+ def configure_exposed_context
57
+ return if configured_exposed_context?
58
+ clazz = self.class
59
+ exposed_methods = clazz.send(:exposure_hash)[clazz.to_s] || []
60
+ # puts "exposed_methods for: #{clazz} - #{exposed_methods}"
61
+ exposed_methods.each do |name, procedure|
62
+ view_context.send :define_singleton_method, name do
63
+ procedure.call
64
+ end
65
+ end
66
+ @configured_exposed_context = true
67
+ end
68
+
69
+ def configured_exposed_context?
70
+ @configured_exposed_context == true
71
+ end
72
+
73
+ protected
74
+
75
+ # returns a ViewContext object
76
+ # view helpers can be exposed as singleton methods, dynamically be attached (see below)
77
+ def build_view_context
78
+ view_context_class.new self
79
+ end
80
+
81
+ def view_context_class
82
+ @view_context_class ||= ContextExposer::ViewContext
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ module ContextExposer
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,14 @@
1
+ class ContextExposer::ViewContext
2
+ attr_reader :controller
3
+
4
+ def initialize controller = nil
5
+ @controller = controller
6
+ end
7
+
8
+ protected
9
+
10
+ def define_singleton_method(name, &block)
11
+ eigenclass = class<<self; self end
12
+ eigenclass.class_eval {define_method name, block}
13
+ end
14
+ end
@@ -0,0 +1 @@
1
+ # Run Capybara Spec on Dummy app PostsController
@@ -0,0 +1,128 @@
1
+ require 'context_exposer'
2
+ require 'action_controller'
3
+
4
+ class MyController < ActionController::Base
5
+ include ContextExposer::BaseController
6
+
7
+ exposed(:post) { Post.find params[:id] }
8
+
9
+ def post
10
+ end
11
+ end
12
+
13
+ class MyCoolController < ActionController::Base
14
+ include ContextExposer::BaseController
15
+
16
+ exposed(:coolness) { "MegaCool" }
17
+
18
+ view_context_class :mega_cool_view_context
19
+
20
+ def show
21
+ configure_exposed_context
22
+ end
23
+
24
+ def params; end
25
+ end
26
+
27
+ class MegaCoolViewContext < ContextExposer::ViewContext
28
+ def initialize controller
29
+ super
30
+ end
31
+
32
+ def power_of number
33
+ number * number
34
+ end
35
+ end
36
+
37
+ describe ContextExposer::BaseController do
38
+
39
+ describe "controller" do
40
+ subject { controller }
41
+
42
+ let(:controller) { MyController.new }
43
+
44
+ # run action post
45
+ before :each do
46
+ controller.post
47
+ end
48
+
49
+ it 'defines :post as an action_method' do
50
+ expect(subject.action_methods).to include('post')
51
+ end
52
+
53
+ it "defines a method context" do
54
+ expect(subject).to respond_to(:context)
55
+ end
56
+
57
+ it "exposes the context to the view layer as a helper" do
58
+ expect(subject._helper_methods).to include(:context)
59
+ end
60
+
61
+ it "prevents the context method from being routable" do
62
+ expect(subject.hidden_actions).to include("context")
63
+ end
64
+
65
+ it 'configured exposed context' do
66
+ expect(subject.configured_exposed_context?).to be_true
67
+ end
68
+
69
+ context 'context' do
70
+ subject { controller.context }
71
+
72
+ it "is an instance of ContextExposer::ViewContext" do
73
+ expect(subject).to be_a ContextExposer::ViewContext
74
+ end
75
+
76
+ it "defines a method :bird" do
77
+ expect(subject).to respond_to(:bird)
78
+ end
79
+
80
+ it "calling method :bird returns 'Bird' " do
81
+ expect(subject.bird).to eq "Bird"
82
+ end
83
+ end
84
+ end
85
+
86
+ describe 'MyCoolController' do
87
+ subject { controller }
88
+
89
+ let(:controller) { MyCoolController.new }
90
+
91
+ # run action post
92
+ before :each do
93
+ controller.show
94
+ end
95
+
96
+ context 'context' do
97
+ subject { controller.context }
98
+
99
+ it "inherits from ContextExposer::ViewContext" do
100
+ expect(subject).to be_a ContextExposer::ViewContext
101
+ end
102
+
103
+ it "is an instance of MegaCoolViewContext" do
104
+ expect(subject).to be_a MegaCoolViewContext
105
+ end
106
+
107
+ it "has reference to controller" do
108
+ expect(subject.controller).to eq controller
109
+ end
110
+
111
+ it "defines a method :coolness" do
112
+ expect(subject).to respond_to(:coolness)
113
+ end
114
+
115
+ it "calling method :coolness returns 'MegaCool' " do
116
+ expect(subject.coolness).to eq "MegaCool"
117
+ end
118
+
119
+ it "defines a method :power_of" do
120
+ expect(subject).to respond_to(:power_of)
121
+ end
122
+
123
+ it "calling method :power_of(2) returns 4" do
124
+ expect(subject.power_of 2).to eq 4
125
+ end
126
+ end
127
+ end
128
+ end