excalibur 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|