presenter-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []