presenter-rails 0.1.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5ef5a2774a142651a244f17d207b850cf66d330b
4
+ data.tar.gz: b5c11f5d72a1f2a6845724ed1d6dbf9b1dea1b08
5
+ SHA512:
6
+ metadata.gz: 11bf5be25532c008fe7affe61c637ce32a3a24ef5c41149bcc8880e8833d44d96a7cf6ebfd0e6fe7147e87689ea54845d7e38a75f25ff010055dc66090b4d37a
7
+ data.tar.gz: 1586385a58f932d1adf22d1d97fcc8d18e5ab949d225b193bd2923bd525ee5d09525dd51b3004be2f6a7e2c364e686e9a2b1ca7e20f7202d5ab4cda6a5c9711c
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Sam Sargent
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.
@@ -0,0 +1,166 @@
1
+ # presenter-rails
2
+
3
+ Presenter is an easy way of implementing the presenter design pattern in Rails.
4
+
5
+ With a naming convention based approach, easily define presenter classes to clean up
6
+ your models and views, handling method delegation based on instance variable & file naming.
7
+
8
+ This should hopefully mean you can just use presenter objects in place of model objects
9
+ throughout your app without effecting any behaviour - as you can still call your custom model
10
+ methods and active record methods on the presenter objects.
11
+
12
+ This ultimately means all methods that belong to the model stay on the model itself
13
+ and seperated from any methods which don't handle data.
14
+
15
+ All presenter classes inherit from ApplicationPresenter generated from the install to keep the same
16
+ convention you see throughout the rest of your Rails app.
17
+
18
+ ## Installation
19
+ Add this line to your application's Gemfile and bundle:
20
+ ```ruby
21
+ gem 'presenter-rails'
22
+ ```
23
+ ```bash
24
+ $ bundle
25
+ ```
26
+ Run the install generator
27
+ ```bash
28
+ $ rails g presenter:install
29
+ ```
30
+ Add this line to the top of your application.rb
31
+ ```ruby
32
+ # config/application.rb
33
+ require 'presenter'
34
+ ```
35
+ Followed by this line inside the Application class
36
+ ```ruby
37
+ # config/application.rb
38
+ module YourAppName
39
+ class Application < Rails::Application
40
+ config.autoload_paths << Rails.root.join('app/presenters')
41
+ end
42
+ end
43
+ ```
44
+
45
+ ## Usage
46
+
47
+ These instructions will use a 'User' model for example purposes. Just switch out User for your own model names
48
+
49
+ After you run the install generator, run
50
+ ```bash
51
+ $ rails g presenter User
52
+ ```
53
+
54
+ to create a presenter file for one of your models. This will inherit from ApplicationPresenter, example shown below:
55
+ ```ruby
56
+ # app/presenters/user_presenter.rb
57
+ class UserPresenter < ApplicationPresenter
58
+ end
59
+ ```
60
+
61
+ Note: This automatically inherits a dynamic initialize method which sets an instance variable. You also inherit a helper method dynamically named after the model too.
62
+ So you DO NOT have to define this yourself:
63
+ ```ruby
64
+ def initialize(user)
65
+ @user = user
66
+ end
67
+
68
+ def user # Make sure you use the helper methods in your presenters in place of the instance variable.
69
+ @user
70
+ end
71
+ ```
72
+
73
+ This also comes with with some error handling and some other code. So don't worry about this.
74
+ I'm showing it just so you know what to expect if you try overwrite initialize or the helper without super. If you wish to extend it
75
+ then remember to super or you will overwrite the default behaviour.
76
+
77
+ If you do wish to extend the initialize method / change the instance variable name, call super first. Next, define an instance variable and assign it to the model object using the helper method (named after the model name).
78
+ ```ruby
79
+ def initialize(user)
80
+ super
81
+ # assigning your variable to the user method gives it access to the inherited code.
82
+ @another_variable_name = self.user
83
+ end
84
+ ```
85
+
86
+ This means you can define methods in your presenter using the instance variable named after your model.
87
+ Let's imagine our User model has a first_name and last_name field and we want to define #name. All we would have to do is:
88
+ ```ruby
89
+ class UserPresenter < ApplicationPresenter
90
+ def name
91
+ "#{user.first_name} #{user.last_name}"
92
+ end
93
+ end
94
+ ```
95
+
96
+ now you can initialize a presenter. There are 2 methods for this, either directly initialize the object as usual with:
97
+ ```ruby
98
+ @user = UserPresenter.new(user)
99
+ ```
100
+ or use our built in helper to accomplish the same thing:
101
+ ```ruby
102
+ @user = present(user)
103
+ ```
104
+ (More on this below)
105
+
106
+ Example provided shows how it would be used in a controller:
107
+ ```ruby
108
+ class UsersController < ApplicationController
109
+ def show
110
+ user = User.find(params[:id]) # get an instance of user
111
+ @user = present(user)
112
+ end
113
+ end
114
+ ```
115
+
116
+ Now the user instance variable has access to all methods belonging to it's presenter and the model being passed in.
117
+ This allows access to
118
+ ```ruby
119
+ @user.name
120
+ # which returns first name and last name like 'Sam Sargent'
121
+ # or even call the normal model methods
122
+ @user.first_name
123
+ # which would delegate the method back to the user model being passed into the present helper
124
+ # returning something like 'Sam'
125
+ # Note: This works with all ActiveRecord methods too, just as if it's an object of the ActiveRecord class User
126
+ # allowing access to even @user.update(first_name: 'Finn') on the presenter object
127
+ ```
128
+
129
+ ## Helper methods
130
+
131
+ ### #present
132
+
133
+ #present is a method accessible in controllers and views for initializing new presenter objects
134
+ ```ruby
135
+ user = User.first
136
+ @user = present(user) # returns a presenter object for that model, replacing need to initialize with #new
137
+
138
+ # instead of
139
+ user = User.first
140
+ @user = UserPresenter.new(user)
141
+
142
+ # alternatively, pass in a collection to return an array of presenter objects
143
+ users = User.all # an active record relation
144
+ @users = present(users)
145
+ users = User.all.to_a # an array
146
+ @users = present(users)
147
+ ```
148
+
149
+ ### #instance getter
150
+
151
+ For each presenter you create, a helper method is defined to get the model object being passed in. This replaces the need to throw around
152
+ the instance variable. It's also faster as it allows the method delegation to not have to parse naming and fetch instance variables every time the inherited #method_missing is called.
153
+
154
+ shown using a User model as example
155
+ ```ruby
156
+ # app/presenters/user_presenter.rb
157
+ class UserPresenter < ApplicationPresenter
158
+ # this has the method #user defined automatically to access the user object being passed in to the presenter
159
+ def name
160
+ "#{user.first_name} {user.last_name}"
161
+ end
162
+ end
163
+ ```
164
+
165
+ ## License
166
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,23 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Presenter'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+
18
+
19
+
20
+
21
+
22
+ require 'bundler/gem_tasks'
23
+
@@ -0,0 +1,8 @@
1
+ Description:
2
+ Creates a presenter based on a model name for you
3
+
4
+ Example:
5
+ rails g presenter user
6
+
7
+ This will create:
8
+ app/presenters/user_presenter.rb
@@ -0,0 +1,9 @@
1
+ Description:
2
+ Creates the presenters folder & application_presenter.rb file for you
3
+
4
+ Example:
5
+ rails g presenter:install
6
+
7
+ This will create:
8
+ app/presenters
9
+ app/presenters/application_presenter.rb
@@ -0,0 +1,12 @@
1
+ module Presenter
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+ class_option :doc, type: :boolean, default: true, desc: 'Include commented documentation'
6
+
7
+ def generate_application_presenter
8
+ template 'application_presenter.template', 'app/presenters/application_presenter.rb'
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,13 @@
1
+ class ApplicationPresenter < Presenter::Base
2
+ <%- if options.doc? -%>
3
+ # You'll notice Presenter::Base, this handles delegation of methods based on naming conventions.
4
+ # Along with defining the #initialize method & the model helper method for all presenters.
5
+
6
+ # Use rails g presenter ModelName to create a presenter file for a model.
7
+
8
+ # All presenter classes inherit from this ApplicationPresenter and so define
9
+ # methods here which you want all your presenters to inherit (just as you usually would).
10
+
11
+ # For more, check the docs at: https://www.github.com/samsarge/presenter-rails
12
+ <%- end -%>
13
+ end
@@ -0,0 +1,11 @@
1
+ module Presenter
2
+ class PresenterGenerator < Rails::Generators::Base
3
+ source_root File.expand_path('../templates', __FILE__)
4
+ class_option :doc, type: :boolean, default: true, desc: 'Include commented documentation'
5
+ argument :model, type: :string
6
+
7
+ def generate_model_presenter
8
+ template 'model_presenter.template', "app/presenters/#{model.singularize.underscore}_presenter.rb"
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ class <%= "#{model.singularize.camelcase}Presenter" %> < ApplicationPresenter
2
+ <%- if options.doc? -%>
3
+ # Don't worry about the initialize method, it's already handled for you.
4
+
5
+ # You can now pass in an instance of <%= model.singularize.camelcase %> when you initialize this presenter
6
+ # and access it with the helper method
7
+ # #<%= model.singularize.underscore %>
8
+ # instead of an instance variable when writing your methods
9
+
10
+ # Write methods for this presenter in here using the helper method
11
+ <%- end -%>
12
+ end
@@ -0,0 +1,5 @@
1
+ require 'presenter/naming'
2
+ require 'presenter/base'
3
+ require 'presenter/helper'
4
+ require 'presenter/controller'
5
+ require 'presenter/version'
@@ -0,0 +1,25 @@
1
+ module Presenter
2
+ class Base
3
+ include Presenter::Naming
4
+
5
+ def initialize(model)
6
+ @model = model
7
+ generate_model_instance_getter
8
+ end
9
+
10
+ protected
11
+ def generate_model_instance_getter
12
+ define_singleton_method model_object_name_from_presenter(@model) do
13
+ @model
14
+ end
15
+ end
16
+
17
+ def method_missing(method, *args, &block)
18
+ @model.send(method, *args, &block)
19
+ end
20
+
21
+ def respond_to_missing?(method, include_all = false)
22
+ @model.respond_to?(method, include_all)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,3 @@
1
+ ActionController::Base.class_eval do
2
+ include Presenter::Helper
3
+ end
@@ -0,0 +1,12 @@
1
+ module Presenter
2
+ module Helper
3
+ include Presenter::Naming
4
+ def present(object_or_collection)
5
+ if object_or_collection.respond_to?(:map) # If it is a collection
6
+ object_or_collection.map { |object| presenter_from_model_object(object).new(object) }
7
+ else # If it is a single object
8
+ presenter_from_model_object(object_or_collection).new(object_or_collection)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ # This module is for naming related methods
2
+ module Presenter
3
+ module Naming
4
+ def model_object_name_from_presenter(model)
5
+ model.class.to_s.underscore.sub('_presenter', '')
6
+ end
7
+
8
+ def presenter_from_model_object(model)
9
+ presenter_name(model).constantize
10
+ end
11
+
12
+ private
13
+ def presenter_name(model)
14
+ "#{model.class.to_s}Presenter"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Presenter
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :presenter_rails do
3
+ # # Task goes here
4
+ # end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: presenter-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Sam Sargent
8
+ - Finn Francis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2017-07-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: 5.0.2
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: 5.0.2
28
+ - !ruby/object:Gem::Dependency
29
+ name: sqlite3
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec-rails
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ description: Keep code neat and abstract view & presenter logic. Adding a simple separation
57
+ of concerns when creating your models with an easy API.
58
+ email:
59
+ - samsarge@hotmail.co.uk
60
+ - finnfrancis123@gmail.com
61
+ executables: []
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - MIT-LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - lib/generators/presenter/USAGE
69
+ - lib/generators/presenter/install/USAGE
70
+ - lib/generators/presenter/install/install_generator.rb
71
+ - lib/generators/presenter/install/templates/application_presenter.template
72
+ - lib/generators/presenter/presenter_generator.rb
73
+ - lib/generators/presenter/templates/model_presenter.template
74
+ - lib/presenter.rb
75
+ - lib/presenter/base.rb
76
+ - lib/presenter/controller.rb
77
+ - lib/presenter/helper.rb
78
+ - lib/presenter/naming.rb
79
+ - lib/presenter/version.rb
80
+ - lib/tasks/presenter/rails_tasks.rake
81
+ homepage: https://github.com/samsarge/presenter-rails
82
+ licenses:
83
+ - MIT
84
+ metadata: {}
85
+ post_install_message:
86
+ rdoc_options: []
87
+ require_paths:
88
+ - lib
89
+ required_ruby_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ required_rubygems_version: !ruby/object:Gem::Requirement
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ requirements: []
100
+ rubyforge_project:
101
+ rubygems_version: 2.4.5.1
102
+ signing_key:
103
+ specification_version: 4
104
+ summary: Easily implement the presenter design pattern in rails projects.
105
+ test_files: []