gifted 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.
- checksums.yaml +7 -0
- data/.DS_Store +0 -0
- data/.gitignore +8 -0
- data/.travis.yml +7 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +176 -0
- data/README.md +126 -0
- data/Rakefile +12 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/gifted.gemspec +34 -0
- data/lib/gifted/decorated.rb +5 -0
- data/lib/gifted/decorator.rb +102 -0
- data/lib/gifted/gift.rb +30 -0
- data/lib/gifted/monkey/abstract_controller/rendering.rb +19 -0
- data/lib/gifted/monkey/action_controller/base/rescue_from.rb +18 -0
- data/lib/gifted/monkey/action_view/partial_renderer.rb +24 -0
- data/lib/gifted/monkey/active_record/associations.rb +60 -0
- data/lib/gifted/railtie.rb +44 -0
- data/lib/gifted/version.rb +5 -0
- data/lib/gifted/view_context.rb +43 -0
- data/lib/gifted/view_helpers.rb +14 -0
- data/lib/gifted.rb +8 -0
- metadata +176 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2ece94319acfed183417eba523628e5114f26bcfd5c2a73d4bd0463c233147dd
|
|
4
|
+
data.tar.gz: 5dbbab9578f5e6f0019757fe4d6edd70c202030f43f17db6d69bcf88d1470e84
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 770cdc16ca0bd4e9cd5a04e4eb22dcad4e2202a0b2da3373a8ac3764354931d7736cb984654afd8b3f74c2be4e09bcec537d1947209a7827a0eea298cc058abb
|
|
7
|
+
data.tar.gz: 744873ea14f98a727f68fe9bdfffdaf4284615fab164c9781ead73bbe7c134855e9fc70be82e8cec9777afe5602cfd04731869673cdcf1edb50dc32194a5a87e
|
data/.DS_Store
ADDED
|
Binary file
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
gifted (0.1.0)
|
|
5
|
+
|
|
6
|
+
GEM
|
|
7
|
+
remote: https://rubygems.org/
|
|
8
|
+
specs:
|
|
9
|
+
actioncable (5.2.3)
|
|
10
|
+
actionpack (= 5.2.3)
|
|
11
|
+
nio4r (~> 2.0)
|
|
12
|
+
websocket-driver (>= 0.6.1)
|
|
13
|
+
actionmailer (5.2.3)
|
|
14
|
+
actionpack (= 5.2.3)
|
|
15
|
+
actionview (= 5.2.3)
|
|
16
|
+
activejob (= 5.2.3)
|
|
17
|
+
mail (~> 2.5, >= 2.5.4)
|
|
18
|
+
rails-dom-testing (~> 2.0)
|
|
19
|
+
actionpack (5.2.3)
|
|
20
|
+
actionview (= 5.2.3)
|
|
21
|
+
activesupport (= 5.2.3)
|
|
22
|
+
rack (~> 2.0)
|
|
23
|
+
rack-test (>= 0.6.3)
|
|
24
|
+
rails-dom-testing (~> 2.0)
|
|
25
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
|
26
|
+
actionview (5.2.3)
|
|
27
|
+
activesupport (= 5.2.3)
|
|
28
|
+
builder (~> 3.1)
|
|
29
|
+
erubi (~> 1.4)
|
|
30
|
+
rails-dom-testing (~> 2.0)
|
|
31
|
+
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
|
32
|
+
activejob (5.2.3)
|
|
33
|
+
activesupport (= 5.2.3)
|
|
34
|
+
globalid (>= 0.3.6)
|
|
35
|
+
activemodel (5.2.3)
|
|
36
|
+
activesupport (= 5.2.3)
|
|
37
|
+
activerecord (5.2.3)
|
|
38
|
+
activemodel (= 5.2.3)
|
|
39
|
+
activesupport (= 5.2.3)
|
|
40
|
+
arel (>= 9.0)
|
|
41
|
+
activestorage (5.2.3)
|
|
42
|
+
actionpack (= 5.2.3)
|
|
43
|
+
activerecord (= 5.2.3)
|
|
44
|
+
marcel (~> 0.3.1)
|
|
45
|
+
activesupport (5.2.3)
|
|
46
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
|
47
|
+
i18n (>= 0.7, < 2)
|
|
48
|
+
minitest (~> 5.1)
|
|
49
|
+
tzinfo (~> 1.1)
|
|
50
|
+
addressable (2.6.0)
|
|
51
|
+
public_suffix (>= 2.0.2, < 4.0)
|
|
52
|
+
arel (9.0.0)
|
|
53
|
+
builder (3.2.3)
|
|
54
|
+
byebug (11.0.1)
|
|
55
|
+
capybara (3.24.0)
|
|
56
|
+
addressable
|
|
57
|
+
mini_mime (>= 0.1.3)
|
|
58
|
+
nokogiri (~> 1.8)
|
|
59
|
+
rack (>= 1.6.0)
|
|
60
|
+
rack-test (>= 0.6.3)
|
|
61
|
+
regexp_parser (~> 1.5)
|
|
62
|
+
xpath (~> 3.2)
|
|
63
|
+
childprocess (1.0.1)
|
|
64
|
+
rake (< 13.0)
|
|
65
|
+
concurrent-ruby (1.1.5)
|
|
66
|
+
crass (1.0.4)
|
|
67
|
+
erubi (1.8.0)
|
|
68
|
+
globalid (0.4.2)
|
|
69
|
+
activesupport (>= 4.2.0)
|
|
70
|
+
i18n (1.6.0)
|
|
71
|
+
concurrent-ruby (~> 1.0)
|
|
72
|
+
json (2.2.0)
|
|
73
|
+
loofah (2.2.3)
|
|
74
|
+
crass (~> 1.0.2)
|
|
75
|
+
nokogiri (>= 1.5.9)
|
|
76
|
+
mail (2.7.1)
|
|
77
|
+
mini_mime (>= 0.1.1)
|
|
78
|
+
marcel (0.3.3)
|
|
79
|
+
mimemagic (~> 0.3.2)
|
|
80
|
+
method_source (0.9.2)
|
|
81
|
+
mimemagic (0.3.3)
|
|
82
|
+
mini_mime (1.0.1)
|
|
83
|
+
mini_portile2 (2.4.0)
|
|
84
|
+
minitest (5.11.3)
|
|
85
|
+
nio4r (2.3.1)
|
|
86
|
+
nokogiri (1.10.3)
|
|
87
|
+
mini_portile2 (~> 2.4.0)
|
|
88
|
+
power_assert (1.1.4)
|
|
89
|
+
public_suffix (3.1.0)
|
|
90
|
+
puma (3.12.1)
|
|
91
|
+
rack (2.0.7)
|
|
92
|
+
rack-test (1.1.0)
|
|
93
|
+
rack (>= 1.0, < 3)
|
|
94
|
+
rails (5.2.3)
|
|
95
|
+
actioncable (= 5.2.3)
|
|
96
|
+
actionmailer (= 5.2.3)
|
|
97
|
+
actionpack (= 5.2.3)
|
|
98
|
+
actionview (= 5.2.3)
|
|
99
|
+
activejob (= 5.2.3)
|
|
100
|
+
activemodel (= 5.2.3)
|
|
101
|
+
activerecord (= 5.2.3)
|
|
102
|
+
activestorage (= 5.2.3)
|
|
103
|
+
activesupport (= 5.2.3)
|
|
104
|
+
bundler (>= 1.3.0)
|
|
105
|
+
railties (= 5.2.3)
|
|
106
|
+
sprockets-rails (>= 2.0.0)
|
|
107
|
+
rails-dom-testing (2.0.3)
|
|
108
|
+
activesupport (>= 4.2.0)
|
|
109
|
+
nokogiri (>= 1.6)
|
|
110
|
+
rails-html-sanitizer (1.0.4)
|
|
111
|
+
loofah (~> 2.2, >= 2.2.2)
|
|
112
|
+
railties (5.2.3)
|
|
113
|
+
actionpack (= 5.2.3)
|
|
114
|
+
activesupport (= 5.2.3)
|
|
115
|
+
method_source
|
|
116
|
+
rake (>= 0.8.7)
|
|
117
|
+
thor (>= 0.19.0, < 2.0)
|
|
118
|
+
rake (10.5.0)
|
|
119
|
+
regexp_parser (1.5.1)
|
|
120
|
+
rr (1.2.1)
|
|
121
|
+
rubyzip (1.2.3)
|
|
122
|
+
selenium-webdriver (3.142.3)
|
|
123
|
+
childprocess (>= 0.5, < 2.0)
|
|
124
|
+
rubyzip (~> 1.2, >= 1.2.2)
|
|
125
|
+
sprockets (3.7.2)
|
|
126
|
+
concurrent-ruby (~> 1.0)
|
|
127
|
+
rack (> 1, < 3)
|
|
128
|
+
sprockets-rails (3.2.1)
|
|
129
|
+
actionpack (>= 4.0)
|
|
130
|
+
activesupport (>= 4.0)
|
|
131
|
+
sprockets (>= 3.0.0)
|
|
132
|
+
sqlite3 (1.4.1)
|
|
133
|
+
test-unit (3.3.3)
|
|
134
|
+
power_assert
|
|
135
|
+
test-unit-activesupport (1.0.9)
|
|
136
|
+
activesupport
|
|
137
|
+
test-unit
|
|
138
|
+
test-unit-capybara (1.0.7)
|
|
139
|
+
capybara (>= 2.1.0)
|
|
140
|
+
json
|
|
141
|
+
test-unit (>= 2.5.3)
|
|
142
|
+
test-unit-rails (5.2.2)
|
|
143
|
+
rails (>= 5.1.2)
|
|
144
|
+
test-unit (>= 3.1.7)
|
|
145
|
+
test-unit-activesupport (>= 1.0.8)
|
|
146
|
+
test-unit-capybara (>= 1.0.5)
|
|
147
|
+
test-unit-rr (>= 1.0.4)
|
|
148
|
+
test-unit-rr (1.0.5)
|
|
149
|
+
rr (>= 1.1.1)
|
|
150
|
+
test-unit (>= 2.5.2)
|
|
151
|
+
thor (0.20.3)
|
|
152
|
+
thread_safe (0.3.6)
|
|
153
|
+
tzinfo (1.2.5)
|
|
154
|
+
thread_safe (~> 0.1)
|
|
155
|
+
websocket-driver (0.7.1)
|
|
156
|
+
websocket-extensions (>= 0.1.0)
|
|
157
|
+
websocket-extensions (0.1.4)
|
|
158
|
+
xpath (3.2.0)
|
|
159
|
+
nokogiri (~> 1.8)
|
|
160
|
+
|
|
161
|
+
PLATFORMS
|
|
162
|
+
ruby
|
|
163
|
+
|
|
164
|
+
DEPENDENCIES
|
|
165
|
+
bundler (~> 2.0)
|
|
166
|
+
byebug
|
|
167
|
+
capybara
|
|
168
|
+
gifted!
|
|
169
|
+
puma
|
|
170
|
+
rake
|
|
171
|
+
selenium-webdriver
|
|
172
|
+
sqlite3
|
|
173
|
+
test-unit-rails
|
|
174
|
+
|
|
175
|
+
BUNDLED WITH
|
|
176
|
+
2.0.1
|
data/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Gifted
|
|
2
|
+
|
|
3
|
+
Simple, yet powerful decorators for Ruby on Rails, inspired (and forked) from [ActiveDecorator](https://github.com/amatsuda/active_decorator). It has the following goals:
|
|
4
|
+
|
|
5
|
+
1. Fast and efficient.
|
|
6
|
+
2. Implicit decorators.
|
|
7
|
+
3. Explicit decoration.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
Gifted has an almost identical feature set to ActiveDecorator...
|
|
12
|
+
|
|
13
|
+
1. Automatically mixes decorator module into corresponding model only when:
|
|
14
|
+
1. Passing a model or collection of models or an instance of ActiveRecord::Relation from controllers to views
|
|
15
|
+
2. rendering partials with models (using `:collection` or `:object` or `:locals` explicitly or implicitly)
|
|
16
|
+
3. fetching already decorated Active Record model object's association
|
|
17
|
+
2. the decorator module runs in the model's context. So, you can directly call any attributes or methods in the decorator module
|
|
18
|
+
3. since decorators are considered as sort of helpers, you can also call any ActionView's helper methods such as `content_tag` or `link_to`
|
|
19
|
+
|
|
20
|
+
But with the following differences...
|
|
21
|
+
|
|
22
|
+
1. Decorator methods are name-spaced to a single `gift` method. This avoids confusion and collision.
|
|
23
|
+
2. Decorators are mixed into SimpleDelegator classes, which themselves delegate to the decorated object.
|
|
24
|
+
2. Decorator Views allow you to create child decorators.
|
|
25
|
+
3. Only works with Rails.
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
Gifted requires Rails >= 5.
|
|
30
|
+
|
|
31
|
+
Add this line to your application's Gemfile:
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem 'gifted'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
And then execute:
|
|
38
|
+
|
|
39
|
+
$ bundle
|
|
40
|
+
|
|
41
|
+
Or install it yourself as:
|
|
42
|
+
|
|
43
|
+
$ gem install gifted
|
|
44
|
+
|
|
45
|
+
## Usage
|
|
46
|
+
|
|
47
|
+
Create your Decorators into `/app/decorators` for each model you want decorated, and define methods. For example, a model `User`, can have a decorator `UserDecorator`:
|
|
48
|
+
|
|
49
|
+
* Model
|
|
50
|
+
```ruby
|
|
51
|
+
class User < ActiveRecord::Base
|
|
52
|
+
# first_name:string last_name:string
|
|
53
|
+
end
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
* Controller
|
|
57
|
+
```ruby
|
|
58
|
+
class UsersController < ApplicationController
|
|
59
|
+
def show
|
|
60
|
+
@user = User.find(params[:id])
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
* Decorator
|
|
66
|
+
```ruby
|
|
67
|
+
# I can call any method from the decorated object, and because I am mixed into SimpleDelegator, I
|
|
68
|
+
# can access the decorated object directly at __getobj__.
|
|
69
|
+
module UserDecorator
|
|
70
|
+
# This is a Decorator View, and can be access by passing the name of the view (:admin) to `#gift`.
|
|
71
|
+
module AdminDecorator
|
|
72
|
+
def full_name
|
|
73
|
+
"#{first_name} #{last_initial} (administrator)"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def full_name
|
|
78
|
+
"#{first_name} #{last_initial}"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def first_name
|
|
82
|
+
first_name.titleize
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def last_initial
|
|
86
|
+
last_name[0]
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
* View
|
|
92
|
+
```erb
|
|
93
|
+
<!-- @user here is auto-decorated in between the controller and the view -->
|
|
94
|
+
|
|
95
|
+
<!-- @user.gift.full_name will use the method #full_name from the decorator. -->
|
|
96
|
+
<p><%= @user.gift.full_name %></p>
|
|
97
|
+
|
|
98
|
+
<!--
|
|
99
|
+
@user.gift.last_name will use the method #last_name from the model, because @user.gift delegates
|
|
100
|
+
to the model.
|
|
101
|
+
-->
|
|
102
|
+
<p><%= @user.gift.last_name %></p>
|
|
103
|
+
|
|
104
|
+
<!--
|
|
105
|
+
Calling gift with a symbol as an argument, will decorate the object with a Decorator view.
|
|
106
|
+
So @user.gift(:admin).last_name will use the method #full_name from UserDecorator::AdminDecorator.
|
|
107
|
+
-->
|
|
108
|
+
<p><%= @user.gift(:admin).full_name %></p>
|
|
109
|
+
|
|
110
|
+
<!-- @user.last_name will use the method #last_name from the model. -->
|
|
111
|
+
<p><%= @user.last_name %></p>
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
## Development
|
|
115
|
+
|
|
116
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
117
|
+
|
|
118
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
119
|
+
|
|
120
|
+
## Contributing
|
|
121
|
+
|
|
122
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/joelmoss/gifted.
|
|
123
|
+
|
|
124
|
+
## Thanks!
|
|
125
|
+
|
|
126
|
+
This gem is hugely inspired by and (in parts) copied from [ActiveDecorator](https://github.com/amatsuda/active_decorator).
|
data/Rakefile
ADDED
data/bin/console
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
require "bundler/setup"
|
|
4
|
+
require "gifted"
|
|
5
|
+
|
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
|
8
|
+
|
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
|
10
|
+
# require "pry"
|
|
11
|
+
# Pry.start
|
|
12
|
+
|
|
13
|
+
require "irb"
|
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/gifted.gemspec
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
5
|
+
require "gifted/version"
|
|
6
|
+
|
|
7
|
+
Gem::Specification.new do |spec|
|
|
8
|
+
spec.name = "gifted"
|
|
9
|
+
spec.version = Gifted::VERSION
|
|
10
|
+
spec.authors = ["Joel Moss"]
|
|
11
|
+
spec.email = ["joel@developwithstyle.com"]
|
|
12
|
+
|
|
13
|
+
spec.summary = %q{Simple, yet Powerful Rails Decorators}
|
|
14
|
+
spec.description = %q{Simple, yet Powerful Rails Decorators}
|
|
15
|
+
spec.homepage = 'https://github.com/joelmoss/gifted'
|
|
16
|
+
|
|
17
|
+
# Specify which files should be added to the gem when it is released.
|
|
18
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
19
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
|
20
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
|
21
|
+
end
|
|
22
|
+
spec.bindir = "exe"
|
|
23
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
24
|
+
spec.require_paths = ["lib"]
|
|
25
|
+
|
|
26
|
+
spec.add_development_dependency 'bundler', '~> 2.0'
|
|
27
|
+
spec.add_development_dependency 'test-unit-rails'
|
|
28
|
+
spec.add_development_dependency 'selenium-webdriver'
|
|
29
|
+
spec.add_development_dependency 'puma'
|
|
30
|
+
spec.add_development_dependency 'capybara'
|
|
31
|
+
spec.add_development_dependency 'sqlite3'
|
|
32
|
+
spec.add_development_dependency 'rake'
|
|
33
|
+
spec.add_development_dependency 'byebug'
|
|
34
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'singleton'
|
|
4
|
+
require 'gifted/view_helpers'
|
|
5
|
+
require 'gifted/decorated'
|
|
6
|
+
require 'gifted/gift'
|
|
7
|
+
|
|
8
|
+
module Gifted
|
|
9
|
+
class Decorator
|
|
10
|
+
include Singleton
|
|
11
|
+
|
|
12
|
+
def initialize
|
|
13
|
+
@@decorators = {}
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def decorate(obj)
|
|
17
|
+
if obj.is_a?(Array)
|
|
18
|
+
obj.each { |r| decorate r }
|
|
19
|
+
elsif obj.is_a?(Hash)
|
|
20
|
+
obj.each_value { |v| decorate v }
|
|
21
|
+
elsif obj.is_a?(ActiveRecord::Relation)
|
|
22
|
+
extend_with_relation_decorator obj
|
|
23
|
+
else
|
|
24
|
+
extend_with_decorated obj
|
|
25
|
+
|
|
26
|
+
return obj unless (d = decorator_for(obj.class))
|
|
27
|
+
|
|
28
|
+
# !! The original monkey patched code.
|
|
29
|
+
# obj.extend d unless obj.is_a? d
|
|
30
|
+
|
|
31
|
+
# The new hotness, which mixes in a single "parent" method called `gift`, that isolates all
|
|
32
|
+
# decorated methods; avoiding confusion as to which methods are decorated and which are not.
|
|
33
|
+
#
|
|
34
|
+
# Example:
|
|
35
|
+
# object.gift.my_decorated_method
|
|
36
|
+
# object.my_non_decorated_method
|
|
37
|
+
#
|
|
38
|
+
# Instead of extending (decorating) the given object with the decorator module, this extends
|
|
39
|
+
# it with Gifted::Gift, and sets the default decorator as the calculated decorator module
|
|
40
|
+
# above. Gifted::Gift extends the object with one method; `gift`, which returns the
|
|
41
|
+
# default decorator.
|
|
42
|
+
obj.extend(::Gifted::Gift).default_decorator = d unless obj.is_a? ::Gifted::Gift
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
obj
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Decorates AR model object's association only when the object was decorated.
|
|
49
|
+
# Returns the association instance.
|
|
50
|
+
def decorate_association(owner, target)
|
|
51
|
+
owner.is_a?(Gifted::Decorated) ? decorate(target) : target
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
private
|
|
55
|
+
|
|
56
|
+
# Returns a decorator module for the given class.
|
|
57
|
+
# Returns `nil` if no decorator module was found.
|
|
58
|
+
def decorator_for(model_class)
|
|
59
|
+
return @@decorators[model_class] if @@decorators.key? model_class
|
|
60
|
+
|
|
61
|
+
decorator_name = "#{model_class.name}Decorator"
|
|
62
|
+
d = Object.const_get decorator_name, false
|
|
63
|
+
unless Class === d
|
|
64
|
+
d.send :include, Gifted::ViewHelpers
|
|
65
|
+
@@decorators[model_class] = d
|
|
66
|
+
else
|
|
67
|
+
# Cache nil results
|
|
68
|
+
@@decorators[model_class] = nil
|
|
69
|
+
end
|
|
70
|
+
rescue NameError
|
|
71
|
+
# This handles ActiveRecord STI models which don't have a decorator class - looking up it's
|
|
72
|
+
# base class.
|
|
73
|
+
|
|
74
|
+
if model_class.respond_to?(:base_class) && (model_class.base_class != model_class)
|
|
75
|
+
@@decorators[model_class] = decorator_for model_class.base_class
|
|
76
|
+
else
|
|
77
|
+
# Cache nil results
|
|
78
|
+
@@decorators[model_class] = nil
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def extend_with_relation_decorator(obj)
|
|
83
|
+
return if obj.is_a? Gifted::RelationDecorator
|
|
84
|
+
|
|
85
|
+
obj.extend Gifted::RelationDecorator
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def extend_with_decorated(obj)
|
|
89
|
+
return if !obj.is_a?(ActiveRecord::Base) || obj.is_a?(Gifted::Decorated)
|
|
90
|
+
|
|
91
|
+
obj.extend Gifted::Decorated
|
|
92
|
+
end
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
module RelationDecorator
|
|
96
|
+
def records
|
|
97
|
+
arr = super
|
|
98
|
+
Gifted::Decorator.instance.decorate arr
|
|
99
|
+
arr
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
data/lib/gifted/gift.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gifted
|
|
4
|
+
module Gift
|
|
5
|
+
attr_accessor :default_decorator
|
|
6
|
+
|
|
7
|
+
def gift(view = nil)
|
|
8
|
+
@decorator_views ||= { default: gift_delegator }
|
|
9
|
+
|
|
10
|
+
if view
|
|
11
|
+
@decorator_views[view] ||= gift_delegator(view)
|
|
12
|
+
else
|
|
13
|
+
@decorator_views[:default]
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
private
|
|
18
|
+
|
|
19
|
+
def gift_delegator(view = nil)
|
|
20
|
+
decorator = default_decorator
|
|
21
|
+
|
|
22
|
+
if view
|
|
23
|
+
decorator = default_decorator.const_get("#{view.to_s.camelcase}Decorator")
|
|
24
|
+
decorator.send :include, Gifted::ViewContext unless decorator.is_a?(Class)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
SimpleDelegator.new(self).extend decorator
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A monkey-patch for Rails controllers/mailers that auto-decorates ivars
|
|
4
|
+
# that are passed to views.
|
|
5
|
+
module Gifted
|
|
6
|
+
module Monkey
|
|
7
|
+
module AbstractController
|
|
8
|
+
module Rendering
|
|
9
|
+
def view_assigns
|
|
10
|
+
hash = super
|
|
11
|
+
hash.each_value do |v|
|
|
12
|
+
Gifted::Decorator.instance.decorate v
|
|
13
|
+
end
|
|
14
|
+
hash
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A monkey-patch for Action Controller to pass the controller view_context over
|
|
4
|
+
# to `render` invocation in `rescue_from` block.
|
|
5
|
+
module Gifted
|
|
6
|
+
module Monkey
|
|
7
|
+
module ActionController
|
|
8
|
+
module Base
|
|
9
|
+
def rescue_with_handler(*)
|
|
10
|
+
Gifted::ViewContext.push(view_context)
|
|
11
|
+
super
|
|
12
|
+
ensure
|
|
13
|
+
Gifted::ViewContext.pop
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A monkey-patch for Action View `render :partial` that auto-decorates `locals` values.
|
|
4
|
+
module Gifted
|
|
5
|
+
module Monkey
|
|
6
|
+
module ActionView
|
|
7
|
+
module PartialRenderer
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def setup(*)
|
|
11
|
+
super
|
|
12
|
+
|
|
13
|
+
@locals.each_value do |v|
|
|
14
|
+
Gifted::Decorator.instance.decorate v
|
|
15
|
+
end if @locals
|
|
16
|
+
Gifted::Decorator.instance.decorate @object if @object
|
|
17
|
+
Gifted::Decorator.instance.decorate @collection unless @collection.blank?
|
|
18
|
+
|
|
19
|
+
self
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A monkey-patch for Active Record that enables association auto-decoration.
|
|
4
|
+
module Gifted
|
|
5
|
+
module Monkey
|
|
6
|
+
module ActiveRecord
|
|
7
|
+
module Associations
|
|
8
|
+
module Association
|
|
9
|
+
def target
|
|
10
|
+
Gifted::Decorator.instance.decorate_association(owner, super)
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
module CollectionProxy
|
|
15
|
+
def take(*)
|
|
16
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def last(*)
|
|
20
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def find_nth_with_limit(*)
|
|
26
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def find_nth_from_last(*)
|
|
30
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
module CollectionAssociation
|
|
35
|
+
private
|
|
36
|
+
|
|
37
|
+
def build_record(*)
|
|
38
|
+
Gifted::Decorator.instance.decorate_association(@owner, super)
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
module AssociationRelation
|
|
44
|
+
def take(*)
|
|
45
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
private
|
|
49
|
+
|
|
50
|
+
def find_nth_with_limit(*)
|
|
51
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def find_nth_from_last(*)
|
|
55
|
+
Gifted::Decorator.instance.decorate_association(@association.owner, super)
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'rails'
|
|
4
|
+
|
|
5
|
+
module Gifted
|
|
6
|
+
class Railtie < ::Rails::Railtie
|
|
7
|
+
initializer 'gifted' do
|
|
8
|
+
ActiveSupport.on_load :action_view do
|
|
9
|
+
require 'gifted/monkey/action_view/partial_renderer'
|
|
10
|
+
ActionView::PartialRenderer.send :prepend, Gifted::Monkey::ActionView::PartialRenderer
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
ActiveSupport.on_load :action_controller do
|
|
14
|
+
require 'gifted/monkey/abstract_controller/rendering'
|
|
15
|
+
ActionController::Base.send :prepend, Gifted::Monkey::AbstractController::Rendering
|
|
16
|
+
|
|
17
|
+
require 'gifted/monkey/action_controller/base/rescue_from'
|
|
18
|
+
ActionController::Base.send :prepend, Gifted::Monkey::ActionController::Base
|
|
19
|
+
|
|
20
|
+
require 'gifted/view_context'
|
|
21
|
+
ActionController::Base.send :include, Gifted::ViewContext::Filter
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
ActiveSupport.on_load :action_mailer do
|
|
25
|
+
require 'gifted/monkey/abstract_controller/rendering'
|
|
26
|
+
ActionMailer::Base.send :prepend, Gifted::Monkey::AbstractController::Rendering
|
|
27
|
+
|
|
28
|
+
if ActionMailer::Base.respond_to? :before_action
|
|
29
|
+
require 'gifted/view_context'
|
|
30
|
+
ActionMailer::Base.send :include, Gifted::ViewContext::Filter
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
ActiveSupport.on_load :active_record do
|
|
35
|
+
require 'gifted/monkey/active_record/associations'
|
|
36
|
+
|
|
37
|
+
ActiveRecord::Associations::Association.send :prepend, Gifted::Monkey::ActiveRecord::Associations::Association
|
|
38
|
+
ActiveRecord::AssociationRelation.send :prepend, Gifted::Monkey::ActiveRecord::AssociationRelation
|
|
39
|
+
ActiveRecord::Associations::CollectionProxy.send :prepend, Gifted::Monkey::ActiveRecord::Associations::CollectionProxy
|
|
40
|
+
ActiveRecord::Associations::CollectionAssociation.send :prepend, Gifted::Monkey::ActiveRecord::Associations::CollectionAssociation
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A module that carries the controllers' view_context to decorators.
|
|
4
|
+
module Gifted
|
|
5
|
+
module ViewContext
|
|
6
|
+
class << self
|
|
7
|
+
def current
|
|
8
|
+
view_context_stack.last
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def push(view_context)
|
|
12
|
+
view_context_stack.push view_context
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def pop
|
|
16
|
+
view_context_stack.pop
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def view_context_stack
|
|
20
|
+
Thread.current[:gifted_view_contexts] ||= []
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run_with(view_context)
|
|
24
|
+
push view_context
|
|
25
|
+
yield
|
|
26
|
+
ensure
|
|
27
|
+
pop
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
module Filter
|
|
32
|
+
extend ActiveSupport::Concern
|
|
33
|
+
|
|
34
|
+
included do
|
|
35
|
+
around_action do |controller, blk|
|
|
36
|
+
Gifted::ViewContext.run_with(controller.view_context) do
|
|
37
|
+
blk.call
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Gifted
|
|
4
|
+
module ViewHelpers
|
|
5
|
+
def method_missing(method_name, *args, &block)
|
|
6
|
+
view_context = Gifted::ViewContext.current
|
|
7
|
+
view_context&.respond_to?(method_name) ? view_context.send(method_name, *args, &block) : super
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def respond_to_missing?(method_name, *args)
|
|
11
|
+
Gifted::ViewContext.current.respond_to?(method_name) || super
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
data/lib/gifted.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gifted
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Joel Moss
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: exe
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2019-06-20 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '2.0'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '2.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: test-unit-rails
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: selenium-webdriver
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ">="
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '0'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ">="
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '0'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: puma
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ">="
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ">="
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: capybara
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ">="
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: '0'
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ">="
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: '0'
|
|
83
|
+
- !ruby/object:Gem::Dependency
|
|
84
|
+
name: sqlite3
|
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
|
86
|
+
requirements:
|
|
87
|
+
- - ">="
|
|
88
|
+
- !ruby/object:Gem::Version
|
|
89
|
+
version: '0'
|
|
90
|
+
type: :development
|
|
91
|
+
prerelease: false
|
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
93
|
+
requirements:
|
|
94
|
+
- - ">="
|
|
95
|
+
- !ruby/object:Gem::Version
|
|
96
|
+
version: '0'
|
|
97
|
+
- !ruby/object:Gem::Dependency
|
|
98
|
+
name: rake
|
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
|
100
|
+
requirements:
|
|
101
|
+
- - ">="
|
|
102
|
+
- !ruby/object:Gem::Version
|
|
103
|
+
version: '0'
|
|
104
|
+
type: :development
|
|
105
|
+
prerelease: false
|
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
107
|
+
requirements:
|
|
108
|
+
- - ">="
|
|
109
|
+
- !ruby/object:Gem::Version
|
|
110
|
+
version: '0'
|
|
111
|
+
- !ruby/object:Gem::Dependency
|
|
112
|
+
name: byebug
|
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
|
114
|
+
requirements:
|
|
115
|
+
- - ">="
|
|
116
|
+
- !ruby/object:Gem::Version
|
|
117
|
+
version: '0'
|
|
118
|
+
type: :development
|
|
119
|
+
prerelease: false
|
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
121
|
+
requirements:
|
|
122
|
+
- - ">="
|
|
123
|
+
- !ruby/object:Gem::Version
|
|
124
|
+
version: '0'
|
|
125
|
+
description: Simple, yet Powerful Rails Decorators
|
|
126
|
+
email:
|
|
127
|
+
- joel@developwithstyle.com
|
|
128
|
+
executables: []
|
|
129
|
+
extensions: []
|
|
130
|
+
extra_rdoc_files: []
|
|
131
|
+
files:
|
|
132
|
+
- ".DS_Store"
|
|
133
|
+
- ".gitignore"
|
|
134
|
+
- ".travis.yml"
|
|
135
|
+
- Gemfile
|
|
136
|
+
- Gemfile.lock
|
|
137
|
+
- README.md
|
|
138
|
+
- Rakefile
|
|
139
|
+
- bin/console
|
|
140
|
+
- bin/setup
|
|
141
|
+
- gifted.gemspec
|
|
142
|
+
- lib/gifted.rb
|
|
143
|
+
- lib/gifted/decorated.rb
|
|
144
|
+
- lib/gifted/decorator.rb
|
|
145
|
+
- lib/gifted/gift.rb
|
|
146
|
+
- lib/gifted/monkey/abstract_controller/rendering.rb
|
|
147
|
+
- lib/gifted/monkey/action_controller/base/rescue_from.rb
|
|
148
|
+
- lib/gifted/monkey/action_view/partial_renderer.rb
|
|
149
|
+
- lib/gifted/monkey/active_record/associations.rb
|
|
150
|
+
- lib/gifted/railtie.rb
|
|
151
|
+
- lib/gifted/version.rb
|
|
152
|
+
- lib/gifted/view_context.rb
|
|
153
|
+
- lib/gifted/view_helpers.rb
|
|
154
|
+
homepage: https://github.com/joelmoss/gifted
|
|
155
|
+
licenses: []
|
|
156
|
+
metadata: {}
|
|
157
|
+
post_install_message:
|
|
158
|
+
rdoc_options: []
|
|
159
|
+
require_paths:
|
|
160
|
+
- lib
|
|
161
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
162
|
+
requirements:
|
|
163
|
+
- - ">="
|
|
164
|
+
- !ruby/object:Gem::Version
|
|
165
|
+
version: '0'
|
|
166
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
167
|
+
requirements:
|
|
168
|
+
- - ">="
|
|
169
|
+
- !ruby/object:Gem::Version
|
|
170
|
+
version: '0'
|
|
171
|
+
requirements: []
|
|
172
|
+
rubygems_version: 3.0.3
|
|
173
|
+
signing_key:
|
|
174
|
+
specification_version: 4
|
|
175
|
+
summary: Simple, yet Powerful Rails Decorators
|
|
176
|
+
test_files: []
|