excalibur 0.0.2
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/.gitignore +22 -0
- data/.rspec +3 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +162 -0
- data/Rakefile +2 -0
- data/excalibur.gemspec +32 -0
- data/lib/excalibur/configuration.rb +77 -0
- data/lib/excalibur/decorator.rb +55 -0
- data/lib/excalibur/duplicator.rb +19 -0
- data/lib/excalibur/railtie.rb +15 -0
- data/lib/excalibur/truncatable_content.rb +82 -0
- data/lib/excalibur/version.rb +4 -0
- data/lib/excalibur/view_helpers.rb +59 -0
- data/lib/excalibur.rb +91 -0
- data/lib/generators/excalibur/decorator_generator.rb +18 -0
- data/lib/generators/excalibur/install_generator.rb +18 -0
- data/lib/generators/templates/decorator.rb +75 -0
- data/lib/generators/templates/excalibur.rb +151 -0
- data/spec/lib/excalibur/configuration_spec.rb +215 -0
- data/spec/lib/excalibur/decorator_spec.rb +224 -0
- data/spec/lib/excalibur/duplicator_spec.rb +49 -0
- data/spec/lib/excalibur/truncatable_content_spec.rb +192 -0
- data/spec/lib/excalibur/view_helper_spec.rb +87 -0
- data/spec/lib/excalibur_spec.rb +85 -0
- data/spec/lib/generators/excalibur/decorator_generator_spec.rb +28 -0
- data/spec/lib/generators/excalibur/install_generator_spec.rb +30 -0
- data/spec/spec_helper.rb +80 -0
- metadata +219 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 3a2f5e3f336d2c2b9cd05b21f50387fca47779dd
|
4
|
+
data.tar.gz: ef5f966e21a9f27b7c23db57533d5c6f6dece688
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 51506887560a0d4771933ef36713ca814c96ed4c2c12fc2ba31cdafd214a7b982c15d0df47a9d33c70bef15e5f03bc50bd5f6086bc55aa1439b5b3a803eda9c3
|
7
|
+
data.tar.gz: eefbce53136c980ee7be535160ed801efc794052bb84686bfcde9152bc35319af85034fc059b19cba6ad9f2383db0d657a28cea935eea4ea6a0896cd62c3623a
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
*.gem
|
2
|
+
*.rbc
|
3
|
+
.bundle
|
4
|
+
.config
|
5
|
+
.yardoc
|
6
|
+
Gemfile.lock
|
7
|
+
InstalledFiles
|
8
|
+
_yardoc
|
9
|
+
coverage
|
10
|
+
doc/
|
11
|
+
lib/bundler/man
|
12
|
+
pkg
|
13
|
+
rdoc
|
14
|
+
spec/reports
|
15
|
+
test/tmp
|
16
|
+
test/version_tmp
|
17
|
+
tmp
|
18
|
+
*.bundle
|
19
|
+
*.so
|
20
|
+
*.o
|
21
|
+
*.a
|
22
|
+
mkmf.log
|
data/.rspec
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Joost (yopefonic) Elfering
|
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,162 @@
|
|
1
|
+
# Excalibur
|
2
|
+
|
3
|
+
Excalibur is a SEO gem for [Ruby on Rails](rubyonrails.org) and helps you to
|
4
|
+
set the title and meta tags for you site overall and per page. Unlike other
|
5
|
+
options like [meta-tags](https://github.com/kpumuk/meta-tags) and
|
6
|
+
[meta_magic](https://github.com/lassebunk/metamagic) Excalibur focusses on
|
7
|
+
providing an object based DSL to turn the objects you are presenting on the
|
8
|
+
page into SEO related tags in the head.
|
9
|
+
|
10
|
+
## Installation
|
11
|
+
|
12
|
+
Add this line to your application's Gemfile:
|
13
|
+
|
14
|
+
gem 'excalibur'
|
15
|
+
|
16
|
+
And then execute:
|
17
|
+
|
18
|
+
$ bundle
|
19
|
+
|
20
|
+
Or install it yourself as:
|
21
|
+
|
22
|
+
$ gem install excalibur
|
23
|
+
|
24
|
+
### Initialize
|
25
|
+
|
26
|
+
To add an initializer to your application execute:
|
27
|
+
|
28
|
+
rails generate excalibur:install
|
29
|
+
|
30
|
+
The initializer is documented with the possible options you have to customize
|
31
|
+
your default setup of Excalibur.
|
32
|
+
|
33
|
+
## Usage
|
34
|
+
|
35
|
+
### Layout
|
36
|
+
|
37
|
+
In your layout you can replace your title tag with the Excalibur render title
|
38
|
+
method:
|
39
|
+
|
40
|
+
```erb
|
41
|
+
<%= render_title_tag %>
|
42
|
+
```
|
43
|
+
|
44
|
+
To render meta tags place this between the ```<head>``` tags of your layout:
|
45
|
+
|
46
|
+
```erb
|
47
|
+
<%= render_meta_tags %>
|
48
|
+
```
|
49
|
+
|
50
|
+
### Views
|
51
|
+
|
52
|
+
As excalibur believes in separation of concerns passing the current object you
|
53
|
+
want to render title and meta tags for is done in the view.
|
54
|
+
|
55
|
+
```erb
|
56
|
+
<% entitle @your_object %>
|
57
|
+
```
|
58
|
+
|
59
|
+
The object your passing into the entitle method will search for a Excalibur
|
60
|
+
decorator. You can find out more on how to create them in the
|
61
|
+
[decorators](#decorators) section.
|
62
|
+
|
63
|
+
To modify the configuration in the view you can pass an option into the
|
64
|
+
```entitle``` method and pass it a new excalibur config object. Read more
|
65
|
+
about creating your own in the [configurations](#configurations) section. It
|
66
|
+
will try and merge the supplied configuration with the one specified in the
|
67
|
+
decorator. Because of this you need to supply it with the same type of object
|
68
|
+
to make that possible.
|
69
|
+
|
70
|
+
Notably the second argument of ```entitle``` can also pass other
|
71
|
+
[Draper](https://github.com/drapergem/draper) related options like draper's
|
72
|
+
context.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
entitle @your_object, config: @custom_config
|
76
|
+
```
|
77
|
+
|
78
|
+
### Decorators
|
79
|
+
|
80
|
+
The decorators are what turns your object into data that can be used for the
|
81
|
+
meta tags and the title. So without a decorator for an object the view helper
|
82
|
+
``` entitle ``` will have no use at all.
|
83
|
+
|
84
|
+
To scaffold a decorator execute:
|
85
|
+
|
86
|
+
rails generate excalibur:install [class name]
|
87
|
+
|
88
|
+
A detailed description on how the decorators work can be found in the newly
|
89
|
+
created decorator class. As a side note the ```Excalibur::Decorator``` is a
|
90
|
+
subclass of ```Draper::Decorator``` and comes with the free functionality
|
91
|
+
provided by [Draper](https://github.com/drapergem/draper). Have fun!
|
92
|
+
|
93
|
+
### Configurations
|
94
|
+
|
95
|
+
**WARNING:** handle with care!
|
96
|
+
|
97
|
+
So you want to roll your own configuration, awesome!
|
98
|
+
```Excalibur::Configuration``` can be initialized with 3 arguments that are
|
99
|
+
the title, description and meta tags.
|
100
|
+
|
101
|
+
Both title and description are an instance of
|
102
|
+
```Excalibur::TruncateableContent``` but as long as you make sure a
|
103
|
+
```.to_s``` with one argument is present for the object you put in there it is
|
104
|
+
fine. The object passed to it is either a decorated object when it is set or a
|
105
|
+
blank decorated object.
|
106
|
+
|
107
|
+
```Excalibur::TruncateableContent``` can be of course used on it's own when
|
108
|
+
creating a new configuration. It also takes 3 arguments. Two hashes; the first
|
109
|
+
for the content and the second for options. On it's own the
|
110
|
+
TruncateableContent does not do a lot but the third argument takes a
|
111
|
+
```Proc``` that is called with the decorator on render. By default the proc
|
112
|
+
combines the content according to the content and the options supplied in the
|
113
|
+
first two hashes. But of course you can supply your own. Setting them to empty
|
114
|
+
hashes or nil will work and when merged the default will keep in place.
|
115
|
+
|
116
|
+
The meta tags, and third argument of the configuration is a structured hash
|
117
|
+
with two layers. The first layer is the global type of meta tag and the second
|
118
|
+
contains the sub-types with the content.
|
119
|
+
|
120
|
+
so a meta tag like this:
|
121
|
+
|
122
|
+
```html
|
123
|
+
<meta name="description" content="Excalibur is cool" />
|
124
|
+
```
|
125
|
+
|
126
|
+
you need a hash like this:
|
127
|
+
|
128
|
+
```ruby
|
129
|
+
{
|
130
|
+
name: {
|
131
|
+
description: 'Excalibur is cool'
|
132
|
+
}
|
133
|
+
}
|
134
|
+
```
|
135
|
+
|
136
|
+
For an OpenGraph tag this would be something like:
|
137
|
+
|
138
|
+
```html
|
139
|
+
<meta property="og:description" content="Excalibur is cool" />
|
140
|
+
```
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
{
|
144
|
+
property: {
|
145
|
+
'og:description' => 'Excalibur is cool'
|
146
|
+
}
|
147
|
+
}
|
148
|
+
```
|
149
|
+
|
150
|
+
The content of the meta_tags can be defined as an Array of items or just a
|
151
|
+
single one. The item itself can be any type of variable that render as a
|
152
|
+
string on a page. It can also be a ```Proc``` that will be passed the
|
153
|
+
decorated object just like the title and description. This Proc can also
|
154
|
+
result in an array.
|
155
|
+
|
156
|
+
## Contributing
|
157
|
+
|
158
|
+
1. Fork it ( https://github.com/yopefonic/excalibur/fork )
|
159
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
160
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
161
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
162
|
+
5. Create a new Pull Request
|
data/Rakefile
ADDED
data/excalibur.gemspec
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'excalibur/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'excalibur'
|
8
|
+
spec.version = Excalibur::VERSION
|
9
|
+
spec.authors = ['Joost Elfering']
|
10
|
+
spec.email = ['yopefonic@gmail.com']
|
11
|
+
spec.summary = %q{helper gem to set page title and meta tags in a rails app}
|
12
|
+
spec.description = %q{helper gem for rails apps to set the title and meta tags for a page. The helper can take custom input per page, objects that use decorator to determine the tags or an application wide configurable default.}
|
13
|
+
spec.homepage = 'https://github.com/yopefonic/excalibur'
|
14
|
+
spec.license = 'MIT'
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0")
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ['lib']
|
20
|
+
|
21
|
+
spec.required_ruby_version = '>= 1.9.3'
|
22
|
+
|
23
|
+
spec.add_dependency 'rails', '~> 3'
|
24
|
+
spec.add_dependency 'draper', '~> 1.3', '>= 1.3.0'
|
25
|
+
|
26
|
+
spec.add_development_dependency 'bundler', '~> 1.6'
|
27
|
+
spec.add_development_dependency 'rake', '~> 10.3'
|
28
|
+
spec.add_development_dependency 'rspec', '~> 3.0'
|
29
|
+
spec.add_development_dependency 'generator_spec', '~> 0.9', '>= 0.9.2'
|
30
|
+
spec.add_development_dependency 'simplecov', '~> 0.8', '>= 0.8.0'
|
31
|
+
spec.add_development_dependency 'pry', '~> 0.10', '>= 0.10.1'
|
32
|
+
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
module Excalibur
|
2
|
+
# the Configuration class is responsible for holding the configurable data
|
3
|
+
# and logic that will be passed down
|
4
|
+
class Configuration
|
5
|
+
include Duplicator
|
6
|
+
|
7
|
+
attr_accessor :title
|
8
|
+
attr_accessor :description
|
9
|
+
attr_accessor :meta_tags
|
10
|
+
|
11
|
+
def initialize(
|
12
|
+
title = TruncateableContent.new,
|
13
|
+
description = TruncateableContent.new,
|
14
|
+
meta_tags = ::HashWithIndifferentAccess.new({}))
|
15
|
+
@title = title
|
16
|
+
@description = description
|
17
|
+
@meta_tags = meta_tags
|
18
|
+
end
|
19
|
+
|
20
|
+
def merge!(obj)
|
21
|
+
if obj.is_a? Configuration
|
22
|
+
@title = merge_instance(@title, obj.title)
|
23
|
+
@description = merge_instance(@description, obj.description)
|
24
|
+
@meta_tags = merge_instance(@meta_tags, obj.meta_tags)
|
25
|
+
|
26
|
+
self
|
27
|
+
else
|
28
|
+
fail(TypeError.new(true),
|
29
|
+
'can only merge two Excalibur::Configuration objects')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def dup
|
34
|
+
self.class.new(
|
35
|
+
dup_instance(@title),
|
36
|
+
dup_instance(@description.dup),
|
37
|
+
dup_instance(@meta_tags.dup))
|
38
|
+
end
|
39
|
+
|
40
|
+
def set_meta_tag(type, name, value = nil)
|
41
|
+
if @meta_tags[type].nil?
|
42
|
+
@meta_tags[type] = ::HashWithIndifferentAccess.new
|
43
|
+
end
|
44
|
+
|
45
|
+
@meta_tags[type][name] = value
|
46
|
+
end
|
47
|
+
|
48
|
+
def remove_meta_tag(type, name)
|
49
|
+
@meta_tags[type].delete(name) if @meta_tags[type].present?
|
50
|
+
@meta_tags.delete(type) if @meta_tags[type].empty?
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def merge_instance(old, new)
|
56
|
+
if old.is_a? new.class
|
57
|
+
merge_content(old, new)
|
58
|
+
elsif !new.nil?
|
59
|
+
new
|
60
|
+
else
|
61
|
+
old
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def merge_content(old, new)
|
66
|
+
if old.is_a? ::Hash
|
67
|
+
old.deep_merge!(new)
|
68
|
+
elsif old.is_a? TruncateableContent
|
69
|
+
old.merge!(new)
|
70
|
+
elsif old.is_a? ::String
|
71
|
+
old + new
|
72
|
+
else
|
73
|
+
new
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
require 'draper'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
# the Decorator class helps content to be derived from application base
|
5
|
+
# object and turn them into Excalibur titles and meta tags. It's main
|
6
|
+
# responsibilities are to make local classes configurable and it acts as a
|
7
|
+
# connector between the application's objects and Excalibur.
|
8
|
+
class Decorator < ::Draper::Decorator
|
9
|
+
delegate_all
|
10
|
+
|
11
|
+
attr_accessor :custom_configuration
|
12
|
+
|
13
|
+
class << self
|
14
|
+
attr_writer :configuration
|
15
|
+
|
16
|
+
def excalibur_init(config = configuration)
|
17
|
+
@configuration = config
|
18
|
+
end
|
19
|
+
|
20
|
+
def configuration
|
21
|
+
@configuration ||= ::Excalibur.configuration.dup
|
22
|
+
end
|
23
|
+
|
24
|
+
def method_missing(meth, *args)
|
25
|
+
if meth.to_s =~ /^excalibur_set_(title|description+)_(content|option|combinator+)$/
|
26
|
+
configuration.send($1).send("update_#{$2}", *args)
|
27
|
+
else
|
28
|
+
super
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def excalibur_set_meta_tag(type, name, value = nil)
|
33
|
+
configuration.set_meta_tag(type, name, value)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(object, options = {})
|
38
|
+
configuration.merge!(options.delete(:config)) if options.key?(:config)
|
39
|
+
|
40
|
+
super(object, options)
|
41
|
+
end
|
42
|
+
|
43
|
+
def configuration
|
44
|
+
@custom_configuration ||= self.class.configuration.dup
|
45
|
+
end
|
46
|
+
|
47
|
+
def customize_configuration(config)
|
48
|
+
configuration.merge!(config)
|
49
|
+
end
|
50
|
+
|
51
|
+
def render_title(obj = self)
|
52
|
+
configuration.title.to_s(obj) if configuration.title.present?
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Excalibur
|
2
|
+
# the Duplicator module helps in making sure duplication of nested objects
|
3
|
+
# functions properly and is used by the class Configuration and the class
|
4
|
+
# TruncableContent
|
5
|
+
module Duplicator
|
6
|
+
# duplicates TruncatableContent and calls the deep_dup method on any form
|
7
|
+
# of Hash. Otherwise return the object as not all objects do not need to
|
8
|
+
# be duplicated.
|
9
|
+
def dup_instance(obj)
|
10
|
+
if obj.is_a?(TruncateableContent)
|
11
|
+
obj.dup
|
12
|
+
elsif obj.is_a?(Hash)
|
13
|
+
obj.deep_dup
|
14
|
+
else
|
15
|
+
obj
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'excalibur/view_helpers'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
# ties the ViewHelpers into Rails and allows them to be used in the views
|
5
|
+
class Railtie < Rails::Railtie
|
6
|
+
initializer 'excalibur.view_helpers' do
|
7
|
+
ActionView::Base.send :include, ViewHelpers
|
8
|
+
end
|
9
|
+
|
10
|
+
generators do
|
11
|
+
require 'generators/excalibur/install_generator'
|
12
|
+
require 'generators/excalibur/decorator_generator'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Excalibur
|
2
|
+
# The TruncateableContent class is responsible for text content that is
|
3
|
+
# constrained in length due to SEO specifications. Mainly used for title
|
4
|
+
# and meta description tags this class facilitates the creation and proper
|
5
|
+
# rendering of the content. A prefix/body/suffix data structure is used so
|
6
|
+
# that only the body is truncated without having an effect on the branding
|
7
|
+
# that is put before and/or after the body.
|
8
|
+
class TruncateableContent
|
9
|
+
include Duplicator
|
10
|
+
|
11
|
+
attr_accessor :content
|
12
|
+
attr_accessor :options
|
13
|
+
attr_accessor :combinator
|
14
|
+
|
15
|
+
def initialize(content = {}, options = {}, combinator = nil)
|
16
|
+
@content = ::HashWithIndifferentAccess.new(content)
|
17
|
+
@options = ::HashWithIndifferentAccess.new(options)
|
18
|
+
@combinator = combinator
|
19
|
+
end
|
20
|
+
|
21
|
+
def can_merge?(obj)
|
22
|
+
obj.is_a? TruncateableContent
|
23
|
+
end
|
24
|
+
|
25
|
+
def merge!(obj)
|
26
|
+
if can_merge?(obj)
|
27
|
+
@content.merge!(obj.content)
|
28
|
+
@options.merge!(obj.options)
|
29
|
+
@combinator = obj.combinator unless obj.combinator.nil?
|
30
|
+
|
31
|
+
self
|
32
|
+
else
|
33
|
+
fail(TypeError.new(true),
|
34
|
+
'can only merge two Excalibur::TruncateableContent objects')
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def dup
|
39
|
+
self.class.new(
|
40
|
+
dup_instance(@content),
|
41
|
+
dup_instance(@options),
|
42
|
+
dup_instance(@combinator)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
def get_content(key, obj = nil)
|
47
|
+
if @content[key].instance_of?(Proc)
|
48
|
+
@content[key].call(obj)
|
49
|
+
elsif @content[key].nil?
|
50
|
+
''
|
51
|
+
else
|
52
|
+
@content[key]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def update_content(key, value = nil)
|
57
|
+
@content[key] = value
|
58
|
+
end
|
59
|
+
|
60
|
+
def update_option(key, value = nil)
|
61
|
+
@options[key] = value
|
62
|
+
end
|
63
|
+
|
64
|
+
def update_combinator(value = nil)
|
65
|
+
@combinator = value
|
66
|
+
end
|
67
|
+
|
68
|
+
def render_long(obj = nil)
|
69
|
+
@content.map { |key, _value| get_content(key, obj).to_s }.inject(:+).to_s
|
70
|
+
end
|
71
|
+
|
72
|
+
def render_short(obj = nil)
|
73
|
+
if @combinator.instance_of? Proc
|
74
|
+
@combinator.call(obj)
|
75
|
+
else
|
76
|
+
render_long(obj)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
alias_method :to_s, :render_short
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Excalibur
|
2
|
+
# the ViewHelpers module contains the methods to access Excalibur from the
|
3
|
+
# view. Most commonly placed in the view templates and the application Layout
|
4
|
+
module ViewHelpers
|
5
|
+
def entitle(object, options = {})
|
6
|
+
@excalibur_subject = excalibur_decorate_subject(object, options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def render_title_tag
|
10
|
+
content_tag :title, excalibur_subject.render_title
|
11
|
+
end
|
12
|
+
|
13
|
+
def render_meta_tags
|
14
|
+
result = ''
|
15
|
+
|
16
|
+
excalibur_subject.configuration.meta_tags.each do |type_name, value|
|
17
|
+
value.each do |type_value, contents|
|
18
|
+
next if contents.nil?
|
19
|
+
contents = render_meta_content(contents)
|
20
|
+
contents = [contents] unless contents.is_a?(Array)
|
21
|
+
|
22
|
+
contents.each do |content|
|
23
|
+
result << tag(
|
24
|
+
:meta,
|
25
|
+
type_name => type_value,
|
26
|
+
content: content
|
27
|
+
)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
result.html_safe
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def render_meta_content(content)
|
38
|
+
if content.is_a? Proc
|
39
|
+
content.call(excalibur_subject)
|
40
|
+
else
|
41
|
+
content
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def excalibur_subject
|
46
|
+
@excalibur_subject ||= new_blank_excalibur_subject
|
47
|
+
end
|
48
|
+
|
49
|
+
def new_blank_excalibur_subject
|
50
|
+
::Excalibur::Decorator.decorate(true)
|
51
|
+
end
|
52
|
+
|
53
|
+
def excalibur_decorate_subject(object, options = {})
|
54
|
+
Object.const_get(
|
55
|
+
"::Excalibur::#{object.class.name}Decorator"
|
56
|
+
).decorate(object, options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/excalibur.rb
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'excalibur/version'
|
2
|
+
require 'active_support/all'
|
3
|
+
require 'excalibur/decorator'
|
4
|
+
require 'excalibur/duplicator'
|
5
|
+
require 'excalibur/configuration'
|
6
|
+
require 'excalibur/truncatable_content'
|
7
|
+
require 'excalibur/railtie' if defined?(Rails)
|
8
|
+
|
9
|
+
# the Excalibur gem helps you to decorate a rails app with proper title and
|
10
|
+
# meta tags for every page. It does this by providing default configuration
|
11
|
+
# options, object based decorators for customization per object type and
|
12
|
+
# helpers to put in on the page.
|
13
|
+
module Excalibur
|
14
|
+
class << self
|
15
|
+
attr_writer :configuration
|
16
|
+
|
17
|
+
def configuration
|
18
|
+
@configuration ||= new_default_configuration
|
19
|
+
end
|
20
|
+
|
21
|
+
def reset
|
22
|
+
@configuration = new_default_configuration
|
23
|
+
end
|
24
|
+
|
25
|
+
# defines the gem default
|
26
|
+
def new_default_configuration
|
27
|
+
Configuration.new(
|
28
|
+
new_default_title,
|
29
|
+
new_default_description,
|
30
|
+
new_default_meta_tags
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def configure
|
35
|
+
yield(configuration)
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def new_default_title
|
41
|
+
TruncateableContent.new(
|
42
|
+
{ body: 'Excalibur' },
|
43
|
+
{ length: 69, omission: '...', separator: '' },
|
44
|
+
proc do |obj|
|
45
|
+
if obj.present?
|
46
|
+
t = obj.configuration.title
|
47
|
+
|
48
|
+
length = t.options[:length]
|
49
|
+
length -= t.get_content(:prefix, obj).length
|
50
|
+
length -= t.get_content(:suffix, obj).length
|
51
|
+
|
52
|
+
res = t.get_content(:prefix, obj)
|
53
|
+
res += t.get_content(:body, obj).truncate(length, t.options)
|
54
|
+
res + t.get_content(:suffix, obj)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def new_default_description
|
61
|
+
TruncateableContent.new(
|
62
|
+
{ body: 'Excalibur; a worthy title for a gem about titles.' },
|
63
|
+
{ length: 155, omission: '...', separator: ' ' },
|
64
|
+
proc do |obj|
|
65
|
+
if obj.present?
|
66
|
+
d = obj.configuration.description
|
67
|
+
|
68
|
+
length = d.options[:length]
|
69
|
+
length -= d.get_content(:prefix, obj).length
|
70
|
+
length -= d.get_content(:suffix, obj).length
|
71
|
+
|
72
|
+
res = d.get_content(:prefix, obj)
|
73
|
+
res += d.get_content(:body, obj).truncate(length, d.options)
|
74
|
+
res + d.get_content(:suffix, obj)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
)
|
78
|
+
end
|
79
|
+
|
80
|
+
def new_default_meta_tags
|
81
|
+
::HashWithIndifferentAccess.new(
|
82
|
+
name: ::HashWithIndifferentAccess.new(
|
83
|
+
description: proc do |obj|
|
84
|
+
obj.configuration.description.to_s(obj)
|
85
|
+
end,
|
86
|
+
viewport: 'width=device-width, initial-scale=1'
|
87
|
+
)
|
88
|
+
)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
module Generators
|
5
|
+
# allows you to use rails generate to create a new scaffolded decorator
|
6
|
+
# for a application native class.
|
7
|
+
# rails g excalibur:decorator [class_name]
|
8
|
+
class DecoratorGenerator < Rails::Generators::NamedBase
|
9
|
+
source_root File.expand_path('../../templates', __FILE__)
|
10
|
+
|
11
|
+
desc 'Creates a Excalibur decorator.'
|
12
|
+
|
13
|
+
def create_decorator
|
14
|
+
template 'decorator.rb', "app/decorators/excalibur/#{file_name}_decorator.rb"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|