oprah 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9338c79bdb8b1d23b379817b46c5c2ed1dbf07d1
4
+ data.tar.gz: 5af6034580ada342dfe5cd69e02f94dbb6deb622
5
+ SHA512:
6
+ metadata.gz: 2810722883f201f4cea966612128a01f5173ea14c64fb5fd1a30982f944377af6494e5de264198c63d22b7d4d91d9c900d67f329d2bc7e7596c754082da31a3b
7
+ data.tar.gz: c7d1053a0023d28d258d9b397aac887b1f1718002824c7b8d4d6c742d6148317c2434fc758b54e602455cbf4438ab8aa29343d807ae501c9c256de00a3ab2f12
data/.gitignore ADDED
@@ -0,0 +1,14 @@
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
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ sudo: false
3
+ cache: bundler
4
+ script: 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
data/CHANGELOG.md ADDED
@@ -0,0 +1,4 @@
1
+ 0.1.1
2
+ -----
3
+
4
+ - 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,243 @@
1
+ # Oprah
2
+
3
+ Opinionated presenters for Rails 5 - without the cruft.
4
+
5
+ ## Table of Contents
6
+
7
+ * [Overview](#overview)
8
+ * [Installation](#installation)
9
+ * [Getting started](#getting-started)
10
+ + [ActionController integration](#actioncontroller-integration)
11
+ * [Collections](#collections)
12
+ * [Associations](#associations)
13
+ * [Composition](#composition)
14
+ + [Performance](#performance)
15
+ + [Ordering](#ordering)
16
+ * [License](#license)
17
+ * [Author](#author)
18
+
19
+ ## Overview
20
+
21
+ If you've ever worked on a sufficiently large Rails application you've probably
22
+ experienced the Rails helper mess first hand. Helper methods are annoying to
23
+ locate, hard to test and not terribly expressive.
24
+
25
+ So why another presenter/decorator library? Oprah was written with a few simple
26
+ goals in mind only covered partially (or not at all) by other gems:
27
+
28
+ - Lightweight
29
+ - Presenters should be easy to test
30
+ - No monkey patching :monkey::gun:
31
+ - Embrace convention over configuration
32
+ - First-class support for composition (modules and concerns)
33
+
34
+ ## Installation
35
+
36
+ Add this line to your application's Gemfile:
37
+
38
+ ``` ruby
39
+ gem 'oprah'
40
+ ```
41
+
42
+ And then execute:
43
+
44
+ ```
45
+ $ bundle
46
+ ```
47
+
48
+ ## Getting started
49
+
50
+ Oprah expects a single presenter for each of your classes or modules. If your
51
+ model is called `User` it will look for a class called `UserPresenter`:
52
+
53
+ ``` ruby
54
+ class User
55
+ def first_name
56
+ "John"
57
+ end
58
+
59
+ def last_name
60
+ "Doe"
61
+ end
62
+ end
63
+
64
+ class UserPresenter < Oprah::Presenter
65
+ def name
66
+ "#{first_name} #{last_name}"
67
+ end
68
+ end
69
+ ```
70
+
71
+ Oprah will figure out the presenters by itself so you don't have to instantiate
72
+ your presenter classes directly:
73
+
74
+ ``` ruby
75
+ presenter = Oprah.present(User.new)
76
+
77
+ presenter.name
78
+ # => "John Doe"
79
+
80
+ ```
81
+
82
+ Of course, all the regular methods on your model are still accessible:
83
+
84
+ ``` ruby
85
+ presenter.first_name
86
+ # => "John"
87
+ ```
88
+
89
+ If you *DO* want to use a specific presenter, you can simply instantiate it
90
+ yourself:
91
+
92
+ ``` ruby
93
+ SomeOtherPresenter.new(User.new)
94
+ ```
95
+
96
+ ### ActionController integration
97
+
98
+ Now, where do we put our presenters? Ideally, you'd want to expose them in your
99
+ controller. Oprah avoids monkey patching and generally it's good to be aware of
100
+ what's going on, even if that means to be (at least a little bit) explicit.
101
+
102
+ Here's how you can use Oprah presenters from your controller:
103
+
104
+ ``` ruby
105
+ class UserController < ApplicationController
106
+ def show
107
+ @user = present User.find(params[:id])
108
+ end
109
+ end
110
+ ```
111
+
112
+ This will also take care of passing the correct view context to the presenter,
113
+ which you can access with the `#view_context` (or shorter, `#h`) instance
114
+ method.
115
+
116
+ ## Collections
117
+
118
+ Oprah has basic support for collections with `.present_many`. It will simply
119
+ apply it's `.present` behavior to each object in the given collection:
120
+
121
+ ``` ruby
122
+ users = [User.new, User.new]
123
+ presenters = Oprah.present_many(users)
124
+
125
+ presenters.first.kind_of?(UserPresenter)
126
+ # => true
127
+
128
+ presenters.last.kind_of?(UserPresenter)
129
+ # => true
130
+ ```
131
+
132
+ Of course, this works in controllers, too:
133
+
134
+ ``` ruby
135
+ class UserController < ApplicationController
136
+ def index
137
+ @users = present_many User.all
138
+ end
139
+ end
140
+ ```
141
+
142
+ ## Associations
143
+
144
+ You can also automatically use presenters for your associations using the
145
+ `#presents_one` and `#presents_many` macros. Let's say you have the following
146
+ `Project` model:
147
+
148
+ ``` ruby
149
+ class Project
150
+ has_many :users
151
+ has_one :owner, class_name: "User"
152
+ end
153
+ ```
154
+
155
+ Oprah lets you easily wrap the associated objects:
156
+
157
+ ``` ruby
158
+ class ProjectPresenter < Oprah::Presenter
159
+ presents_many :users
160
+ presents_one :owner
161
+ end
162
+ ```
163
+
164
+ Note that you don't need to explicitly state the association class.
165
+
166
+ ## Composition
167
+
168
+ Let's say you extraced some behaviour out of your model into a reusable module (or
169
+ `ActiveSupport::Concern`). Oprah lets you write a single, separate presenter for
170
+ this module and automatically chains it to your "main presenter" by walking up the
171
+ ancestor chain of the given object.
172
+
173
+ Let's say we want to mix a shared `Describable` module into our `User` class from
174
+ above and render the description to HTML:
175
+
176
+
177
+ ``` ruby
178
+ module Describable
179
+ def description
180
+ "*AWESOME*"
181
+ end
182
+ end
183
+
184
+ class User
185
+ include Describable
186
+ end
187
+
188
+ class DescribablePresenter < Oprah::Presenter
189
+ def description
190
+ Kramdown::Document.new(object.description).to_html
191
+ end
192
+ end
193
+ ```
194
+
195
+ You can now access the methods of both, `UserPresenter` *and*
196
+ `DescribablePresenter`:
197
+
198
+ ``` ruby
199
+ presenter = Oprah.present(User.new)
200
+
201
+ presenter.description
202
+ => "<p><em>AWESOME</em></p>\n"
203
+
204
+ presenter.name
205
+ # => John Doe
206
+ ```
207
+
208
+ ### Performance
209
+
210
+ Of course, looking up all the presenters would imply a performance issue. But
211
+ don't worry, Oprah caches all matching presenters for a class (and busts it's
212
+ cache on code reloads for a smooth development experience).
213
+
214
+ ### Ordering
215
+
216
+ Oprah walks your object's ancestor chain in reverse. For example, you'd be
217
+ able to access the methods exposed by the `DescribablePresenter` from your
218
+ `UserPresenter`. You can even use `super`:
219
+
220
+ ``` ruby
221
+ class DescribablePresenter < Oprah::Presenter
222
+ def baz
223
+ "foo"
224
+ end
225
+ end
226
+
227
+ class UserPresenter < Oprah::Presenter
228
+ def baz
229
+ super + "bar"
230
+ end
231
+ end
232
+
233
+ Oprah.present(User.new).baz
234
+ # => "foobar"
235
+ ```
236
+
237
+ ## License
238
+
239
+ Released under the MIT license. See the LICENSE file for details.
240
+
241
+ ## Author
242
+
243
+ 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,58 @@
1
+ module Oprah
2
+ # A cache store to keep Object-to-Presenter mappings. This class is
3
+ # thread-safe.
4
+ class Cache
5
+ def initialize
6
+ @mutex = Mutex.new
7
+ @mapping = {}
8
+ end
9
+
10
+ # Looks up presenters matching to `object` and stores them in the cache.
11
+ #
12
+ # @param object [Object] The presentable object
13
+ # @return [Array] An array of Presenter classes
14
+ def lookup(object)
15
+ @mutex.synchronize do
16
+ key = class_name_for(object)
17
+
18
+ if found = @mapping[key]
19
+ return found
20
+ end
21
+
22
+ @mapping[key] = presenter_classes_for(object)
23
+ end
24
+ end
25
+
26
+ # Clears the presenter cache.
27
+ #
28
+ # @return [Boolean]
29
+ def clear!
30
+ @mutex.synchronize do
31
+ @mapping = {}
32
+ end
33
+
34
+ Rails.logger.debug "Oprah cache cleared." if Oprah.debug?
35
+
36
+ true
37
+ end
38
+
39
+ private
40
+
41
+ def presenter_classes_for(object)
42
+ class_for(object).ancestors.map do |klass|
43
+ begin
44
+ (klass.name + "Presenter").constantize
45
+ rescue
46
+ end
47
+ end.compact.reverse
48
+ end
49
+
50
+ def class_name_for(object)
51
+ class_for(object).name
52
+ end
53
+
54
+ def class_for(object)
55
+ object.kind_of?(Class) ? object : object.class
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,46 @@
1
+ module Oprah
2
+ # Helpers that will be mixed into `ActionController::Base` by
3
+ # the {Oprah::Railtie}.
4
+ module ControllerHelpers
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ helper_method :present
9
+ helper_method :present_many
10
+ end
11
+
12
+ # Presents the given `object` using {Presenter.present}.
13
+ #
14
+ # Will pass the view context returned from {#oprah_view_context} to the
15
+ # presenter.
16
+ #
17
+ # @param object [Object] The object to present
18
+ # @param view_context [ActionView::Context] View context to assign
19
+ # @return [Presenter] Presented object
20
+ def present(object, view_context: oprah_view_context)
21
+ Oprah.present(object, view_context: view_context)
22
+ end
23
+
24
+ # Presents the given `objects` using {Presenter.present}.
25
+ #
26
+ # Will pass the view context returned from {#oprah_view_context} to the
27
+ # presenter.
28
+ #
29
+ # @param objects [Enumerable] The objects to present
30
+ # @param view_context [ActionView::Context] View context to assign
31
+ # @return [Presenter] Presented object
32
+ def present_many(objects, view_context: oprah_view_context)
33
+ Oprah.present_many(objects, view_context: view_context)
34
+ end
35
+
36
+ # The view context automatically passed to presented objects.
37
+ #
38
+ # You can override this method pass a custom view context to all
39
+ # presented objects from the controller scope.
40
+ #
41
+ # @return [ActionView::Context]
42
+ def oprah_view_context
43
+ view_context
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,138 @@
1
+ module Oprah
2
+ class Presenter
3
+ # @return [Object] The presented object
4
+ attr_reader :object
5
+
6
+ # @return [ActionView::Base] The view context
7
+ attr_reader :view_context
8
+
9
+ alias :h :view_context
10
+
11
+ # @!visibility private
12
+ @@cache = Oprah::Cache.new
13
+
14
+ class << self
15
+ # Returns the shared presenter cache object.
16
+ #
17
+ # @return [Cache]
18
+ def cache
19
+ @@cache
20
+ end
21
+
22
+ # Presents the given `object` with all it's matching presenters,
23
+ # following it's ancestors in reverse.
24
+ #
25
+ # @param object [Object] The object to present
26
+ # @param view_context [ActionView::Context] View context to assign
27
+ # @return [Presenter] Presented object
28
+ def present(object, view_context: default_view_context)
29
+ @@cache.lookup(object).inject(object) do |memo, presenter|
30
+ presenter.new(memo, view_context: view_context)
31
+ end
32
+ end
33
+
34
+ # Presents the given `objects` with all their matching presenters.
35
+ # The individual behavior is the same as `.present`'s.
36
+ #
37
+ # @param objects [Enumerable] The objects to present
38
+ # @param view_context [ActionView::Context] View context to assign
39
+ # @return [Enumerable] Presented collection
40
+ def present_many(objects, view_context: default_view_context)
41
+ objects.map do |object|
42
+ present(object, view_context: view_context)
43
+ end
44
+ end
45
+
46
+ # Automatically wrap the objects returned by the given one-to-one
47
+ # `association` method in presenters.
48
+ #
49
+ # @param association [Symbol] Name of the association
50
+ # @return [Boolean]
51
+ def presents_one(association)
52
+ define_method association do
53
+ self.class.present(
54
+ object.__send__(association), view_context: view_context)
55
+ end
56
+ end
57
+
58
+ # Automatically wrap the objects returned by the given one-to-many
59
+ # or many-to-many `association` method in presenters.
60
+ #
61
+ # @param association [Symbol] Name of the association
62
+ # @return [Boolean]
63
+ def presents_many(association)
64
+ define_method association do
65
+ self.class.present_many(
66
+ object.__send__(association), view_context: view_context)
67
+ end
68
+
69
+ true
70
+ end
71
+
72
+ # Returns the default view context to use if no view context is explicitly
73
+ # passed to the presenter.
74
+ #
75
+ # @return [ActionView::Context]
76
+ def default_view_context
77
+ ActionController::Base.new.view_context
78
+ end
79
+ end
80
+
81
+ # Initializes a new Presenter.
82
+ #
83
+ # @param object [Object] The object to present
84
+ # @param view_context [ActionView::Context] View context to assign
85
+ def initialize(object, view_context: self.class.default_view_context)
86
+ @object = object
87
+ @view_context = view_context
88
+ end
89
+
90
+ # Delegates all method calls not handled by the presenter to `object`.
91
+ def method_missing(meth, *args, &block)
92
+ if respond_to?(meth)
93
+ object.__send__(meth, *args, &block)
94
+ else
95
+ super
96
+ end
97
+ end
98
+
99
+ # Returns true if either `object` or `self` responds to the given method
100
+ # name.
101
+ #
102
+ # @param method [Symbol] Name of the method
103
+ # @param include_private [Boolean] Whether to include private methods
104
+ # @return [Boolean] result
105
+ def respond_to?(method, include_private = false)
106
+ super || object.respond_to?(method, include_private)
107
+ end
108
+
109
+ # Returns true if `klass` is the class of `object` or the presenter, or
110
+ # if `#class` is one of the superclasses of `object`, the presenter or
111
+ # modules included in `object` or the presenter.
112
+ #
113
+ # @param other [Module]
114
+ # @return [Boolean] result
115
+ def kind_of?(other)
116
+ super || object.kind_of?(other)
117
+ end
118
+
119
+ alias :is_a? :kind_of?
120
+
121
+ # Returns `true` if `object` or the presenter is an instance of the given
122
+ # `class`.
123
+ #
124
+ # @param klass [Class]
125
+ # @return [Boolean] result
126
+ def instance_of?(klass)
127
+ super || object.instance_of?(klass)
128
+ end
129
+
130
+ # Returns `true` if `object` or the presenter tests positive for equality.
131
+ #
132
+ # @param other [Object]
133
+ # @return [Boolean] result
134
+ def ==(other)
135
+ super || object == other
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/railtie'
2
+
3
+ module Oprah
4
+ class Railtie < Rails::Railtie
5
+ initializer "oprah.configure_cache_clear_on_code_reload" do
6
+ ActiveSupport::Reloader.to_run do
7
+ Oprah::Presenter.cache.clear!
8
+ end
9
+ end
10
+
11
+ initializer "oprah.configure_action_controller_helpers" do
12
+ ActiveSupport.on_load :action_controller do
13
+ ActionController::Base.include(Oprah::ControllerHelpers)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,4 @@
1
+ module Oprah
2
+ # @return [String] The Oprah library version.
3
+ VERSION = "0.1.1"
4
+ end
data/lib/oprah.rb ADDED
@@ -0,0 +1,43 @@
1
+ # stdlib
2
+ require 'singleton'
3
+
4
+ # gems
5
+ require 'active_support/concern'
6
+ require 'active_support/inflector'
7
+ require 'action_controller'
8
+
9
+ # internal
10
+ require 'oprah/cache'
11
+ require 'oprah/controller_helpers'
12
+ require 'oprah/presenter'
13
+ require 'oprah/version'
14
+
15
+ require 'oprah/railtie' if defined?(Rails)
16
+
17
+ # The Oprah namespace.
18
+ module Oprah
19
+ # @!visibility private
20
+ def debug?
21
+ !!ENV["OPRAH_DEBUG"]
22
+ end
23
+
24
+ # Shortcut to {Oprah::Presenter#present}.
25
+ #
26
+ # @param object [Object] The object to present
27
+ # @param view_context [ActionView::Context] View context to assign
28
+ # @return [Presenter] Presented object
29
+ def present(object, view_context: Presenter.default_view_context)
30
+ Presenter.present(object, view_context: view_context)
31
+ end
32
+
33
+ # Shortcut to {Presenter#present_many}.
34
+ #
35
+ # @param objects [Enumerable] The objects to present
36
+ # @param view_context [ActionView::Context] View context to assign
37
+ # @return [Enumerable] Presented collection
38
+ def present_many(objects, view_context: Presenter.default_view_context)
39
+ Presenter.present_many(objects, view_context: view_context)
40
+ end
41
+
42
+ extend self
43
+ end
data/oprah.gemspec ADDED
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'oprah/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "oprah"
8
+ gem.version = Oprah::VERSION
9
+ gem.authors = ["Tobias Svensson"]
10
+ gem.email = ["tob@tobiassvensson.co.uk"]
11
+ gem.summary = "Opinionated presenters for Rails 5 - without the cruft"
12
+ gem.description = gem.summary
13
+ gem.homepage = "https://github.com/endofunky/oprah"
14
+ gem.license = "Apache License, Version 2.0"
15
+
16
+ gem.files = `git ls-files -z`.split("\x0")
17
+ gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
19
+ gem.require_paths = ["lib"]
20
+
21
+ gem.has_rdoc = 'yard'
22
+
23
+ gem.add_dependency "activesupport", ">= 5.0.0"
24
+ gem.add_dependency "actionpack", ">= 5.0.0"
25
+
26
+ gem.add_development_dependency "yard", "~> 0.9.5"
27
+ gem.add_development_dependency "redcarpet", "~> 3.3.4"
28
+ gem.add_development_dependency "bundler", "~> 1.7"
29
+ gem.add_development_dependency "rake", "~> 10.0"
30
+ gem.add_development_dependency "minitest", "~> 5.5.1"
31
+ end
data/tasks/bundler.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'bundler/gem_helper'
2
+
3
+ namespace :gem do
4
+ Bundler::GemHelper.install_tasks name: 'oprah'
5
+ end
data/tasks/console.rb ADDED
@@ -0,0 +1,35 @@
1
+ class RakeConsole
2
+ GEM = Dir["*.gemspec"].first.sub('.gemspec', '')
3
+ REQUIRE_PATH = File.join(Dir.pwd, 'lib', GEM)
4
+
5
+ module Helpers
6
+ def reload!
7
+ puts "Reloading..."
8
+ $LOADED_FEATURES.select do |feat|
9
+ feat =~ /\/#{GEM}\//
10
+ end.each { |file| load file }
11
+ true
12
+ end
13
+ end
14
+
15
+ def start
16
+ require REQUIRE_PATH
17
+ ARGV.clear
18
+ Object.include(Helpers)
19
+
20
+ begin
21
+ require 'pry'
22
+ TOPLEVEL_BINDING.pry
23
+ rescue LoadError
24
+ require 'irb'
25
+ require 'irb/completion'
26
+ IRB.start
27
+ end
28
+ end
29
+ end
30
+
31
+ desc "Start development console"
32
+ task :console do
33
+ RakeConsole.new.start
34
+ end
35
+
data/tasks/minitest.rb ADDED
@@ -0,0 +1,8 @@
1
+ require 'rake/testtask'
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "test"
5
+ t.test_files = FileList['test/**/*_test.rb']
6
+ t.verbose = false
7
+ end
8
+
data/tasks/yard.rb ADDED
@@ -0,0 +1,16 @@
1
+ require 'yard'
2
+ require 'yard/rake/yardoc_task'
3
+
4
+ YARD::Rake::YardocTask.new(:doc) do |t|
5
+ t.files = ['lib/**/*.rb']
6
+ t.options = %w{
7
+ --verbose
8
+ --markup markdown
9
+ --readme README.md
10
+ --tag comment
11
+ --hide-tag comment
12
+ --hide-void-return
13
+ -M
14
+ redcarpet
15
+ }
16
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,63 @@
1
+ require 'minitest/autorun'
2
+ require 'minitest/pride'
3
+ require 'oprah'
4
+
5
+ module Fixtures
6
+ module Entity
7
+ end
8
+
9
+ class EntityPresenter < Oprah::Presenter
10
+ def foo
11
+ "foo"
12
+ end
13
+ end
14
+
15
+ class User
16
+ include Entity
17
+
18
+ def first_name
19
+ "Foo"
20
+ end
21
+
22
+ def last_name
23
+ "Bar"
24
+ end
25
+
26
+ private
27
+
28
+ def password
29
+ "baz"
30
+ end
31
+ end
32
+
33
+ class UserPresenter < Oprah::Presenter
34
+ def name
35
+ [first_name, last_name].join(' ')
36
+ end
37
+
38
+ def foo
39
+ super + "bar"
40
+ end
41
+ end
42
+
43
+ class Comment
44
+ end
45
+
46
+ class CommentPresenter < Oprah::Presenter
47
+ end
48
+
49
+ class Project
50
+ def comments
51
+ Array.new(3) { Comment.new }
52
+ end
53
+
54
+ def owner
55
+ User.new
56
+ end
57
+ end
58
+
59
+ class ProjectPresenter < Oprah::Presenter
60
+ presents_many :comments
61
+ presents_one :owner
62
+ end
63
+ end
@@ -0,0 +1,65 @@
1
+ require 'helper'
2
+
3
+ module Oprah
4
+ class ControllerHelpersTest < Minitest::Test
5
+ class Controller
6
+ @@helper_methods = []
7
+
8
+ class << self
9
+ def helper_methods
10
+ @@helper_methods
11
+ end
12
+
13
+ def helper_method(method)
14
+ @@helper_methods << method
15
+ end
16
+ end
17
+
18
+ def view_context
19
+ :ok
20
+ end
21
+
22
+ include Oprah::ControllerHelpers
23
+ end
24
+
25
+ include Fixtures
26
+
27
+ def setup
28
+ super
29
+ @controller = Controller.new
30
+ end
31
+
32
+ def test_present
33
+ presenter = @controller.present(User.new)
34
+
35
+ assert_kind_of UserPresenter, presenter
36
+ assert_kind_of EntityPresenter, presenter
37
+
38
+ assert_equal :ok, presenter.view_context
39
+ end
40
+
41
+ def test_present
42
+ presenters = @controller.present_many([User.new, User.new])
43
+
44
+ assert_equal 2, presenters.length
45
+
46
+ presenters.each do |presenter|
47
+ assert_equal "Foo Bar", presenter.name
48
+ assert_equal :ok, presenter.view_context
49
+ end
50
+ end
51
+
52
+ def test_present_custom_view_context
53
+ presenter = @controller.present(User.new, view_context: :foobar)
54
+ assert_equal :foobar, presenter.view_context
55
+ end
56
+
57
+ def test_helper_method
58
+ assert_equal [:present, :present_many], Controller.helper_methods
59
+ end
60
+
61
+ def test_oprah_view_context
62
+ assert_equal @controller.oprah_view_context, @controller.view_context
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,17 @@
1
+ require 'helper'
2
+
3
+ module Oprah
4
+ class Test < Minitest::Test
5
+ def test_debug
6
+ old = ENV["OPRAH_DEBUG"]
7
+
8
+ ENV["OPRAH_DEBUG"] = nil
9
+ refute Oprah.debug?
10
+
11
+ ENV["OPRAH_DEBUG"] = "1"
12
+ assert Oprah.debug?
13
+
14
+ ENV["OPRAH_DEBUG"] = old
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,107 @@
1
+ require 'helper'
2
+
3
+ module Oprah
4
+ class PresenterTest < Minitest::Test
5
+ include Fixtures
6
+
7
+ def test_cache
8
+ assert_kind_of Oprah::Cache, Presenter.cache
9
+ assert_equal Presenter.cache, Presenter.cache
10
+ assert_equal UserPresenter.cache, Presenter.cache
11
+ end
12
+
13
+ def test_present_many
14
+ present_many([User.new, User.new]).each do |presenter|
15
+ assert_equal "Foo Bar", presenter.name
16
+ end
17
+ end
18
+
19
+ def test_presents_many
20
+ project = present(Project.new)
21
+
22
+ assert_equal 3, project.comments.length
23
+
24
+ project.comments.each do |comment|
25
+ assert_kind_of Comment, comment
26
+ assert_kind_of CommentPresenter, comment
27
+ end
28
+ end
29
+
30
+ def test_presents_one
31
+ project = present(Project.new)
32
+ owner = project.owner
33
+
34
+ assert_kind_of UserPresenter, owner
35
+ assert_kind_of User, owner
36
+ end
37
+
38
+ def test_presenter_stack_ordering
39
+ assert_equal "foobar", present(User.new).foo
40
+ end
41
+
42
+ def test_default_view_context_using_present
43
+ presenter = Presenter.present(User.new)
44
+ assert_kind_of ActionView::Context, presenter.view_context
45
+ end
46
+
47
+ def test_default_view_context_using_initialize
48
+ presenter = UserPresenter.new(User.new)
49
+ assert_kind_of ActionView::Context, presenter.view_context
50
+ end
51
+
52
+ def test_method_missing_delegation
53
+ assert_equal "Foo Bar", present(User.new).name
54
+ assert_equal "Foo", present(User.new).first_name
55
+ assert_equal "Bar", present(User.new).last_name
56
+ end
57
+
58
+ def test_method_missing_wont_delegate_to_private_methods
59
+ assert_raises NoMethodError do
60
+ present(User.new).password
61
+ end
62
+ end
63
+
64
+ def test_kind_of?
65
+ presenter = present(User.new)
66
+
67
+ assert_kind_of User, presenter
68
+ assert_kind_of UserPresenter, presenter
69
+ assert_kind_of Entity, presenter
70
+ assert_kind_of EntityPresenter, presenter
71
+ refute_kind_of self.class, presenter
72
+ end
73
+
74
+ def test_instance_of?
75
+ presenter = present(User.new)
76
+
77
+ assert_instance_of User, presenter
78
+ assert_instance_of UserPresenter, presenter
79
+ refute_instance_of Entity, presenter
80
+ assert_instance_of EntityPresenter, presenter
81
+ refute_instance_of self.class, presenter
82
+ end
83
+
84
+ def test_equality
85
+ project = Project.new
86
+ project_presenter = Project.new
87
+
88
+ user = User.new
89
+ user_presenter = present(user)
90
+
91
+ assert_equal user_presenter, user
92
+ assert_equal user_presenter, user_presenter
93
+ refute_equal user_presenter, project
94
+ refute_equal user_presenter, project_presenter
95
+ end
96
+
97
+ private
98
+
99
+ def present_many(*args, &block)
100
+ Oprah.present_many(*args, &block)
101
+ end
102
+
103
+ def present(*args, &block)
104
+ Oprah.present(*args, &block)
105
+ end
106
+ end
107
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: oprah
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Tobias Svensson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 5.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 5.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: actionpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 5.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 5.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: yard
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.9.5
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.9.5
55
+ - !ruby/object:Gem::Dependency
56
+ name: redcarpet
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 3.3.4
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 3.3.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.7'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: minitest
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 5.5.1
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 5.5.1
111
+ description: Opinionated presenters for Rails 5 - without the cruft
112
+ email:
113
+ - tob@tobiassvensson.co.uk
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".travis.yml"
120
+ - CHANGELOG.md
121
+ - CONTRIBUTING.md
122
+ - Gemfile
123
+ - LICENSE
124
+ - README.md
125
+ - Rakefile
126
+ - lib/oprah.rb
127
+ - lib/oprah/cache.rb
128
+ - lib/oprah/controller_helpers.rb
129
+ - lib/oprah/presenter.rb
130
+ - lib/oprah/railtie.rb
131
+ - lib/oprah/version.rb
132
+ - oprah.gemspec
133
+ - tasks/bundler.rb
134
+ - tasks/console.rb
135
+ - tasks/minitest.rb
136
+ - tasks/yard.rb
137
+ - test/helper.rb
138
+ - test/oprah/controller_helpers_test.rb
139
+ - test/oprah/oprah_test.rb
140
+ - test/oprah/presenter_test.rb
141
+ homepage: https://github.com/endofunky/oprah
142
+ licenses:
143
+ - Apache License, Version 2.0
144
+ metadata: {}
145
+ post_install_message:
146
+ rdoc_options: []
147
+ require_paths:
148
+ - lib
149
+ required_ruby_version: !ruby/object:Gem::Requirement
150
+ requirements:
151
+ - - ">="
152
+ - !ruby/object:Gem::Version
153
+ version: '0'
154
+ required_rubygems_version: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: '0'
159
+ requirements: []
160
+ rubyforge_project:
161
+ rubygems_version: 2.5.1
162
+ signing_key:
163
+ specification_version: 4
164
+ summary: Opinionated presenters for Rails 5 - without the cruft
165
+ test_files:
166
+ - test/helper.rb
167
+ - test/oprah/controller_helpers_test.rb
168
+ - test/oprah/oprah_test.rb
169
+ - test/oprah/presenter_test.rb