showcase 0.1.7 → 0.2.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rspec +2 -0
- data/.travis.yml +9 -1
- data/Appraisals +8 -0
- data/Gemfile +3 -0
- data/README.md +186 -21
- data/Rakefile +14 -1
- data/gemfiles/activerecord3.gemfile +8 -0
- data/gemfiles/activerecord4.gemfile +8 -0
- data/lib/generators/showcase/presenter/presenter_generator.rb +2 -0
- data/lib/generators/showcase/presenter/templates/presenter.rb +2 -1
- data/lib/showcase/helpers/config_object.rb +28 -0
- data/lib/showcase/helpers/html_options.rb +22 -0
- data/lib/showcase/helpers/seo_meta_builder.rb +56 -0
- data/lib/showcase/presenter.rb +1 -0
- data/lib/showcase/railtie.rb +12 -0
- data/lib/showcase/traits/base.rb +23 -0
- data/lib/showcase/traits/link_to.rb +37 -0
- data/lib/showcase/traits/record.rb +61 -0
- data/lib/showcase/traits/seo.rb +27 -0
- data/lib/showcase/traits/share.rb +61 -0
- data/lib/showcase/traits.rb +11 -0
- data/lib/showcase/version.rb +1 -1
- data/lib/showcase.rb +2 -0
- data/showcase.gemspec +3 -0
- data/spec/fixtures.rb +16 -0
- data/spec/helpers/config_object_spec.rb +45 -0
- data/spec/helpers/seo_meta_builder_spec.rb +95 -0
- data/spec/helpers_spec.rb +34 -0
- data/spec/{showcase_spec.rb → presenter_spec.rb} +1 -37
- data/spec/spec_helper.rb +12 -0
- data/spec/traits/base_spec.rb +56 -0
- data/spec/traits/link_to_spec.rb +123 -0
- data/spec/traits/record_spec.rb +59 -0
- data/spec/traits/seo_spec.rb +37 -0
- data/spec/traits/share_spec.rb +60 -0
- metadata +67 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3abceb896eb37c725738c997b0bddcb09e74605b
|
4
|
+
data.tar.gz: 8ad7e9cd6cc06b41bf2008f1e25a4116eed1e80f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 1ff5470207bd67524b1de59fa28e4bc9970c2488ed9df89392a8630f939e1f0d5bea6445528cbfbf9e9f3c631b4765f001cd4acccd45208c217fbf8978583ba0
|
7
|
+
data.tar.gz: 1454964cabb2b9f4be47fee3d041a3f3cdcbe061375a29de485d96bb10ae564c23100ea06643c92bcda3589f692f1ce74e0fa5b3356746b2c2676ad8f4c080fa
|
data/.rspec
ADDED
data/.travis.yml
CHANGED
data/Appraisals
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,17 +1,15 @@
|
|
1
|
-
# Showcase [![Build Status](https://travis-ci.org/
|
1
|
+
# Showcase [![Build Status](https://travis-ci.org/stefanoverna/showcase.png?branch=master)](https://travis-ci.org/stefanoverna/showcase) [![Coverage Status](https://coveralls.io/repos/stefanoverna/showcase/badge.png?branch=master)](https://coveralls.io/r/stefanoverna/showcase)
|
2
2
|
|
3
|
-
A simple (< 100 lines of code) but powerful exhibit/presenter implementation.
|
3
|
+
A simple (< 100 lines of code) but powerful exhibit/presenter implementation.
|
4
|
+
It's framework agnostic: works with Rails, Padrino or just Sinatra.
|
4
5
|
|
5
|
-
|
6
|
+
Since version 0.2.0 Showcase is bundled with [a set of "traits"](https://github.com/stefanoverna/showcase#traits)
|
7
|
+
you can pick and choose to augment your presenters with additional sugar
|
8
|
+
(available in Rails 3+ only).
|
6
9
|
|
7
|
-
|
10
|
+
## Why should I use presenters in my Rails app?
|
8
11
|
|
9
|
-
|
10
|
-
* **It is a true Decorator.** All unrecognized messages are passed through to the underlying object. This facilitates a gradual migration to the use of Exhibits to encapsulate presentation knowledge, since they can be layered onto models without any change to the existing views. It also enables multiple Exhibits to be layered onto an object, each handling different aspects of presentation.
|
11
|
-
* **It brings together a model and a context.** Exhibits need a reference to a “context” object—either a controller or a view context—in order to be able to render templates as well as construct URLs for the object or related resources.
|
12
|
-
* **It encapsulates decisions about how to render an object.** The tell-tale of an Exhibit is telling an object “render yourself”, rather than explicitly rendering a template and passing the object in as an argument.
|
13
|
-
* **It may modify the behavior of an object.** For instance, an Exhibit might impose a scope on a `Blog#entries` association which only returns entries that are visible to the current user (as determined from the Exhibit’s controller context). Or it might reformat the return value of a `#social_security_number` method to include dashes and have all but the last four digits obscured: `***-**-5678`.
|
14
|
-
* **There is a many-to-many relationship between model classes and exhibit classes.** One generic exhibit class may apply to several different types of model. Other exhibits may be specific, not just to a model, but to a model in a particular state, or within a particular viewing context.
|
12
|
+
See [Avdi's Exhibits introductory post](http://devblog.avdi.org/2012/06/04/displaycase-gem-now-available/).
|
15
13
|
|
16
14
|
## Installation
|
17
15
|
|
@@ -29,15 +27,8 @@ Or install it yourself as:
|
|
29
27
|
|
30
28
|
## Usage
|
31
29
|
|
32
|
-
With Rails, include `Showcase::Helpers`
|
33
|
-
|
34
|
-
```ruby
|
35
|
-
# config/initializers/showcase.rb
|
36
|
-
ActionController::Base.send :include, Showcase::Helpers
|
37
|
-
ActionView::Base.send :include, Showcase::Helpers
|
38
|
-
```
|
39
|
-
|
40
|
-
With Padrino, include `Showcase::Helpers` in your app `helpers` block.
|
30
|
+
With Rails, you're already set, move on! With Padrino, include `Showcase::Helpers`
|
31
|
+
in your app `helpers` block.
|
41
32
|
|
42
33
|
```ruby
|
43
34
|
helpers do
|
@@ -45,7 +36,8 @@ helpers do
|
|
45
36
|
end
|
46
37
|
```
|
47
38
|
|
48
|
-
You can now instantiate new presenters in your controller/views using the
|
39
|
+
You can now instantiate new presenters in your controller/views using the
|
40
|
+
included helpers:
|
49
41
|
|
50
42
|
```ruby
|
51
43
|
# this is the object that needs to be presented
|
@@ -74,7 +66,8 @@ class ProjectPresenter < Showcase::Presenter
|
|
74
66
|
# automatically wraps the attribute into an AdminPresenter
|
75
67
|
presents :person, with: AdminPresenter
|
76
68
|
|
77
|
-
# expects project.task to return an enumerable. automatically wraps each task
|
69
|
+
# expects project.task to return an enumerable. automatically wraps each task
|
70
|
+
# in a TaskPresenter presenter
|
78
71
|
presents_collection :tasks
|
79
72
|
|
80
73
|
# you can use `view_context`, or the shortcut `h`, to access the context.
|
@@ -85,6 +78,178 @@ class ProjectPresenter < Showcase::Presenter
|
|
85
78
|
end
|
86
79
|
```
|
87
80
|
|
81
|
+
## Rails
|
82
|
+
|
83
|
+
### Generators
|
84
|
+
|
85
|
+
Showcase comes with a generator to create new presenters a little faster:
|
86
|
+
|
87
|
+
```
|
88
|
+
rails generate showcase:presenter User
|
89
|
+
```
|
90
|
+
|
91
|
+
Will generate `app/presenters/user_presenter.rb`. If your Rails app has the file
|
92
|
+
`app/presenters/base_presenter.rb`, the newly created presenter will inherit
|
93
|
+
from `BasePresenter` instead of `Shocase::Presenter`.
|
94
|
+
|
95
|
+
### Traits
|
96
|
+
|
97
|
+
Please [read the tests](https://github.com/stefanoverna/showcase/tree/master/lib/showcase/traits)
|
98
|
+
for a detailed explanation of each method available.
|
99
|
+
|
100
|
+
#### `Showcase::Traits::Record`
|
101
|
+
|
102
|
+
To be used to present ActiveModel-based records. Inside your presenter, include
|
103
|
+
the trait like this:
|
104
|
+
|
105
|
+
```ruby
|
106
|
+
class ProjectPresenter < Showcase::Presenter
|
107
|
+
include Showcase::Traits::Record
|
108
|
+
end
|
109
|
+
```
|
110
|
+
|
111
|
+
##### `#dom_id`
|
112
|
+
|
113
|
+
```ruby
|
114
|
+
present(@project).dom_id # => "project_12"
|
115
|
+
```
|
116
|
+
|
117
|
+
##### `#dom_class`
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
present(@project).dom_class # => "project"
|
121
|
+
```
|
122
|
+
|
123
|
+
##### `#box`
|
124
|
+
|
125
|
+
Super useful in acceptance testing to check the presence of a record inside a
|
126
|
+
view:
|
127
|
+
|
128
|
+
```erb
|
129
|
+
<% present(@project).box do %>
|
130
|
+
<p>Hi there!</p>
|
131
|
+
<% end %>
|
132
|
+
```
|
133
|
+
|
134
|
+
Produces the following:
|
135
|
+
|
136
|
+
```html
|
137
|
+
<div class="project" id="project_12">
|
138
|
+
<p>Hi there</p>
|
139
|
+
</div>
|
140
|
+
|
141
|
+
Additional HTML attributes can be optionally specified within a config block
|
142
|
+
inside the presenter:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
class ProjectPresenter < Showcase::Presenter
|
146
|
+
include Showcase::Traits::Record
|
147
|
+
|
148
|
+
box do |c|
|
149
|
+
c.html_options class: 'another-class', role: 'project'
|
150
|
+
end
|
151
|
+
end
|
152
|
+
```
|
153
|
+
|
154
|
+
#### `Showcase::Traits::LinkTo`
|
155
|
+
|
156
|
+
Adds a nice DSL to declare links within your presenter.
|
157
|
+
|
158
|
+
```ruby
|
159
|
+
class ProjectPresenter < Showcase::Presenter
|
160
|
+
include Showcase::Traits::LinkTo
|
161
|
+
|
162
|
+
link_to do |c|
|
163
|
+
c.url h.project_path(self)
|
164
|
+
c.label name
|
165
|
+
c.active h.controller_name == 'projects'
|
166
|
+
c.active_class 'current'
|
167
|
+
c.html_options role: 'label'
|
168
|
+
end
|
169
|
+
|
170
|
+
link_to :tasks do
|
171
|
+
c.url h.project_tasks_path(self)
|
172
|
+
c.label "Tasks"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
```
|
176
|
+
|
177
|
+
In your views:
|
178
|
+
|
179
|
+
```erb
|
180
|
+
<%= project.url %>
|
181
|
+
<%= project.tasks_url %>
|
182
|
+
|
183
|
+
<%= project.link_active? %>
|
184
|
+
<%= project.tasks_link_active? %>
|
185
|
+
|
186
|
+
<%= project.link %>
|
187
|
+
<%= project.tasks_link %>
|
188
|
+
|
189
|
+
<%= project.link('Alternative label') %>
|
190
|
+
<%= project.link(class: 'additional_class') %>
|
191
|
+
<%= project.link do %>
|
192
|
+
Link content!
|
193
|
+
<% end %>
|
194
|
+
```
|
195
|
+
|
196
|
+
#### `Showcase::Traits::Share`
|
197
|
+
|
198
|
+
Useful to produce social share links:
|
199
|
+
|
200
|
+
```ruby
|
201
|
+
class ProjectPresenter < Showcase::Presenter
|
202
|
+
include Showcase::Traits::Share
|
203
|
+
|
204
|
+
share do |c|
|
205
|
+
c.url h.project_path(self)
|
206
|
+
c.text name
|
207
|
+
c.image_url cover_image
|
208
|
+
end
|
209
|
+
end
|
210
|
+
```
|
211
|
+
In your views:
|
212
|
+
|
213
|
+
```erb
|
214
|
+
<%= project.twitter_share_url %>
|
215
|
+
<%= project.twitter_share_link %>
|
216
|
+
<%= project.twitter_share_link('Alternative label') %>
|
217
|
+
<%= project.twitter_share_link(class: 'additional_class') %>
|
218
|
+
<%= project.twitter_share_link do %>
|
219
|
+
Link content!
|
220
|
+
<% end %>
|
221
|
+
|
222
|
+
<%= project.facebook_share_link %>
|
223
|
+
<%= project.gplus_share_link %>
|
224
|
+
<%= project.pinterest_share_link %>
|
225
|
+
<%= project.linkedin_share_link %>
|
226
|
+
```
|
227
|
+
|
228
|
+
#### `Showcase::Traits::Seo`
|
229
|
+
|
230
|
+
Useful to produce SEO meta tags (title, description, Facebook OpenGraph,
|
231
|
+
Twitter cards, and canonical URL):
|
232
|
+
|
233
|
+
```ruby
|
234
|
+
class ProjectPresenter < Showcase::Presenter
|
235
|
+
include Showcase::Traits::Seo
|
236
|
+
|
237
|
+
seo do |c|
|
238
|
+
c.title name
|
239
|
+
c.description [ description, 'Fallback description if blank' ]
|
240
|
+
c.image_url cover_thumb_image
|
241
|
+
c.canonical_url h.project_path(self)
|
242
|
+
end
|
243
|
+
end
|
244
|
+
```
|
245
|
+
In your views:
|
246
|
+
|
247
|
+
```erb
|
248
|
+
<% content_for(:head) do %>
|
249
|
+
<%= present(@project).seo_tags(title_suffix: ' - BaseClump') %>
|
250
|
+
<% end %>
|
251
|
+
```
|
252
|
+
|
88
253
|
## Contributing
|
89
254
|
|
90
255
|
1. Fork it
|
data/Rakefile
CHANGED
@@ -1 +1,14 @@
|
|
1
|
-
require
|
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
|
+
|
@@ -4,8 +4,10 @@ module Showcase
|
|
4
4
|
source_root File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
5
5
|
|
6
6
|
def create_presenter
|
7
|
+
@base_presenter = File.exist?('app/presenters/base_presenter.rb') ? "BasePresenter" : "Showcase::Presenter"
|
7
8
|
template 'presenter.rb', File.join('app/presenters', class_path, "#{file_name}_presenter.rb")
|
8
9
|
end
|
9
10
|
end
|
10
11
|
end
|
11
12
|
end
|
13
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
module Showcase
|
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,22 @@
|
|
1
|
+
class HtmlOptions
|
2
|
+
def initialize(options = {})
|
3
|
+
@options = (options || {}).symbolize_keys
|
4
|
+
end
|
5
|
+
|
6
|
+
def add_class!(css_class)
|
7
|
+
@options[:class] ||= ""
|
8
|
+
css_classes = @options[:class].split(/\s+/)
|
9
|
+
css_classes << css_class
|
10
|
+
@options[:class] = css_classes.join(" ")
|
11
|
+
end
|
12
|
+
|
13
|
+
def merge_attrs!(options = {})
|
14
|
+
options = (options || {}).symbolize_keys
|
15
|
+
@options.merge!(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_h
|
19
|
+
@options.dup
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Showcase
|
2
|
+
module Helpers
|
3
|
+
class SeoMetaBuilder
|
4
|
+
attr_reader :context
|
5
|
+
|
6
|
+
def initialize(context)
|
7
|
+
@context = context
|
8
|
+
end
|
9
|
+
|
10
|
+
def title(values, options = {})
|
11
|
+
title = first_nonblank(values)
|
12
|
+
title += options[:title_suffix] if options[:title_suffix]
|
13
|
+
|
14
|
+
context.content_tag(:title, title) <<
|
15
|
+
seo_meta_tags(:og_title, :twitter_title, values)
|
16
|
+
end
|
17
|
+
|
18
|
+
def description(values, options = {})
|
19
|
+
seo_meta_tags(:description, :og_description, :twitter_description, values)
|
20
|
+
end
|
21
|
+
|
22
|
+
def image_url(image_url, options = {})
|
23
|
+
seo_meta_tags(:og_image, :twitter_image, image_url)
|
24
|
+
end
|
25
|
+
|
26
|
+
def canonical_url(url, options = {})
|
27
|
+
seo_meta_tags(:og_url, url) <<
|
28
|
+
context.tag(:link, rel: "canonical", "href" => url)
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def first_nonblank(values)
|
34
|
+
Array(values).map(&:presence).compact.first
|
35
|
+
end
|
36
|
+
|
37
|
+
def seo_meta_tags(*args)
|
38
|
+
value = first_nonblank(args.pop)
|
39
|
+
|
40
|
+
return nil unless value.present?
|
41
|
+
|
42
|
+
args.map do |name|
|
43
|
+
chunks = name.to_s.split("_")
|
44
|
+
attr_name = if chunks.first == 'og'
|
45
|
+
'property'
|
46
|
+
else
|
47
|
+
'name'
|
48
|
+
end
|
49
|
+
name = chunks.join(':')
|
50
|
+
context.tag(:meta, attr_name => name, content: value)
|
51
|
+
end.join.html_safe
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
data/lib/showcase/presenter.rb
CHANGED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Showcase
|
2
|
+
module Traits
|
3
|
+
module Base
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
|
6
|
+
module ClassMethods
|
7
|
+
private
|
8
|
+
|
9
|
+
def define_method?(name_chunks, &block)
|
10
|
+
method_name = Array(name_chunks).map(&:to_s).map(&:presence).compact.join("_")
|
11
|
+
if method_defined?(method_name)
|
12
|
+
false
|
13
|
+
else
|
14
|
+
define_method(method_name, &block)
|
15
|
+
true
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'showcase/helpers/config_object'
|
2
|
+
require 'showcase/helpers/html_options'
|
3
|
+
|
4
|
+
module Showcase
|
5
|
+
module Traits
|
6
|
+
|
7
|
+
module LinkTo
|
8
|
+
extend ActiveSupport::Concern
|
9
|
+
include Base
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
def link_to(name = nil, &block)
|
13
|
+
define_method? [name, :url] do
|
14
|
+
Helpers::ConfigObject.new(self, &block).to_struct.url
|
15
|
+
end
|
16
|
+
|
17
|
+
define_method? [name, :link_active?] do
|
18
|
+
Helpers::ConfigObject.new(self, &block).to_struct.active
|
19
|
+
end
|
20
|
+
|
21
|
+
define_method? [name, :link] do |*args, &link_block|
|
22
|
+
config = Helpers::ConfigObject.new(self, &block).to_struct
|
23
|
+
|
24
|
+
html_options = HtmlOptions.new(config.html_options)
|
25
|
+
html_options.merge_attrs!(args.extract_options!)
|
26
|
+
html_options.add_class!(config.active_class || 'active') if config.active
|
27
|
+
|
28
|
+
args = Array(config.label) if args.empty? && !link_block
|
29
|
+
h.link_to *args, config.url, html_options.to_h, &link_block
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Showcase
|
2
|
+
module Traits
|
3
|
+
|
4
|
+
module Record
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def box(&block)
|
9
|
+
@box_config_block = block
|
10
|
+
end
|
11
|
+
|
12
|
+
def __box_config_block__
|
13
|
+
@box_config_block
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def dom_id
|
18
|
+
record_identifier.dom_id(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
def dom_class
|
22
|
+
record_identifier.dom_class(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def box(*args, &block)
|
26
|
+
options = args.extract_options!
|
27
|
+
tag = args.pop || :div
|
28
|
+
|
29
|
+
config_block = self.__decorator_class__.__box_config_block__
|
30
|
+
config_options = if config_block
|
31
|
+
Helpers::ConfigObject.new(self, &config_block).to_struct.html_options
|
32
|
+
else
|
33
|
+
{}
|
34
|
+
end
|
35
|
+
|
36
|
+
html_options = HtmlOptions.new(config_options)
|
37
|
+
html_options.merge_attrs!(options)
|
38
|
+
html_options.add_class!(dom_class)
|
39
|
+
html_options.merge_attrs!(id: dom_id)
|
40
|
+
|
41
|
+
h.content_tag(tag, html_options.to_h) do
|
42
|
+
h.capture(self, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def record_identifier
|
49
|
+
if defined?(ActionView::RecordIdentifier)
|
50
|
+
ActionView::RecordIdentifier
|
51
|
+
elsif defined?(ActionController::RecordIdentifier)
|
52
|
+
ActionController::RecordIdentifier
|
53
|
+
else
|
54
|
+
raise 'No RecordIdentifier found!'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'showcase/helpers/config_object'
|
2
|
+
require 'showcase/helpers/seo_meta_builder'
|
3
|
+
|
4
|
+
module Showcase
|
5
|
+
module Traits
|
6
|
+
module Seo
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
include Base
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
|
12
|
+
def seo(name = nil, options = {}, &block)
|
13
|
+
define_method? [name, :seo_tags] do |options = {}|
|
14
|
+
meta = Helpers::ConfigObject.new(self, &block).to_hash
|
15
|
+
builder = Helpers::SeoMetaBuilder.new(view_context)
|
16
|
+
parts = %w(title description canonical_url image_url canonical_url).map(&:to_sym)
|
17
|
+
parts.map do |tag|
|
18
|
+
builder.send(tag, meta[tag], options) if meta[tag]
|
19
|
+
end.compact.join.html_safe
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|