delegated_presenter 0.1.1 → 1.0.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.
- data/.gitignore +7 -0
- data/.rspec +2 -0
- data/.travis.yml +7 -0
- data/Gemfile +15 -2
- data/MIT-LICENSE +20 -0
- data/README.md +69 -2
- data/README.rdoc +3 -0
- data/Rakefile +27 -1
- data/delegated_presenter.gemspec +8 -0
- data/lib/delegated_presenter/base.rb +122 -10
- data/lib/delegated_presenter/error.rb +11 -0
- data/lib/delegated_presenter/presents_before_rendering.rb +46 -0
- data/lib/delegated_presenter/railtie.rb +6 -0
- data/lib/delegated_presenter/version.rb +1 -1
- data/lib/delegated_presenter.rb +2 -0
- data/lib/generators/templates/presenter.rb.erb +1 -1
- data/lib/tasks/delegated_presenter_tasks.rake +4 -0
- data/spec/dummy/.rspec +1 -0
- data/spec/dummy/README.rdoc +261 -0
- data/spec/dummy/Rakefile +7 -0
- data/spec/dummy/app/assets/javascripts/application.js +15 -0
- data/spec/dummy/app/assets/javascripts/sample_objects.js +2 -0
- data/spec/dummy/app/assets/stylesheets/application.css +13 -0
- data/spec/dummy/app/assets/stylesheets/sample_objects.css +4 -0
- data/spec/dummy/app/controllers/application_controller.rb +3 -0
- data/spec/dummy/app/controllers/sample_objects_controller.rb +13 -0
- data/spec/dummy/app/helpers/application_helper.rb +2 -0
- data/spec/dummy/app/helpers/sample_objects_helper.rb +2 -0
- data/spec/dummy/app/mailers/.gitkeep +0 -0
- data/spec/dummy/app/models/.gitkeep +0 -0
- data/spec/dummy/app/models/inherited_sample_object.rb +3 -0
- data/spec/dummy/app/models/sample_object.rb +3 -0
- data/spec/dummy/app/presenters/sample_object_presenter.rb +16 -0
- data/spec/dummy/app/views/layouts/application.html.erb +14 -0
- data/spec/dummy/app/views/sample_objects/index.html.erb +0 -0
- data/spec/dummy/app/views/sample_objects/show.html.erb +0 -0
- data/spec/dummy/config/application.rb +65 -0
- data/spec/dummy/config/boot.rb +10 -0
- data/spec/dummy/config/database.yml +25 -0
- data/spec/dummy/config/environment.rb +5 -0
- data/spec/dummy/config/environments/development.rb +37 -0
- data/spec/dummy/config/environments/production.rb +67 -0
- data/spec/dummy/config/environments/test.rb +37 -0
- data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/spec/dummy/config/initializers/inflections.rb +15 -0
- data/spec/dummy/config/initializers/mime_types.rb +5 -0
- data/spec/dummy/config/initializers/secret_token.rb +7 -0
- data/spec/dummy/config/initializers/session_store.rb +8 -0
- data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/spec/dummy/config/locales/en.yml +5 -0
- data/spec/dummy/config/routes.rb +3 -0
- data/spec/dummy/config.ru +4 -0
- data/spec/dummy/db/migrate/20121006185000_create_sample_objects.rb +13 -0
- data/spec/dummy/db/schema.rb +27 -0
- data/spec/dummy/lib/assets/.gitkeep +0 -0
- data/spec/dummy/log/.gitkeep +0 -0
- data/spec/dummy/public/404.html +26 -0
- data/spec/dummy/public/422.html +26 -0
- data/spec/dummy/public/500.html +25 -0
- data/spec/dummy/public/favicon.ico +0 -0
- data/spec/dummy/script/rails +6 -0
- data/spec/factories/sample_object.rb +18 -0
- data/spec/presenter_spec_spec.rb +17 -0
- data/spec/presents_before_rendering_spec.rb +19 -0
- data/spec/spec_helper.rb +42 -0
- metadata +201 -3
data/.gitignore
CHANGED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Gemfile
CHANGED
@@ -1,4 +1,17 @@
|
|
1
|
-
source
|
1
|
+
source "http://rubygems.org"
|
2
2
|
|
3
|
-
#
|
3
|
+
# Declare your gem's dependencies in delegated_presenter.gemspec.
|
4
|
+
# Bundler will treat runtime dependencies like base dependencies, and
|
5
|
+
# development dependencies will be added by default to the :development group.
|
4
6
|
gemspec
|
7
|
+
|
8
|
+
# jquery-rails is used by the dummy application
|
9
|
+
gem "jquery-rails"
|
10
|
+
|
11
|
+
# Declare any dependencies that are still in development here instead of in
|
12
|
+
# your gemspec. These might include edge Rails or gems from your path or
|
13
|
+
# Git. Remember to move these dependencies to your gemspec before releasing
|
14
|
+
# your gem to rubygems.org.
|
15
|
+
|
16
|
+
# To use debugger
|
17
|
+
# gem 'debugger'
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# DelegatedPresenter
|
2
2
|
|
3
|
-
|
3
|
+
DelegatedPresenter gives you an easy way to present objects and collections to your models. It uses ruby's Delegate model so it is lightweight and functional!
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -18,7 +18,74 @@ Or install it yourself as:
|
|
18
18
|
|
19
19
|
## Usage
|
20
20
|
|
21
|
-
|
21
|
+
### Generate a presenter using a rails generator:
|
22
|
+
|
23
|
+
$ rails g delegated_presenter contact
|
24
|
+
|
25
|
+
### Add some functionality to your presenter
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class ContactPresenter < DelegatedPresenter::Base
|
29
|
+
|
30
|
+
# By default this presenter will try and present a Contact if it exists.
|
31
|
+
# You can explicitly tell the presenter to present other models using the following syntax:
|
32
|
+
|
33
|
+
presents OtherContact, SomeOtherModel
|
34
|
+
|
35
|
+
# Add some functionality to your presenter!
|
36
|
+
# The presenter will always look to the model it is presenting for methods and attributes not defined in the presenter.
|
37
|
+
# If you want to override model method, you can always call `presented_model.{method_name}` to access the original method.
|
38
|
+
|
39
|
+
def middle_initial
|
40
|
+
"#{middle_name.first}." if middle_name
|
41
|
+
end
|
42
|
+
|
43
|
+
def full_name
|
44
|
+
[prefix, first_name, middle_initial, last_name, suffix].compact.join(' ')
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
### Use the controller helper
|
51
|
+
See: {DelegatedPresenter::PresentsBeforeRendering}
|
52
|
+
|
53
|
+
Use the following to present a model instance or collection with a presenter, by default it will try and use a presenter with the same class as the instance or collection.
|
54
|
+
|
55
|
+
```ruby
|
56
|
+
class ContactsController < ApplicationController
|
57
|
+
|
58
|
+
presents :contact
|
59
|
+
|
60
|
+
end
|
61
|
+
```
|
62
|
+
|
63
|
+
If you for any reason need to explicitly define the presenter you may define a ```with: :presenter_name``` option, like so.
|
64
|
+
|
65
|
+
```ruby
|
66
|
+
class UsersController < ApplicationController
|
67
|
+
|
68
|
+
presents :user, with: :contact_presenter
|
69
|
+
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
Or if you like you can just manually initialize the presenter in your actions.
|
74
|
+
|
75
|
+
```ruby
|
76
|
+
class ContactsController < ApplicationController
|
77
|
+
|
78
|
+
def index
|
79
|
+
@contacts = ContactPresenter.new Contact.all
|
80
|
+
end
|
81
|
+
|
82
|
+
def show
|
83
|
+
@contact = ContactPresenter.new Contact.find(params[:id])
|
84
|
+
end
|
85
|
+
|
86
|
+
end
|
87
|
+
|
88
|
+
```
|
22
89
|
|
23
90
|
## Contributing
|
24
91
|
|
data/README.rdoc
ADDED
data/Rakefile
CHANGED
@@ -1 +1,27 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'DelegatedPresenter'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
data/delegated_presenter.gemspec
CHANGED
@@ -17,4 +17,12 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
gem.add_dependency "activesupport"
|
20
|
+
|
21
|
+
gem.add_development_dependency "rspec"
|
22
|
+
gem.add_development_dependency "sqlite3"
|
23
|
+
gem.add_development_dependency "rspec-rails"
|
24
|
+
gem.add_development_dependency "rails"
|
25
|
+
gem.add_development_dependency "factory_girl"
|
26
|
+
gem.add_development_dependency "database_cleaner"
|
27
|
+
|
20
28
|
end
|
@@ -1,34 +1,146 @@
|
|
1
|
+
# @abstract Inherit from it to create a presenter.
|
2
|
+
# @raise [DelegatedPresenter::Error::MethodHidden]
|
3
|
+
# If a method is specified as hidden.
|
4
|
+
# @raise [DelegatedPresenter::Error::MethodNotExposed]
|
5
|
+
# If a method is not exposed.
|
1
6
|
class DelegatedPresenter::Base < SimpleDelegator
|
2
7
|
|
3
|
-
|
8
|
+
PRESENTABLE = {}
|
9
|
+
HIDDEN_METHODS = {}
|
10
|
+
EXPOSED_METHODS = {}
|
11
|
+
|
12
|
+
private_constant :PRESENTABLE, :HIDDEN_METHODS, :EXPOSED_METHODS
|
13
|
+
|
14
|
+
delegate :exposed_methods, :hidden_methods, :presentable_objects, to: :presenter_class
|
4
15
|
|
5
16
|
alias :presented_model :__getobj__
|
6
17
|
|
18
|
+
# Initializes the presenter with an object.
|
19
|
+
# @param object Can be an collection or instance of a presentable models.
|
20
|
+
# @raise [DelegatedPresenter::Error::NotPresentable] if the object is not instance or collection
|
21
|
+
# of a presentable model.
|
7
22
|
def initialize(object)
|
8
|
-
|
9
|
-
|
10
|
-
|
23
|
+
if object.is_a?(Array)
|
24
|
+
map_array(object)
|
25
|
+
else
|
26
|
+
raise DelegatedPresenter::Error::NotPresentable, "#{self.presenter_class} cannot present a #{object.class}" unless object_is_presentable?(object)
|
27
|
+
__setobj__(object)
|
28
|
+
if exposed_methods
|
29
|
+
expose_methods
|
30
|
+
elsif hidden_methods
|
31
|
+
hide_methods
|
32
|
+
end
|
33
|
+
end
|
11
34
|
end
|
12
35
|
|
36
|
+
# The class of the presenter.
|
13
37
|
alias_method :presenter_class, :class
|
14
38
|
|
39
|
+
# The class of the presented object.
|
15
40
|
def class
|
16
|
-
|
41
|
+
presented_model.class
|
17
42
|
end
|
18
43
|
|
19
|
-
|
20
|
-
(@@presentable[self.presenter_class.name] || [])
|
21
|
-
end
|
44
|
+
private
|
22
45
|
|
46
|
+
# Determine what objects what to present.
|
47
|
+
# @!visibility public
|
48
|
+
# @param :objects One or more models to be presented.
|
49
|
+
# @example Add some presentable objects:
|
50
|
+
# presents :contact, :user, :client
|
23
51
|
def self.presents(*objects)
|
24
|
-
|
25
|
-
|
52
|
+
PRESENTABLE[name] ||= []
|
53
|
+
PRESENTABLE[name] += objects.flatten.collect{ |i| i.to_s.camelize.singularize }
|
54
|
+
end
|
55
|
+
|
56
|
+
# @!visibility public
|
57
|
+
# Hide methods from the presenter.
|
58
|
+
# @param methods one ore more methods from the model to hide from the presenter.
|
59
|
+
# @example Hide methods:
|
60
|
+
# hide :id, :crypted_password, :password_salt
|
61
|
+
def self.hide(*hidden_methods)
|
62
|
+
self.hidden_methods[name] ||= []
|
63
|
+
self.hidden_methods[name] += hidden_methods
|
64
|
+
end
|
65
|
+
|
66
|
+
# @!visibility public
|
67
|
+
# Exposes methods to the presenter, when defined all others will be hidden.
|
68
|
+
# @param :methods one ore more methods from the model to expose from the presenter.
|
69
|
+
# @example Expose methods:
|
70
|
+
# expose :first_name, :last_name
|
71
|
+
def self.expose(*exposed_methods)
|
72
|
+
EXPOSED_METHODS[name] ||= []
|
73
|
+
EXPOSED_METHODS[name] += exposed_methods.flatten
|
26
74
|
end
|
27
75
|
|
76
|
+
## Internal Methods: Not Documented
|
77
|
+
|
78
|
+
# @api private
|
28
79
|
def self.inherited(subclass)
|
29
80
|
presentable_class = subclass.name.gsub(/Presenter$/,'')
|
30
81
|
presentable_class.constantize rescue nil
|
31
82
|
subclass.presents presentable_class if Object.const_defined?(presentable_class)
|
32
83
|
end
|
33
84
|
|
85
|
+
## Inherited Object Getters
|
86
|
+
|
87
|
+
# Presentable Objects
|
88
|
+
# @api private
|
89
|
+
def self.presentable_objects
|
90
|
+
(PRESENTABLE[name] || [])
|
91
|
+
end
|
92
|
+
|
93
|
+
# Methods to expose
|
94
|
+
# @api private
|
95
|
+
def self.exposed_methods
|
96
|
+
EXPOSED_METHODS[name]
|
97
|
+
end
|
98
|
+
|
99
|
+
# Methods to hide
|
100
|
+
# @api private
|
101
|
+
def self.hidden_methods
|
102
|
+
HIDDEN_METHODS[name]
|
103
|
+
end
|
104
|
+
|
105
|
+
# Raises an error when hidden methods are called.
|
106
|
+
# @api private
|
107
|
+
def hide_methods
|
108
|
+
hidden_methods.flatten.each do |i|
|
109
|
+
singleton_class.send :define_method, i do |*args|
|
110
|
+
raise DelegatedPresenter::Error::MethodHidden, "Method `#{i} is hidden."
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
# Hides all methods except the ones exposed
|
116
|
+
# @api private
|
117
|
+
def expose_methods
|
118
|
+
(unique_model_methods - exposed_methods).flatten.each do |i|
|
119
|
+
singleton_class.send :define_method, i do |*args|
|
120
|
+
raise DelegatedPresenter::Error::MethodNotExposed, "Method `#{i} is not exposed."
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Determines if an object is presentable
|
126
|
+
# @api private
|
127
|
+
def object_is_presentable?(object)
|
128
|
+
presentable_objects.include?(object.class.name)
|
129
|
+
end
|
130
|
+
|
131
|
+
# Extracts the the models unique methods from the superclass
|
132
|
+
# @api private
|
133
|
+
def unique_model_methods
|
134
|
+
presented_model.methods - presented_model.class.superclass.instance_methods
|
135
|
+
end
|
136
|
+
|
137
|
+
# Maps an array of instances to delegated presented instances
|
138
|
+
# @api private
|
139
|
+
def map_array(array)
|
140
|
+
array.map!{ |object| presenter_class.new(object) }
|
141
|
+
inherited_methods = presenter_class.instance_methods - DelegatedPresenter::Base.instance_methods
|
142
|
+
inherited_methods.each { |i| singleton_class.send(:undef_method, i) }
|
143
|
+
__setobj__ array
|
144
|
+
end
|
145
|
+
|
34
146
|
end
|
@@ -1,4 +1,15 @@
|
|
1
1
|
module DelegatedPresenter::Error
|
2
|
+
|
3
|
+
# Error raised when an object is not presentable.
|
2
4
|
class NotPresentable < StandardError
|
3
5
|
end
|
6
|
+
|
7
|
+
# Error raised when a method is not exposed.
|
8
|
+
class MethodNotExposed < StandardError
|
9
|
+
end
|
10
|
+
|
11
|
+
# Error raised when a method is not exposed.
|
12
|
+
class MethodHidden < StandardError
|
13
|
+
end
|
14
|
+
|
4
15
|
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module DelegatedPresenter::PresentsBeforeRendering
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
class_attribute :presents_before_rendering
|
6
|
+
self.presents_before_rendering = []
|
7
|
+
end
|
8
|
+
|
9
|
+
private
|
10
|
+
|
11
|
+
# Presents specified instance variables before rendering.
|
12
|
+
def render
|
13
|
+
presents_before_rendering.each do |var|
|
14
|
+
ivar = instance_variable_get("@#{var}")
|
15
|
+
if ivar.present?
|
16
|
+
object_class = [ivar].flatten.collect(&:class).first.to_s
|
17
|
+
instance_variable_set("@#{var}", (object_class + 'Presenter').constantize.new(ivar))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
super
|
22
|
+
end
|
23
|
+
|
24
|
+
module ClassMethods
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
# @!visibility public
|
29
|
+
# Sets up a presenter for instance variables. By default it will try to determine the presenter but this can be overridden via the "*with*" option.
|
30
|
+
# @overload presents(instance_var1, instance_var2, [...])
|
31
|
+
# Specifies which instance variables to present, assumes the presenter has a name of *InstanceClassPresenter*.
|
32
|
+
# @param instance_vars the instance variables to present.
|
33
|
+
# @overload presents(instance_var1, instance_var2, [...], options)
|
34
|
+
# Specifies which instance variables to present, assumes the presenter has a name of *InstanceClassPresenter*.
|
35
|
+
# @param instance_vars the instance variables to present.
|
36
|
+
# @option options [Symbol] :with The presenter to use.
|
37
|
+
def presents(*instance_vars)
|
38
|
+
options = instance_vars.extract_options!
|
39
|
+
self.presents_before_rendering += instance_vars.flatten.map(&:to_s)
|
40
|
+
end
|
41
|
+
|
42
|
+
alias :present :presents
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
@@ -4,4 +4,10 @@ class DelegatedPresenter::Railtie < Rails::Railtie
|
|
4
4
|
require "generators/delegated_presenter_generator"
|
5
5
|
end
|
6
6
|
|
7
|
+
initializer "Add presents before rendering to controller" do
|
8
|
+
ActiveSupport.on_load :action_controller do
|
9
|
+
ActionController::Base.send :include, DelegatedPresenter::PresentsBeforeRendering
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
7
13
|
end
|
data/lib/delegated_presenter.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
class <%= class_name %>Presenter < DelegatedPresenter::Base
|
2
2
|
|
3
|
-
# By default this presenter
|
3
|
+
# By default this presenter will try and present a `<%= class_name %>` if it exists.
|
4
4
|
# You can explicitly tell the presenter to present other models using the following syntax:
|
5
5
|
|
6
6
|
# presents MyModel1, MyModel2
|
data/spec/dummy/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|