gestart-showcase 1.0.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/.github/workflows/ci.yml +29 -0
- data/.gitignore +19 -0
- data/.rspec +2 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +49 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.md +179 -0
- data/Rakefile +14 -0
- data/gestart-showcase.gemspec +29 -0
- data/lib/gestart-showcase.rb +1 -0
- data/lib/gestart_showcase/errors.rb +5 -0
- data/lib/gestart_showcase/helpers/config_object.rb +28 -0
- data/lib/gestart_showcase/helpers/html_class_attribute.rb +23 -0
- data/lib/gestart_showcase/helpers/html_options.rb +32 -0
- data/lib/gestart_showcase/helpers/module_method_builder.rb +21 -0
- data/lib/gestart_showcase/helpers/present.rb +50 -0
- data/lib/gestart_showcase/presenter.rb +64 -0
- data/lib/gestart_showcase/railtie.rb +12 -0
- data/lib/gestart_showcase/traits/record.rb +64 -0
- data/lib/gestart_showcase/traits.rb +6 -0
- data/lib/gestart_showcase/version.rb +4 -0
- data/lib/gestart_showcase.rb +7 -0
- data/spec/fixtures.rb +112 -0
- data/spec/gestart_showcase/helpers/html_class_attribute_spec.rb +83 -0
- data/spec/gestart_showcase/helpers/html_options_spec.rb +37 -0
- data/spec/gestart_showcase/helpers/present_spec.rb +53 -0
- data/spec/gestart_showcase/presenter_spec.rb +71 -0
- data/spec/gestart_showcase/traits/record_spec.rb +59 -0
- data/spec/helpers/config_object_spec.rb +45 -0
- data/spec/spec_helper.rb +12 -0
- metadata +150 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 2fd017b78a3588a69ad0bb67759b8ce42f54ceea685a568bf7f37fd74297a602
|
|
4
|
+
data.tar.gz: 284a480294e3603197899f9143f1414daeb94b0159a4d5bdccacb6139a69fbe6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 595db0c75c8855199d60a7bc6ca9a07cec8d6d96855f93fea7d540ea19f7b524ed5cc83c0a7339951a5d1f12fd3dc8e9a53d23df676f00f2ee4026e162a4885e
|
|
7
|
+
data.tar.gz: b393e943f26c68e32d1703c3e71c5000c5e617f3bc1d55d3664fc98056b8655179a1a5e6a19cdd0f34e7e9819fe9cb009c5a5a92116751faa8b08773955d3e10
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
rspec:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
- uses: ruby/setup-ruby@v1
|
|
14
|
+
with:
|
|
15
|
+
ruby-version: '3.4.9'
|
|
16
|
+
bundler-cache: true
|
|
17
|
+
- run: bundle exec rspec
|
|
18
|
+
|
|
19
|
+
rspec-frozen:
|
|
20
|
+
runs-on: ubuntu-latest
|
|
21
|
+
steps:
|
|
22
|
+
- uses: actions/checkout@v4
|
|
23
|
+
- uses: ruby/setup-ruby@v1
|
|
24
|
+
with:
|
|
25
|
+
ruby-version: '3.4.9'
|
|
26
|
+
bundler-cache: true
|
|
27
|
+
- run: bundle exec rspec
|
|
28
|
+
env:
|
|
29
|
+
RUBYOPT: '--enable-frozen-string-literal'
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 3.4.9
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# v1.0.0 — the fork
|
|
2
|
+
|
|
3
|
+
Gestart fork of [`showcase`](https://github.com/welaika/showcase) 0.2.5, the last
|
|
4
|
+
upstream release, dated 16/03/2014.
|
|
5
|
+
|
|
6
|
+
**Why.** `Showcase::Presenter` sits under roughly 430 call sites in Gestart, and
|
|
7
|
+
it arrived as a dependency of `admino` rather than as a choice. Once admino was
|
|
8
|
+
forked, this was the last third-party code left in the rendering path of every
|
|
9
|
+
index page and every presenter. Rewriting it was rejected: the presenter is
|
|
10
|
+
already a `SimpleDelegator`, but one that overrides `class`, `kind_of?`, `is_a?`
|
|
11
|
+
and `instance_of?` so they answer as the record does — `form_for`, `render`,
|
|
12
|
+
`polymorphic_path` and `dom_id` all lean on that, and no test covers it.
|
|
13
|
+
|
|
14
|
+
The full decision lives in the application repo: `docs/decisioni/admino.md`.
|
|
15
|
+
|
|
16
|
+
**What changed from 0.2.5.**
|
|
17
|
+
|
|
18
|
+
* Pruned what Gestart never calls: the `LinkTo`, `Seo` and `Share` traits, the
|
|
19
|
+
`SeoMetaBuilder` and `FirstNonblank` helpers, `Traits::Base` (which only those
|
|
20
|
+
traits used) and the presenter generator. Their specs went with them.
|
|
21
|
+
* `Traits::Record` **stays whole**, `box` included. It was listed as dead and it
|
|
22
|
+
is not: `dom_id` reaches the rendered page through admino's table presenter,
|
|
23
|
+
which asks `resource.respond_to?(:dom_id)` and, when it does, writes an `id` on
|
|
24
|
+
every row. Removing it would have silently dropped `id="tool_1"` from every
|
|
25
|
+
index table.
|
|
26
|
+
* `traits/record.rb` now requires `ConfigObject` and `HtmlOptions` explicitly.
|
|
27
|
+
Upstream got them for free because `link_to.rb` required them first; with that
|
|
28
|
+
file gone, the constants stopped loading.
|
|
29
|
+
* Suite runnable on Ruby 3.4 and Rails 8.1: `RailsViewContext` is built with an
|
|
30
|
+
explicit lookup context, `RSpecHtmlMatchers` is included explicitly, and
|
|
31
|
+
`appraisal`, `coveralls` and the Rails 3.2/4 gemfiles are gone.
|
|
32
|
+
|
|
33
|
+
**Maintenance.** This fork gets repaired, not extended: it is fixed when Rails
|
|
34
|
+
breaks it, and nothing more.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
# Version 0.2.5
|
|
39
|
+
|
|
40
|
+
* Smarter HtmlOptions (extracted CSS class logic to HtmlClassAttribute)
|
|
41
|
+
|
|
42
|
+
# Version 0.2.4
|
|
43
|
+
|
|
44
|
+
* Added missing ActiveSupport requires
|
|
45
|
+
|
|
46
|
+
# Version 0.2.3
|
|
47
|
+
|
|
48
|
+
* Fix missing namespace in HtmlOptions and FirstNonblank helpers
|
|
49
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Stefano Verna
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
# gestart-showcase
|
|
2
|
+
|
|
3
|
+
A simple but powerful exhibit/presenter implementation.
|
|
4
|
+
|
|
5
|
+
> **This is a maintenance fork.** `gestart-showcase` is Gestart's fork of
|
|
6
|
+
> [`showcase`](https://github.com/welaika/showcase) 0.2.5, whose last upstream
|
|
7
|
+
> release is dated 16/03/2014. It exists because the presenter layer sits under
|
|
8
|
+
> roughly 430 call sites in Gestart, and upstream will get no further fixes.
|
|
9
|
+
>
|
|
10
|
+
> **It gets repaired, not extended.** It is fixed when Rails breaks it, and
|
|
11
|
+
> nothing more.
|
|
12
|
+
>
|
|
13
|
+
> Compared to upstream: the namespace is `GestartShowcase::`, and the `LinkTo`,
|
|
14
|
+
> `Seo` and `Share` traits, the `SeoMetaBuilder` and `FirstNonblank` helpers and
|
|
15
|
+
> the presenter generator are gone — Gestart never called them. `Traits::Record`
|
|
16
|
+
> stays, `box` included. See [`CHANGELOG.md`](CHANGELOG.md).
|
|
17
|
+
|
|
18
|
+
## Why should I use presenters in my Rails app?
|
|
19
|
+
|
|
20
|
+
See [Avdi's Exhibits introductory post](http://devblog.avdi.org/2012/06/04/displaycase-gem-now-available/).
|
|
21
|
+
|
|
22
|
+
## Installation
|
|
23
|
+
|
|
24
|
+
Add this line to your application's Gemfile:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
gem 'gestart-showcase'
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
And then execute:
|
|
31
|
+
|
|
32
|
+
```
|
|
33
|
+
$ bundle
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Or install it yourself as:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
$ gem install gestart-showcase
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Usage
|
|
43
|
+
|
|
44
|
+
With Rails, you're already set, move on! With Padrino, include `GestartShowcase::Helpers::Present`
|
|
45
|
+
in your app `helpers` block.
|
|
46
|
+
|
|
47
|
+
```ruby
|
|
48
|
+
helpers do
|
|
49
|
+
include GestartShowcase::Helpers::Present
|
|
50
|
+
end
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
You can now instantiate new presenters in your controller/views using the
|
|
54
|
+
included helpers:
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# this is the object that needs to be presented
|
|
58
|
+
person = Person.new
|
|
59
|
+
|
|
60
|
+
# automatically infers presenter class to use based on person's class name
|
|
61
|
+
present(person) # => returns a PersonPresenter instance
|
|
62
|
+
|
|
63
|
+
# you can also explicitly tell what presenter to use
|
|
64
|
+
present(person, AdminPresenter) # => returns an AdminPresenter instance
|
|
65
|
+
|
|
66
|
+
# explicit presenter and context
|
|
67
|
+
present(person, PersonPresenter, context)
|
|
68
|
+
|
|
69
|
+
# maps each person in the collection with a presenter
|
|
70
|
+
present_collection([person]) # => returns an array of PersonPresenters
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Define your presenters i.e. in a `app/presenters` folder:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
class ProjectPresenter < GestartShowcase::Presenter
|
|
77
|
+
# automatically wraps the attribute into a PersonPresenter
|
|
78
|
+
presents :person
|
|
79
|
+
|
|
80
|
+
# automatically wraps the attribute into an AdminPresenter
|
|
81
|
+
presents :person, with: AdminPresenter
|
|
82
|
+
|
|
83
|
+
# expects project.task to return an enumerable. automatically wraps each task
|
|
84
|
+
# in a TaskPresenter presenter
|
|
85
|
+
presents_collection :tasks
|
|
86
|
+
|
|
87
|
+
# you can use `view_context`, or the shortcut `h`, to access the context.
|
|
88
|
+
# `object` refers to the object being presented
|
|
89
|
+
def title
|
|
90
|
+
h.link_to object.title, object
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
## Rails
|
|
96
|
+
|
|
97
|
+
### Traits
|
|
98
|
+
|
|
99
|
+
Please [read the tests](https://github.com/gestartcloudsrl/gestart-showcase/tree/main/lib/gestart_showcase/traits)
|
|
100
|
+
for a detailed explanation of each method available.
|
|
101
|
+
|
|
102
|
+
#### `GestartShowcase::Traits::Record`
|
|
103
|
+
|
|
104
|
+
To be used to present ActiveModel-based records. Inside your presenter, include
|
|
105
|
+
the trait like this:
|
|
106
|
+
|
|
107
|
+
```ruby
|
|
108
|
+
class ProjectPresenter < GestartShowcase::Presenter
|
|
109
|
+
include GestartShowcase::Traits::Record
|
|
110
|
+
end
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
##### `#dom_id`
|
|
114
|
+
|
|
115
|
+
```ruby
|
|
116
|
+
present(@project).dom_id # => "project_12"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
##### `#dom_class`
|
|
120
|
+
|
|
121
|
+
```ruby
|
|
122
|
+
present(@project).dom_class # => "project"
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
##### `#box`
|
|
126
|
+
|
|
127
|
+
Super useful in acceptance testing to check the presence of a record inside a
|
|
128
|
+
view:
|
|
129
|
+
|
|
130
|
+
```erb
|
|
131
|
+
<% present(@project).box(class: 'big') do %>
|
|
132
|
+
<p>Hi there!</p>
|
|
133
|
+
<% end %>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Produces the following:
|
|
137
|
+
|
|
138
|
+
```html
|
|
139
|
+
<div class="project big" id="project_12">
|
|
140
|
+
<p>Hi there</p>
|
|
141
|
+
</div>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Additional HTML attributes can be optionally specified within a config block
|
|
145
|
+
inside the presenter:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
class ProjectPresenter < GestartShowcase::Presenter
|
|
149
|
+
include GestartShowcase::Traits::Record
|
|
150
|
+
|
|
151
|
+
box do |c|
|
|
152
|
+
c.html_options class: 'another-class', role: 'project'
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Testing
|
|
158
|
+
|
|
159
|
+
Install gems:
|
|
160
|
+
|
|
161
|
+
```
|
|
162
|
+
$ bundle
|
|
163
|
+
$ rake appraisal:install
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
Launch tests:
|
|
167
|
+
|
|
168
|
+
```
|
|
169
|
+
rake appraisal
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
## Contributing
|
|
174
|
+
|
|
175
|
+
1. Fork it
|
|
176
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
177
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
178
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
179
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
require 'appraisal'
|
|
4
|
+
|
|
5
|
+
desc 'Default: run specs'
|
|
6
|
+
task default: :spec
|
|
7
|
+
|
|
8
|
+
require 'rspec/core/rake_task'
|
|
9
|
+
RSpec::Core::RakeTask.new do |t|
|
|
10
|
+
t.pattern = "spec/**/*_spec.rb"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
Bundler::GemHelper.install_tasks
|
|
14
|
+
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'gestart_showcase/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = 'gestart-showcase'
|
|
8
|
+
gem.version = GestartShowcase::VERSION
|
|
9
|
+
gem.authors = ['Gestart Cloud S.r.l.', 'Stefano Verna']
|
|
10
|
+
gem.email = ['dev@gestart.net']
|
|
11
|
+
gem.description = %q{Gestart fork of showcase 0.2.5, pruned of what Gestart does not use}
|
|
12
|
+
gem.summary = %q{Gestart fork of showcase: the presenter layer behind every Gestart view}
|
|
13
|
+
gem.homepage = 'https://github.com/gestartcloudsrl/gestart-showcase'
|
|
14
|
+
gem.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
gem.files = `git ls-files`.split($/)
|
|
17
|
+
gem.executables = gem.files.grep(%r{^bin/}).map { |f| File.basename(f) }
|
|
18
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
gem.require_paths = ['lib']
|
|
20
|
+
|
|
21
|
+
gem.required_ruby_version = '>= 3.1'
|
|
22
|
+
|
|
23
|
+
gem.add_dependency 'activesupport'
|
|
24
|
+
|
|
25
|
+
gem.add_development_dependency 'rspec'
|
|
26
|
+
gem.add_development_dependency 'rails'
|
|
27
|
+
gem.add_development_dependency 'rspec-html-matchers'
|
|
28
|
+
gem.add_development_dependency 'ostruct'
|
|
29
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require 'gestart_showcase'
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'ostruct'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
class ConfigObject
|
|
6
|
+
def initialize(context, &block)
|
|
7
|
+
@config_block = block
|
|
8
|
+
@context = context
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def to_struct
|
|
12
|
+
OpenStruct.new(to_hash)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_hash
|
|
16
|
+
@result = {}
|
|
17
|
+
@context.instance_exec(self, &@config_block)
|
|
18
|
+
@result.symbolize_keys
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def method_missing(name, *args, &block)
|
|
22
|
+
name = name.to_s.gsub(/=$/, '')
|
|
23
|
+
@result[name.to_sym] = args.first
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module GestartShowcase
|
|
2
|
+
module Helpers
|
|
3
|
+
class HtmlClassAttribute
|
|
4
|
+
def initialize(value = nil)
|
|
5
|
+
@css_classes = value.to_s.strip.split(/\s+/)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def <<(value)
|
|
9
|
+
value = value.to_s.strip.split(/\s+/)
|
|
10
|
+
@css_classes += value
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def to_html_attribute
|
|
14
|
+
if @css_classes.any?
|
|
15
|
+
@css_classes.uniq.join(' ')
|
|
16
|
+
else
|
|
17
|
+
nil
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require 'gestart_showcase/helpers/html_class_attribute'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
class HtmlOptions
|
|
6
|
+
def initialize(options = {})
|
|
7
|
+
@options = (options || {}).symbolize_keys
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def add_class!(css_class)
|
|
11
|
+
merge_attrs!(class: css_class)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def merge_attrs!(options = {})
|
|
15
|
+
new_options = (options || {}).symbolize_keys
|
|
16
|
+
|
|
17
|
+
if new_options[:class]
|
|
18
|
+
class_attr = HtmlClassAttribute.new(@options[:class])
|
|
19
|
+
class_attr << new_options[:class]
|
|
20
|
+
new_options[:class] = class_attr.to_html_attribute
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
@options.merge!(new_options)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def to_h
|
|
27
|
+
@options.dup
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
require 'active_support/core_ext/object/blank'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
module ModuleMethodBuilder
|
|
6
|
+
def define_module_method(name_chunks, &block)
|
|
7
|
+
method_name = Array(name_chunks).map(&:to_s).map(&:presence).compact.join("_")
|
|
8
|
+
if method_defined?(method_name)
|
|
9
|
+
false
|
|
10
|
+
else
|
|
11
|
+
method_module = Module.new do
|
|
12
|
+
define_method(method_name, &block)
|
|
13
|
+
end
|
|
14
|
+
include(method_module)
|
|
15
|
+
true
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'active_support/concern'
|
|
2
|
+
require 'active_support/inflector'
|
|
3
|
+
require 'active_support/core_ext/hash/keys'
|
|
4
|
+
|
|
5
|
+
require 'gestart_showcase/errors'
|
|
6
|
+
|
|
7
|
+
module GestartShowcase
|
|
8
|
+
module Helpers
|
|
9
|
+
module Present
|
|
10
|
+
extend ActiveSupport::Concern
|
|
11
|
+
|
|
12
|
+
def presenter_context
|
|
13
|
+
if respond_to? :view_context
|
|
14
|
+
view_context
|
|
15
|
+
else
|
|
16
|
+
self
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def present(obj, klass = nil, context = presenter_context, options = {})
|
|
21
|
+
options.assert_valid_keys(:nil_presenter)
|
|
22
|
+
|
|
23
|
+
if obj || options.fetch(:nil_presenter, false)
|
|
24
|
+
klass ||= presenter_class(obj)
|
|
25
|
+
if klass.nil?
|
|
26
|
+
raise GestartShowcase::PresenterClassNotFound, "Unable to guess a presenter class for #{obj.inspect}!"
|
|
27
|
+
end
|
|
28
|
+
klass.new(obj, context)
|
|
29
|
+
else
|
|
30
|
+
nil
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def present_collection(obj, klass = nil, context = presenter_context, options = {})
|
|
35
|
+
obj.map { |o| present(o, klass, context, options) }
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def presenter_class(obj)
|
|
41
|
+
(obj.class.ancestors - obj.class.included_modules).each do |k|
|
|
42
|
+
klass = "#{k.name}Presenter".safe_constantize
|
|
43
|
+
return klass if klass
|
|
44
|
+
end
|
|
45
|
+
nil
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'delegate'
|
|
2
|
+
require 'gestart_showcase/helpers/present'
|
|
3
|
+
require 'gestart_showcase/helpers/module_method_builder'
|
|
4
|
+
require 'active_support/core_ext/array/extract_options'
|
|
5
|
+
require 'active_support/core_ext/hash/keys'
|
|
6
|
+
|
|
7
|
+
module GestartShowcase
|
|
8
|
+
class Presenter < SimpleDelegator
|
|
9
|
+
include Helpers::Present
|
|
10
|
+
extend Helpers::ModuleMethodBuilder
|
|
11
|
+
|
|
12
|
+
attr_reader :view_context
|
|
13
|
+
|
|
14
|
+
alias_method :object, :__getobj__
|
|
15
|
+
alias_method :h, :view_context
|
|
16
|
+
alias_method :try, :__send__
|
|
17
|
+
alias_method :__decorator_class__, :class
|
|
18
|
+
|
|
19
|
+
def initialize(obj, context)
|
|
20
|
+
super(obj)
|
|
21
|
+
@view_context = context
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def class
|
|
25
|
+
object.class
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def kind_of?(klass)
|
|
29
|
+
object.kind_of?(klass)
|
|
30
|
+
end
|
|
31
|
+
alias_method :is_a?, :kind_of?
|
|
32
|
+
|
|
33
|
+
def instance_of?(klass)
|
|
34
|
+
object.instance_of?(klass)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.presents(*args)
|
|
38
|
+
wrap_methods(args, :present)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def self.presents_collection(*args)
|
|
42
|
+
wrap_methods(args, :present_collection)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def self.wrap_methods(args, method)
|
|
48
|
+
options = args.extract_options!
|
|
49
|
+
options.assert_valid_keys(:with, :nil_presenter)
|
|
50
|
+
presenter_klass = options.fetch(:with, nil)
|
|
51
|
+
|
|
52
|
+
args.each do |attr|
|
|
53
|
+
define_module_method attr do
|
|
54
|
+
send(method,
|
|
55
|
+
object.send(attr),
|
|
56
|
+
presenter_klass,
|
|
57
|
+
view_context,
|
|
58
|
+
options.slice(:nil_presenter))
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'gestart_showcase/traits'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
class Railtie < Rails::Railtie
|
|
5
|
+
initializer "action_view.initialize_showcase" do
|
|
6
|
+
ActiveSupport.on_load(:action_view) do
|
|
7
|
+
ActionView::Base.send :include, GestartShowcase::Helpers::Present
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'gestart_showcase/helpers/config_object'
|
|
2
|
+
require 'gestart_showcase/helpers/html_options'
|
|
3
|
+
|
|
4
|
+
module GestartShowcase
|
|
5
|
+
module Traits
|
|
6
|
+
|
|
7
|
+
module Record
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
def box(&block)
|
|
12
|
+
@box_config_block = block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def __box_config_block__
|
|
16
|
+
@box_config_block
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def dom_id
|
|
21
|
+
record_identifier.dom_id(self)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def dom_class
|
|
25
|
+
record_identifier.dom_class(self)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def box(*args, &block)
|
|
29
|
+
options = args.extract_options!
|
|
30
|
+
tag = args.pop || :div
|
|
31
|
+
|
|
32
|
+
config_block = self.__decorator_class__.__box_config_block__
|
|
33
|
+
config_options = if config_block
|
|
34
|
+
Helpers::ConfigObject.new(self, &config_block).to_struct.html_options
|
|
35
|
+
else
|
|
36
|
+
{}
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
html_options = Helpers::HtmlOptions.new(config_options)
|
|
40
|
+
html_options.merge_attrs!(options)
|
|
41
|
+
html_options.add_class!(dom_class)
|
|
42
|
+
html_options.merge_attrs!(id: dom_id)
|
|
43
|
+
|
|
44
|
+
h.content_tag(tag, html_options.to_h) do
|
|
45
|
+
h.capture(self, &block)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
def record_identifier
|
|
52
|
+
if defined?(ActionView::RecordIdentifier)
|
|
53
|
+
ActionView::RecordIdentifier
|
|
54
|
+
elsif defined?(ActionController::RecordIdentifier)
|
|
55
|
+
ActionController::RecordIdentifier
|
|
56
|
+
else
|
|
57
|
+
raise 'No RecordIdentifier found!'
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
data/spec/fixtures.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
class RailsViewContext < ActionView::Base
|
|
2
|
+
include ActionView::Helpers::TagHelper
|
|
3
|
+
|
|
4
|
+
def initialize(*)
|
|
5
|
+
super(ActionView::LookupContext.new([]), {}, nil)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def capture(*args, &block)
|
|
9
|
+
block.call(*args)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Model
|
|
14
|
+
extend ActiveModel::Naming
|
|
15
|
+
|
|
16
|
+
def to_key
|
|
17
|
+
Array('1')
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
class Person < Struct.new(:name)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Customer < Person
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
class EnterpriseCustomer < Customer
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Project < Struct.new(:name)
|
|
31
|
+
def owner
|
|
32
|
+
Person.new("Stefano Verna")
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def collaborators
|
|
36
|
+
[ Person.new("Ju Liu") ]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def first_collaborator
|
|
40
|
+
collaborators.first
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def owner_child
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def foobar
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def dummy
|
|
50
|
+
"foobar"
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
class PersonPresenter < GestartShowcase::Presenter
|
|
55
|
+
def sex
|
|
56
|
+
'male'
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def bold_name
|
|
60
|
+
h.bold(object.name)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
class AdminPresenter < PersonPresenter
|
|
65
|
+
def bold_name
|
|
66
|
+
h.bold("Admin #{object.name}")
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
class ProjectPresenter < GestartShowcase::Presenter
|
|
71
|
+
presents :owner, with: AdminPresenter
|
|
72
|
+
presents_collection :collaborators
|
|
73
|
+
presents :owner_child
|
|
74
|
+
presents :foobar, nil_presenter: true
|
|
75
|
+
|
|
76
|
+
def name
|
|
77
|
+
"Presented #{object.name}"
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def owner
|
|
81
|
+
super
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def bold_name
|
|
85
|
+
h.bold(object.name)
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def first_collaborator
|
|
89
|
+
present(object.first_collaborator)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
class NilClassPresenter < GestartShowcase::Presenter
|
|
94
|
+
def hi
|
|
95
|
+
"hi!"
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
class Context
|
|
100
|
+
include GestartShowcase::Helpers::Present
|
|
101
|
+
|
|
102
|
+
def bold(text)
|
|
103
|
+
"**#{text}**"
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
class Shop
|
|
108
|
+
def owner
|
|
109
|
+
Person.new("Stefano Verna")
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
describe HtmlClassAttribute do
|
|
6
|
+
let(:attribute) { HtmlClassAttribute.new(string) }
|
|
7
|
+
let(:string) { nil }
|
|
8
|
+
|
|
9
|
+
describe '.to_html_attribute' do
|
|
10
|
+
subject { attribute.to_html_attribute }
|
|
11
|
+
|
|
12
|
+
context 'nil' do
|
|
13
|
+
let(:string) { nil }
|
|
14
|
+
it { should be_nil }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context 'empty' do
|
|
18
|
+
let(:string) { '' }
|
|
19
|
+
it { should be_nil }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
context 'whitespaces only' do
|
|
23
|
+
let(:string) { ' ' }
|
|
24
|
+
it { should be_nil }
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
context 'with trailing spaces' do
|
|
28
|
+
let(:string) { ' foo ' }
|
|
29
|
+
it { should eq 'foo' }
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
context 'with multiple classes' do
|
|
33
|
+
let(:string) { ' foo bar ' }
|
|
34
|
+
it { should eq 'foo bar' }
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
describe '#<<' do
|
|
39
|
+
before do
|
|
40
|
+
attribute << addition
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
subject { attribute.to_html_attribute }
|
|
44
|
+
|
|
45
|
+
context 'with no existing class attribute' do
|
|
46
|
+
let(:string) { nil }
|
|
47
|
+
let(:addition) { ' foo ' }
|
|
48
|
+
|
|
49
|
+
it { should eq 'foo' }
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
context 'with empty class attribute' do
|
|
53
|
+
let(:string) { '' }
|
|
54
|
+
let(:addition) { 'foo' }
|
|
55
|
+
|
|
56
|
+
it { should eq 'foo' }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
context 'with existing class attribute' do
|
|
60
|
+
let(:string) { 'bar' }
|
|
61
|
+
let(:addition) { 'foo' }
|
|
62
|
+
|
|
63
|
+
it { should eq 'bar foo' }
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
context 'with the same css class' do
|
|
67
|
+
let(:string) { 'foo bar' }
|
|
68
|
+
let(:addition) { 'foo' }
|
|
69
|
+
|
|
70
|
+
it { should eq 'foo bar' }
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
context 'multiple additions' do
|
|
74
|
+
let(:string) { 'foo bar' }
|
|
75
|
+
let(:addition) { 'foo qux' }
|
|
76
|
+
|
|
77
|
+
it { should eq 'foo bar qux' }
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
describe HtmlOptions do
|
|
6
|
+
let(:options) { HtmlOptions.new(attributes) }
|
|
7
|
+
let(:attributes) { {} }
|
|
8
|
+
|
|
9
|
+
it 'takes a hash of attributes' do
|
|
10
|
+
expect(options.to_h).to eq attributes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
describe '#merge_attrs!' do
|
|
14
|
+
let(:attributes) { { 'id' => 'bar', 'data-alert' => 'true' } }
|
|
15
|
+
|
|
16
|
+
it 'merges attributes with the passed ones' do
|
|
17
|
+
options.merge_attrs!('data-method' => 'post', 'id' => 'foo')
|
|
18
|
+
expect(options.to_h).to eq({
|
|
19
|
+
:'data-alert' => 'true',
|
|
20
|
+
:'data-method' => 'post',
|
|
21
|
+
:'id' => 'foo'
|
|
22
|
+
})
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
context 'css classes' do
|
|
26
|
+
let(:attributes) { { 'class' => 'foo bar' } }
|
|
27
|
+
|
|
28
|
+
it 'merges them appropriately' do
|
|
29
|
+
options.merge_attrs!('class' => 'foo qux')
|
|
30
|
+
expect(options.to_h).to eq(class: 'foo bar qux')
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase
|
|
4
|
+
module Helpers
|
|
5
|
+
describe Present do
|
|
6
|
+
let(:object) { Person.new('Steve Ballmer') }
|
|
7
|
+
let(:subclass_object) { EnterpriseCustomer.new('Tito Traves') }
|
|
8
|
+
let(:object_with_no_presenter) { Shop.new }
|
|
9
|
+
let(:context) { Context.new }
|
|
10
|
+
|
|
11
|
+
describe '.present' do
|
|
12
|
+
it 'instantiates a new presenter, inferring the presenter class' do
|
|
13
|
+
PersonPresenter.stub(:new).with(object, context).and_return 'Presenter'
|
|
14
|
+
context.present(object).should == 'Presenter'
|
|
15
|
+
end
|
|
16
|
+
context 'with a superclass presenter' do
|
|
17
|
+
it "instantiates a new presenter, searching presenter class in object ancestors chain" do
|
|
18
|
+
PersonPresenter.stub(:new).with(subclass_object, context).and_return 'Presenter'
|
|
19
|
+
context.present(subclass_object).should == 'Presenter'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
context 'with no existing presenter class' do
|
|
23
|
+
it "raises a PresenterClassNotFound error" do
|
|
24
|
+
expect {
|
|
25
|
+
context.present(object_with_no_presenter)
|
|
26
|
+
}.to raise_error(GestartShowcase::PresenterClassNotFound)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
it 'uses the specified presenter class, when passed' do
|
|
30
|
+
ProjectPresenter.stub(:new).with(object, context).and_return 'Presenter'
|
|
31
|
+
context.present(object, ProjectPresenter).should == 'Presenter'
|
|
32
|
+
end
|
|
33
|
+
it 'uses the specified context, when passed' do
|
|
34
|
+
different_context = double
|
|
35
|
+
context.present(object, ProjectPresenter, different_context).view_context.should == different_context
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
describe '.present_collection' do
|
|
40
|
+
it 'returns a presenter for each object in the collection' do
|
|
41
|
+
collection = [ Person.new('Mark'), Person.new('Luke') ]
|
|
42
|
+
|
|
43
|
+
PersonPresenter.stub(:new).with(collection[0], context).and_return 'foo'
|
|
44
|
+
PersonPresenter.stub(:new).with(collection[1], context).and_return 'bar'
|
|
45
|
+
|
|
46
|
+
presented_collection = context.present_collection(collection)
|
|
47
|
+
presented_collection.should == [ 'foo', 'bar' ]
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
describe GestartShowcase::Presenter do
|
|
4
|
+
let(:context) { Context.new }
|
|
5
|
+
let(:object) { Project.new('GestartShowcase') }
|
|
6
|
+
let(:subject) { ProjectPresenter.new(object, context) }
|
|
7
|
+
|
|
8
|
+
it 'takes the object and a context as parameters' do
|
|
9
|
+
subject.object.should == object
|
|
10
|
+
subject.view_context.should == context
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
it 'preserves original .class' do
|
|
14
|
+
subject.class.should == Project
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
it 'should be kind of original class' do
|
|
18
|
+
subject.should be_kind_of Project
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
it 'should be instance of original class' do
|
|
22
|
+
subject.should be_instance_of Project
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
it 'delegates methods to object' do
|
|
26
|
+
subject.dummy.should == 'foobar'
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'allows overriding of methods' do
|
|
30
|
+
subject.name.should == 'Presented GestartShowcase'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
it 'implements :try method the right way' do
|
|
34
|
+
subject.try(:name).should == 'Presented GestartShowcase'
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'allows .h as shortcut to access the context' do
|
|
38
|
+
subject.bold_name.should == '**GestartShowcase**'
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
describe '.present' do
|
|
42
|
+
it 'passes the context' do
|
|
43
|
+
subject.first_collaborator.bold_name.should == '**Ju Liu**'
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
describe '#presents' do
|
|
48
|
+
it 'wraps the specified attributes inside a presenter' do
|
|
49
|
+
subject.owner.sex.should == 'male'
|
|
50
|
+
end
|
|
51
|
+
it 'passes the context' do
|
|
52
|
+
subject.owner.bold_name.should == '**Admin Stefano Verna**'
|
|
53
|
+
end
|
|
54
|
+
it 'returns null if nil_presenter is false or undefined' do
|
|
55
|
+
subject.owner_child.should be_nil
|
|
56
|
+
end
|
|
57
|
+
it 'returns a null presenter if nil_presenter is true' do
|
|
58
|
+
subject.foobar.hi.should eq "hi!"
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
describe '#presents_collection' do
|
|
63
|
+
it 'wraps the specified collection attributes inside a presenter' do
|
|
64
|
+
subject.collaborators.first.sex.should == 'male'
|
|
65
|
+
end
|
|
66
|
+
it 'passes the context' do
|
|
67
|
+
subject.collaborators.first.bold_name.should == '**Ju Liu**'
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase::Traits
|
|
4
|
+
describe Record do
|
|
5
|
+
|
|
6
|
+
let(:view) { RailsViewContext.new }
|
|
7
|
+
let(:record) { Model.new }
|
|
8
|
+
let(:presenter) {
|
|
9
|
+
Class.new(GestartShowcase::Presenter) do
|
|
10
|
+
include Record
|
|
11
|
+
|
|
12
|
+
box do |c|
|
|
13
|
+
c.html_options role: 'actor', class: 'first'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
}
|
|
17
|
+
subject { presenter.new(record, view) }
|
|
18
|
+
|
|
19
|
+
describe '#dom_id' do
|
|
20
|
+
it 'returns a CSS ID for the model' do
|
|
21
|
+
expect(subject.dom_id).to eq 'model_1'
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
describe '#dom_class' do
|
|
26
|
+
it 'returns a CSS class for model' do
|
|
27
|
+
expect(subject.dom_class).to eq 'model'
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe '#box' do
|
|
32
|
+
it 'wraps content inside an element that uniquely identifies the record' do
|
|
33
|
+
result = subject.box { "foo" }
|
|
34
|
+
expect(result).to have_tag(:div, with: { class: 'model', id: 'model_1' })
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
it 'adds attributes specified within box block' do
|
|
38
|
+
result = subject.box { "foo" }
|
|
39
|
+
expect(result).to have_tag(:div, with: { role: 'actor', class: 'first' })
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
context 'with a specified tag' do
|
|
43
|
+
it 'uses it' do
|
|
44
|
+
result = subject.box(:span) { "foo" }
|
|
45
|
+
expect(result).to have_tag(:span)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
context 'with some class' do
|
|
50
|
+
it 'uses it' do
|
|
51
|
+
result = subject.box(class: 'bar') { "foo" }
|
|
52
|
+
expect(result).to have_tag(:div, with: { class: 'model bar'})
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
module GestartShowcase::Helpers
|
|
4
|
+
describe ConfigObject do
|
|
5
|
+
|
|
6
|
+
let(:context_class) {
|
|
7
|
+
Class.new do
|
|
8
|
+
def foo
|
|
9
|
+
'foo'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def bar
|
|
13
|
+
'bar'
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
}
|
|
17
|
+
let(:context) { context_class.new }
|
|
18
|
+
|
|
19
|
+
describe 'to_hash' do
|
|
20
|
+
it 'accepts attr=() notation' do
|
|
21
|
+
result = ConfigObject.new(context) do |c|
|
|
22
|
+
c.first = foo
|
|
23
|
+
c.second = bar
|
|
24
|
+
c.third = '3rd'
|
|
25
|
+
end.to_hash
|
|
26
|
+
|
|
27
|
+
hash = { first: 'foo', second: 'bar', third: '3rd' }
|
|
28
|
+
expect(result).to eq hash
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
it 'accepts attr() notation' do
|
|
32
|
+
result = ConfigObject.new(context) do |c|
|
|
33
|
+
c.first foo
|
|
34
|
+
c.second bar
|
|
35
|
+
c.third '3rd'
|
|
36
|
+
end.to_hash
|
|
37
|
+
|
|
38
|
+
hash = { first: 'foo', second: 'bar', third: '3rd' }
|
|
39
|
+
expect(result).to eq hash
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gestart-showcase
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Gestart Cloud S.r.l.
|
|
8
|
+
- Stefano Verna
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 1980-01-02 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: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ">="
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rspec
|
|
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: rails
|
|
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: rspec-html-matchers
|
|
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: ostruct
|
|
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
|
+
description: Gestart fork of showcase 0.2.5, pruned of what Gestart does not use
|
|
84
|
+
email:
|
|
85
|
+
- dev@gestart.net
|
|
86
|
+
executables: []
|
|
87
|
+
extensions: []
|
|
88
|
+
extra_rdoc_files: []
|
|
89
|
+
files:
|
|
90
|
+
- ".github/workflows/ci.yml"
|
|
91
|
+
- ".gitignore"
|
|
92
|
+
- ".rspec"
|
|
93
|
+
- ".tool-versions"
|
|
94
|
+
- CHANGELOG.md
|
|
95
|
+
- Gemfile
|
|
96
|
+
- LICENSE.txt
|
|
97
|
+
- README.md
|
|
98
|
+
- Rakefile
|
|
99
|
+
- gestart-showcase.gemspec
|
|
100
|
+
- lib/gestart-showcase.rb
|
|
101
|
+
- lib/gestart_showcase.rb
|
|
102
|
+
- lib/gestart_showcase/errors.rb
|
|
103
|
+
- lib/gestart_showcase/helpers/config_object.rb
|
|
104
|
+
- lib/gestart_showcase/helpers/html_class_attribute.rb
|
|
105
|
+
- lib/gestart_showcase/helpers/html_options.rb
|
|
106
|
+
- lib/gestart_showcase/helpers/module_method_builder.rb
|
|
107
|
+
- lib/gestart_showcase/helpers/present.rb
|
|
108
|
+
- lib/gestart_showcase/presenter.rb
|
|
109
|
+
- lib/gestart_showcase/railtie.rb
|
|
110
|
+
- lib/gestart_showcase/traits.rb
|
|
111
|
+
- lib/gestart_showcase/traits/record.rb
|
|
112
|
+
- lib/gestart_showcase/version.rb
|
|
113
|
+
- spec/fixtures.rb
|
|
114
|
+
- spec/gestart_showcase/helpers/html_class_attribute_spec.rb
|
|
115
|
+
- spec/gestart_showcase/helpers/html_options_spec.rb
|
|
116
|
+
- spec/gestart_showcase/helpers/present_spec.rb
|
|
117
|
+
- spec/gestart_showcase/presenter_spec.rb
|
|
118
|
+
- spec/gestart_showcase/traits/record_spec.rb
|
|
119
|
+
- spec/helpers/config_object_spec.rb
|
|
120
|
+
- spec/spec_helper.rb
|
|
121
|
+
homepage: https://github.com/gestartcloudsrl/gestart-showcase
|
|
122
|
+
licenses:
|
|
123
|
+
- MIT
|
|
124
|
+
metadata: {}
|
|
125
|
+
rdoc_options: []
|
|
126
|
+
require_paths:
|
|
127
|
+
- lib
|
|
128
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
129
|
+
requirements:
|
|
130
|
+
- - ">="
|
|
131
|
+
- !ruby/object:Gem::Version
|
|
132
|
+
version: '3.1'
|
|
133
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
|
+
requirements:
|
|
135
|
+
- - ">="
|
|
136
|
+
- !ruby/object:Gem::Version
|
|
137
|
+
version: '0'
|
|
138
|
+
requirements: []
|
|
139
|
+
rubygems_version: 3.6.9
|
|
140
|
+
specification_version: 4
|
|
141
|
+
summary: 'Gestart fork of showcase: the presenter layer behind every Gestart view'
|
|
142
|
+
test_files:
|
|
143
|
+
- spec/fixtures.rb
|
|
144
|
+
- spec/gestart_showcase/helpers/html_class_attribute_spec.rb
|
|
145
|
+
- spec/gestart_showcase/helpers/html_options_spec.rb
|
|
146
|
+
- spec/gestart_showcase/helpers/present_spec.rb
|
|
147
|
+
- spec/gestart_showcase/presenter_spec.rb
|
|
148
|
+
- spec/gestart_showcase/traits/record_spec.rb
|
|
149
|
+
- spec/helpers/config_object_spec.rb
|
|
150
|
+
- spec/spec_helper.rb
|