detaso-oprah 0.3.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 1ee902d46e66b0a39b778241c9d18a8202dab24713ffaf4389b9ba3e43da831c
4
+ data.tar.gz: 8a611ae249feb8c39c849201cd463cd269b3f532de9f7fcdac8f5fe8fc296b07
5
+ SHA512:
6
+ metadata.gz: d42a4b5ca87c66396eab35e4f6a53f75078a2e6fd1b4ac48ab91161d63ec727ea3395047ca6b348b9be1d2e6194a635690a92c1df1252284db277251b31b3069
7
+ data.tar.gz: bd2947f1f69df130f0c96101a08ff3ee4a982509ad7c6285f039961a7c1a6e0977ba791552d85ff51279b9b9ac12e873f3f635d78450b2a95dff5f783e0cd72b
data/.gitignore ADDED
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ test/dummy/log/
data/.travis.yml ADDED
@@ -0,0 +1,18 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ script: bundle exec rake test
5
+ rvm:
6
+ - 2.3.1
7
+ matrix:
8
+ global:
9
+ - BUNDLE_JOBS=4
10
+ before_install:
11
+ - bundle install --retry=3
12
+ before_update:
13
+ - bundle update
14
+ notifications:
15
+ email: false
16
+ addons:
17
+ code_climate:
18
+ repo_token: 3d9c9c681c7be79695156ecbf9978ada542e60b726a7ba728866dee6f95f3ccd
data/CHANGELOG.md ADDED
@@ -0,0 +1,39 @@
1
+ 0.3.0
2
+ -----
3
+
4
+ - Presenter now inherits from SimpleDelegator
5
+ - Support for anonymous ancestors [#9]
6
+
7
+
8
+ 0.2.1
9
+ -----
10
+
11
+ - Support for ActionMailer
12
+
13
+ 0.2.0
14
+ -----
15
+
16
+ - Replace `Oprah::Cache` with `ActiveSupport::Cache::MemoryStore` [#3]
17
+
18
+ 0.1.3
19
+ -----
20
+
21
+ - Presenters can now be specified using the `only:` keyword argument, which
22
+ takes either a Class or an Array of classes
23
+ - Replace repeated method default arguments with splats
24
+ - Add assertions to TestHelper
25
+ - Delegate to most recent view context in presenters created from within Rails
26
+ controllers [#1]
27
+ - Add `#present` and `#present_many` methods to Presenter [#2]
28
+
29
+ 0.1.2
30
+ -----
31
+
32
+ - Explicitly rescue `NameError` in `Cache#presenter_classes_for`
33
+ - Add `Oprah::TestHelpers`
34
+ - Delegate `#to_s` and `#inspect` to the presented object
35
+
36
+ 0.1.1
37
+ -----
38
+
39
+ - Initial public release
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,86 @@
1
+ # Contributing
2
+
3
+ When contributing to this repository, please first discuss the change you wish
4
+ to make via issue, email, or any other method with the owners of this repository
5
+ before making a change.
6
+
7
+ Please note we have a code of conduct, please follow it in all your interactions
8
+ with the project.
9
+
10
+ ## Pull Request Process
11
+
12
+ If you'd like to contribute to Oprah, start by forking the repository on GitHub:
13
+
14
+ http://github.com/endofunky/oprah
15
+
16
+ To get all of the dependencies, install the gem first. The best way to get your
17
+ changes merged back into core is as follows:
18
+
19
+ - If you are about to add a larger feature, open an issue on the GitHub issue
20
+ tracker to discuss your ideas first. If whatever you're about to implement is
21
+ already in the issue tracker, please let us know that you picked it up.
22
+
23
+ - Clone down your fork.
24
+
25
+ - Create a thoughtfully named topic branch to contain your change.
26
+
27
+ - Write some code.
28
+
29
+ - Add tests and make sure everything still passes by running bundle exec rake.
30
+ This is mandatory for pull requests to be accepted.
31
+
32
+ - If you are adding new functionality, document it!
33
+
34
+ - Do not change the version number.
35
+
36
+ - If necessary, rebase your commits into logical chunks, without errors.
37
+
38
+ - Push the branch up to GitHub.
39
+
40
+ - Send a pull request to the endofunky/oprah project.
41
+
42
+ ## Contributor Code of Conduct
43
+
44
+ As contributors and maintainers of this project, and in the interest of
45
+ fostering an open and welcoming community, we pledge to respect all people who
46
+ contribute through reporting issues, posting feature requests, updating
47
+ documentation, submitting pull requests or patches, and other activities.
48
+
49
+ We are committed to making participation in this project a harassment-free
50
+ experience for everyone, regardless of level of experience, gender, gender
51
+ identity and expression, sexual orientation, disability, personal appearance,
52
+ body size, race, ethnicity, age, religion, or nationality.
53
+
54
+ Examples of unacceptable behavior by participants include:
55
+
56
+ - The use of sexualized language or imagery
57
+
58
+ - Personal attacks
59
+
60
+ - Trolling or insulting/derogatory comments
61
+
62
+ - Public or private harassment
63
+
64
+ - Publishing other's private information, such as physical or electronic
65
+ addresses, without explicit permission
66
+
67
+ - Other unethical or unprofessional conduct.
68
+
69
+ Project maintainers have the right and responsibility to remove, edit, or
70
+ reject comments, commits, code, wiki edits, issues, and other contributions that
71
+ are not aligned to this Code of Conduct. By adopting this Code of Conduct,
72
+ project maintainers commit themselves to fairly and consistently applying these
73
+ principles to every aspect of managing this project. Project maintainers who do
74
+ not follow or enforce the Code of Conduct may be permanently removed from the
75
+ project team.
76
+
77
+ This code of conduct applies both within project spaces and in public spaces
78
+ when an individual is representing the project or its community.
79
+
80
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
81
+ reported by opening an issue or contacting one or more of the project
82
+ maintainers.
83
+
84
+ This Code of Conduct is adapted from the
85
+ [Contributor Covenant](http://contributor-covenant.org), version 1.2.0,
86
+ available at http://contributor-covenant.org/version/1/2/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sheaf.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License
2
+
3
+ Copyright (c) 2016 Tobias Svensson <tob@tobiassvensson.co.uk>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,314 @@
1
+ # Oprah
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/oprah.svg)](https://badge.fury.io/rb/oprah)
4
+ [![Build Status](https://travis-ci.org/endofunky/oprah.svg)](https://travis-ci.org/endofunky/oprah)
5
+ [![Code Climate](https://codeclimate.com/github/endofunky/oprah.svg)](https://codeclimate.com/github/endofunky/oprah)
6
+ [![Dependency Status](https://gemnasium.com/badges/github.com/endofunky/oprah.svg)](https://gemnasium.com/github.com/endofunky/oprah)
7
+
8
+ Opinionated presenters for Rails 5 - without the cruft.
9
+
10
+ ## Table of Contents
11
+
12
+ * [Overview](#overview)
13
+ * [Installation](#installation)
14
+ * [Getting started](#getting-started)
15
+ + [ActionController integration](#actioncontroller-integration)
16
+ + [ActionMailer integration](#actionmailer-integration)
17
+ * [Collections](#collections)
18
+ * [Associations](#associations)
19
+ * [Composition](#composition)
20
+ + [Performance](#performance)
21
+ + [Ordering](#ordering)
22
+ + [Choosing presenters](#choosing-presenters)
23
+ * [Testing](#testing)
24
+ * [API Documentation](#api-documentation)
25
+ * [Contributing](#contributing)
26
+ * [License](#license)
27
+ * [Author](#author)
28
+
29
+ ## Overview
30
+
31
+ If you've ever worked on a sufficiently large Rails application you've probably
32
+ experienced the Rails helper mess first hand. Helper methods are annoying to
33
+ locate, hard to test and not terribly expressive.
34
+
35
+ So why another presenter/decorator library? Oprah was written with a few simple
36
+ goals in mind only covered partially (or not at all) by other gems:
37
+
38
+ - Thin, lightweight layer over Ruby's `SimpleDelegator`
39
+ - Presenters should be easy to test
40
+ - Avoid monkey patching, where possible :monkey::gun:
41
+ - Embrace convention over configuration
42
+ - First-class support for composition (modules and concerns)
43
+
44
+ ## Installation
45
+
46
+ Add this line to your application's Gemfile:
47
+
48
+ ``` ruby
49
+ gem 'oprah'
50
+ ```
51
+
52
+ And then execute:
53
+
54
+ ```
55
+ $ bundle
56
+ ```
57
+
58
+ ## Getting started
59
+
60
+ Oprah expects a single presenter for each of your classes or modules. If your
61
+ model is called `User` it will look for a class called `UserPresenter`:
62
+
63
+ ``` ruby
64
+ class User
65
+ def first_name
66
+ "John"
67
+ end
68
+
69
+ def last_name
70
+ "Doe"
71
+ end
72
+ end
73
+
74
+ class UserPresenter < Oprah::Presenter
75
+ def name
76
+ "#{first_name} #{last_name}"
77
+ end
78
+ end
79
+ ```
80
+
81
+ Oprah will figure out the presenters by itself so you don't have to instantiate
82
+ your presenter classes directly:
83
+
84
+ ``` ruby
85
+ presenter = Oprah.present(User.new)
86
+
87
+ presenter.name
88
+ # => "John Doe"
89
+
90
+ ```
91
+
92
+ Of course, all the regular methods on your model are still accessible:
93
+
94
+ ``` ruby
95
+ presenter.first_name
96
+ # => "John"
97
+ ```
98
+
99
+ If you *DO* want to use a specific presenter, you can simply instantiate it
100
+ yourself:
101
+
102
+ ``` ruby
103
+ SomeOtherPresenter.new(User.new)
104
+ ```
105
+
106
+ ### ActionController integration
107
+
108
+ Now, where do we put our presenters? Ideally, you'd want to expose them in your
109
+ controller. Oprah avoids monkey patching and generally it's good to be aware of
110
+ what's going on, even if that means to be (at least a little bit) explicit.
111
+
112
+ Here's how you can use Oprah presenters from your controller:
113
+
114
+ ``` ruby
115
+ class UsersController < ApplicationController
116
+ def show
117
+ @user = present User.find(params[:id])
118
+ end
119
+ end
120
+ ```
121
+
122
+ This will also take care of passing the correct view context to the presenter,
123
+ which you can access with the `#view_context` (or shorter, `#h`) instance
124
+ method.
125
+
126
+ ### ActionMailer integration
127
+
128
+ Oprah will make the same helpers you have in ActionController available to
129
+ ActionMailer:
130
+
131
+ ``` ruby
132
+ class UserMailer < ApplicationMailer
133
+ default from: 'notifications@example.com'
134
+
135
+ def welcome_email(user)
136
+ @user = present user
137
+ mail(to: @user.email, subject: 'Welcome to My Awesome Site')
138
+ end
139
+ end
140
+ ```
141
+
142
+ ## Collections
143
+
144
+ Oprah has basic support for collections with `.present_many`. It will simply
145
+ apply it's `.present` behavior to each object in the given collection:
146
+
147
+ ``` ruby
148
+ users = [User.new, User.new]
149
+ presenters = Oprah.present_many(users)
150
+
151
+ presenters.first.kind_of?(UserPresenter)
152
+ # => true
153
+
154
+ presenters.last.kind_of?(UserPresenter)
155
+ # => true
156
+ ```
157
+
158
+ Of course, this works in controllers, too:
159
+
160
+ ``` ruby
161
+ class UserController < ApplicationController
162
+ def index
163
+ @users = present_many User.all
164
+ end
165
+ end
166
+ ```
167
+
168
+ ## Associations
169
+
170
+ You can also automatically use presenters for your associations using the
171
+ `#presents_one` and `#presents_many` macros. Let's say you have the following
172
+ `Project` model:
173
+
174
+ ``` ruby
175
+ class Project
176
+ has_many :users
177
+ has_one :owner, class_name: "User"
178
+ end
179
+ ```
180
+
181
+ Oprah lets you easily wrap the associated objects:
182
+
183
+ ``` ruby
184
+ class ProjectPresenter < Oprah::Presenter
185
+ presents_many :users
186
+ presents_one :owner
187
+ end
188
+ ```
189
+
190
+ Note that you don't need to explicitly state the association class.
191
+
192
+ ## Composition
193
+
194
+ Let's say you extraced some behaviour out of your model into a reusable module (or
195
+ `ActiveSupport::Concern`). Oprah lets you write a single, separate presenter for
196
+ this module and automatically chains it to your "main presenter" by walking up the
197
+ ancestor chain of the given object.
198
+
199
+ Let's say we want to mix a shared `Describable` module into our `User` class from
200
+ above and render the description to HTML:
201
+
202
+
203
+ ``` ruby
204
+ module Describable
205
+ def description
206
+ "*AWESOME*"
207
+ end
208
+ end
209
+
210
+ class User
211
+ include Describable
212
+ end
213
+
214
+ class DescribablePresenter < Oprah::Presenter
215
+ def description
216
+ Kramdown::Document.new(object.description).to_html
217
+ end
218
+ end
219
+ ```
220
+
221
+ You can now access the methods of both, `UserPresenter` *and*
222
+ `DescribablePresenter`:
223
+
224
+ ``` ruby
225
+ presenter = Oprah.present(User.new)
226
+
227
+ presenter.description
228
+ => "<p><em>AWESOME</em></p>\n"
229
+
230
+ presenter.name
231
+ # => John Doe
232
+ ```
233
+
234
+ ### Performance
235
+
236
+ Of course, looking up all the presenters would imply a performance issue. But
237
+ don't worry, Oprah caches all matching presenters for a class (and busts it's
238
+ cache on code reloads for a smooth development experience).
239
+
240
+ ### Ordering
241
+
242
+ Oprah walks your object's ancestor chain in reverse. For example, you'd be
243
+ able to access the methods exposed by the `DescribablePresenter` from your
244
+ `UserPresenter`. You can even use `super`:
245
+
246
+ ``` ruby
247
+ class DescribablePresenter < Oprah::Presenter
248
+ def baz
249
+ "foo"
250
+ end
251
+ end
252
+
253
+ class UserPresenter < Oprah::Presenter
254
+ def baz
255
+ super + "bar"
256
+ end
257
+ end
258
+
259
+ Oprah.present(User.new).baz
260
+ # => "foobar"
261
+ ```
262
+
263
+ ### Choosing presenters
264
+
265
+ When presenting an object you can optionally choose which presenter classes
266
+ to use:
267
+
268
+ ``` ruby
269
+ Oprah.present(User.new, only: DescribablePresenter)
270
+ ```
271
+
272
+ This parameter takes either a single presenter or an `Array` of presenters.
273
+ The presenter(s) given need to match the object's class or one of it's
274
+ ancestors. Non-matching presenters given will be ignored.
275
+
276
+ ## Testing
277
+
278
+ Testing presenters is as simple as testing a regular class. Oprah also
279
+ provides couple of helpers to make it even easier:
280
+
281
+ ``` ruby
282
+ class UserPresenterTest < Minitest::Test
283
+ include Oprah::TestHelpers
284
+
285
+ def setup
286
+ @presenter = present User.new
287
+ end
288
+
289
+ def test_presented
290
+ assert_presented @presenter
291
+ end
292
+
293
+ def test_name
294
+ assert_equal "John Doe", @presenter.name
295
+ end
296
+ end
297
+ ```
298
+
299
+ ## API Documentation
300
+
301
+ Comprehensive API Documentation is available at
302
+ [rubydoc.info](http://www.rubydoc.info/gems/oprah).
303
+
304
+ ## Contributing
305
+
306
+ Please check out our [contributing guidelines](CONTRIBUTING.md).
307
+
308
+ ## License
309
+
310
+ Released under the MIT license. See the LICENSE file for details.
311
+
312
+ ## Author
313
+
314
+ Tobias Svensson, [@endofunky](https://twitter.com/endofunky), [http://github.com/endofunky](http://github.com/endofunky)
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ require File.join(Dir.pwd, 'lib', 'oprah', 'version')
2
+
3
+ Dir["tasks/**/*.rb"].each { |task| load task }
4
+
5
+ task default: :test
@@ -0,0 +1,109 @@
1
+ module Oprah
2
+ # Helpers that will be mixed into `ActionController::Base` and
3
+ # `ActionMailer::Base` by the {Oprah::Railtie}.
4
+ module ControllerHelpers
5
+ # A proxy class to delegate method calls to view contexts in presenters
6
+ # to the most recently created view context by
7
+ # {ControllerHelpers#view_context}.
8
+ #
9
+ # `ViewContextProxy` objects are automatically created in
10
+ # {ControllerHelpers#present} and {ControllerHelpers#present_many} and
11
+ # shouldn't have to be created manually.
12
+ #
13
+ # @since 0.1.3
14
+ class ViewContextProxy < ActiveSupport::ProxyObject
15
+ # @param [ActionController::Base] controller
16
+ # The controller to delegate to.
17
+ def initialize(controller)
18
+ @controller = controller
19
+ end
20
+
21
+ # Delegates all method calls to the `ActionView::Base` returned from
22
+ # {ControllerHelpers#oprah_view_context}.
23
+ def method_missing(meth, *args, &block)
24
+ @controller.oprah_view_context.__send__(meth, *args, &block)
25
+ end
26
+ end
27
+
28
+ extend ActiveSupport::Concern
29
+
30
+ included do
31
+ helper_method :present
32
+ helper_method :present_many
33
+ end
34
+
35
+ # Presents a single object.
36
+ #
37
+ # Will pass the view context returned from {#oprah_view_context} to the
38
+ # presenter by default. This can be overridden.
39
+ #
40
+ # @see Presenter.present
41
+ def present(*args, **kwargs, &block)
42
+ kwargs = {
43
+ view_context: oprah_view_context_proxy
44
+ }.merge(kwargs)
45
+
46
+ Presenter.present(*args, **kwargs, &block)
47
+ end
48
+
49
+ # Presents a collection of objects.
50
+ #
51
+ # Will pass the view context returned from {#oprah_view_context} to the
52
+ # presenter by default. This can be overridden.
53
+ #
54
+ # @see Presenter.present_many
55
+ def present_many(*args, **kwargs, &block)
56
+ kwargs = {
57
+ view_context: oprah_view_context_proxy
58
+ }.merge(kwargs)
59
+
60
+ Presenter.present_many(*args, **kwargs, &block)
61
+ end
62
+
63
+ # The view context automatically passed to objects presented from this
64
+ # controller.
65
+ #
66
+ # You can override this method pass a custom view context to all
67
+ # presented objects from the controller scope.
68
+ #
69
+ # @see #oprah_view_context=
70
+ # @return [ActionView::Base]
71
+ def oprah_view_context
72
+ @oprah_view_context || view_context
73
+ end
74
+
75
+ # Assigns the view context returned from {#oprah_view_context}.
76
+ #
77
+ # You can override this method pass a custom view context to all
78
+ # presented objects from the controller scope.
79
+ #
80
+ # @since 0.1.3
81
+ # @see #oprah_view_context
82
+ # @param [ActionView::Base] view_context The view context to assign
83
+ # @return [ActionView::Base]
84
+ def oprah_view_context=(view_context)
85
+ @oprah_view_context = view_context
86
+ end
87
+
88
+ # Returns an instance of a view class and sets the current view context
89
+ # returned by {#oprah_view_context}.
90
+ #
91
+ # If you override this method in your controller ensure you keep Oprah's
92
+ # view context updated using {#oprah_view_context=}.
93
+ #
94
+ # @since 0.1.3
95
+ # @see http://api.rubyonrails.org/classes/ActionView/Rendering.html#method-i-view_context
96
+ # Rails API Documentation
97
+ # @return [ActionView::Base]
98
+ def view_context
99
+ self.oprah_view_context = super
100
+ end
101
+
102
+ private
103
+
104
+ # @since 0.1.3
105
+ def oprah_view_context_proxy
106
+ @oprah_view_context_proxy ||= ViewContextProxy.new(self)
107
+ end
108
+ end
109
+ end