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
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'rails/generators/base'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
module Generators
|
5
|
+
# the install generator allows you to get a free to use and fully
|
6
|
+
# documented initializer file for your rails app by running:
|
7
|
+
# rails g excalibur:install
|
8
|
+
class InstallGenerator < Rails::Generators::Base
|
9
|
+
source_root File.expand_path('../../templates', __FILE__)
|
10
|
+
|
11
|
+
desc 'Creates a Excalibur initializer.'
|
12
|
+
|
13
|
+
def copy_initializer
|
14
|
+
template 'excalibur.rb', 'config/initializers/excalibur.rb'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
class Excalibur::<%= class_name %>Decorator < Excalibur::Decorator
|
2
|
+
# ==> Full custom configuration
|
3
|
+
# it is possible to force a new config onto over the application wide
|
4
|
+
# default set in the initializer.
|
5
|
+
#
|
6
|
+
# You can pass a custom configuration in like so:
|
7
|
+
# excalibur_init @my_custom_config
|
8
|
+
#
|
9
|
+
# or start with a blank configuration like so:
|
10
|
+
# excalibur_init Excalibur::Configuration.new
|
11
|
+
#
|
12
|
+
# The last method is easiest as all configuration options are available in
|
13
|
+
# this decorator through a DSL instead of building the object from scratch.
|
14
|
+
|
15
|
+
# ==> Title and Description settings
|
16
|
+
# Titles and descriptions act mostly the same way within Excalibur. They
|
17
|
+
# share the same DSL with the small difference in method name. Below the
|
18
|
+
# description is for title but it applies to description as well.
|
19
|
+
#
|
20
|
+
# => Set content
|
21
|
+
# the excalibur_set_title_content method takes two parameters to change the
|
22
|
+
# content of a title. By defaults you can set a :prefix, :body and :suffix.
|
23
|
+
# All of these can be a string or a Proc.
|
24
|
+
#
|
25
|
+
# as an example:
|
26
|
+
# excalibur_set_title_content :body, 'my class specific title body'
|
27
|
+
#
|
28
|
+
# => Set option
|
29
|
+
# the excalibur_set_title_option method takes two arguments to change the
|
30
|
+
# options of a title. By defaults you can set a :length, :omission and
|
31
|
+
# :separator.
|
32
|
+
#
|
33
|
+
# :length takes any integer to indicate the length limit of the title.
|
34
|
+
# :omission is what fills the end of the line when the body is truncated
|
35
|
+
# below the limited length of the title :body.
|
36
|
+
# :separator determines what character the :body will be truncated on. with
|
37
|
+
# a single space (' ') it will break on words and with no-space ('') it
|
38
|
+
# will break on any character.
|
39
|
+
#
|
40
|
+
# as an examples:
|
41
|
+
# excalibur_set_title_content :length, 42
|
42
|
+
# excalibur_set_title_content :omission, '... (continued)'
|
43
|
+
#
|
44
|
+
# => set combinator
|
45
|
+
# the excalibur_set_title_combinator takes one argument and it should be a
|
46
|
+
# Proc. The proc is passed the decorated object so you have access to the
|
47
|
+
# object and the configuration of required. It needs to result in a string.
|
48
|
+
#
|
49
|
+
# excalibur_set_title_combinator(proc { |obj|
|
50
|
+
# # your code here
|
51
|
+
# })
|
52
|
+
|
53
|
+
# ==> Meta tag settings
|
54
|
+
# the excalibur_set_meta_tag method takes three arguments to set the content
|
55
|
+
# for a meta tag. The first and second arguments are about the type of meta
|
56
|
+
# tag while the third is used for the content.
|
57
|
+
#
|
58
|
+
# A description meta tag would look like this:
|
59
|
+
# excalibur_set_meta_tag :name, :description, 'Some description content'
|
60
|
+
#
|
61
|
+
# The content of a meta tag can be anything that prints nicely into a
|
62
|
+
# string, a Proc or an Array. The Proc may also result in an array and is
|
63
|
+
# passed the decorated object for access to the objects attributes and the
|
64
|
+
# excalibur configuration acting for the object.
|
65
|
+
|
66
|
+
# ==> Recommended use
|
67
|
+
# Because the title, description and the meta tags all allow for a Proc to
|
68
|
+
# be passed it gives you a lot of freedom. However like any freedom it is to
|
69
|
+
# be used with some warning. It is recommended when doing larger amounts of
|
70
|
+
# work in code of a proc to use the decorator to delegate that
|
71
|
+
# responsibility. The decorator becomes easier to test and it is also a lot
|
72
|
+
# cleaner.
|
73
|
+
#
|
74
|
+
# TODO: An article about how to do it properly will come soon!
|
75
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
# Use this to set up your default
|
2
|
+
Excalibur.configure do |config|
|
3
|
+
# ==> Your sane defaults:
|
4
|
+
|
5
|
+
# => Title content
|
6
|
+
# config.title.update_content :prefix, 'prefix - '
|
7
|
+
config.title.update_content :body, 'My website has a title by Excalibur'
|
8
|
+
|
9
|
+
# => Title options
|
10
|
+
# config.title.update_option :length, 69
|
11
|
+
# config.title.update_option :omission, '...'
|
12
|
+
# config.title.update_option :separator, ''
|
13
|
+
|
14
|
+
# => Description content
|
15
|
+
config.description.update_content :body, 'Excalibur gave me a description!'
|
16
|
+
|
17
|
+
# => Description options
|
18
|
+
# config.description.update_option :length, 155
|
19
|
+
# config.description.update_option :omission, '...'
|
20
|
+
# config.description.update_option :separator, ' '
|
21
|
+
|
22
|
+
# => Meta tags
|
23
|
+
config.set_meta_tag :name, :viewport, 'width=device-width, initial-scale=1'
|
24
|
+
|
25
|
+
# ==> Configuration DSL:
|
26
|
+
#
|
27
|
+
# Both the title and description are configurable in the same way;
|
28
|
+
# config.title for the title and config.description for the description.
|
29
|
+
# They both consist out of three elements; content, options and the
|
30
|
+
# combinator. The meta tags have their own interface as they are slightly
|
31
|
+
# different. Both the content in the title/description and the content in
|
32
|
+
# the meta tags can be given a Proc in order for on-render computation to
|
33
|
+
# occur.
|
34
|
+
|
35
|
+
# => Title/Description content
|
36
|
+
# The default of the gem tries to combine 3 parts; :prefix,
|
37
|
+
# :body and :suffix. They are put together in that order by the default
|
38
|
+
# combinator.
|
39
|
+
#
|
40
|
+
# To set these add:
|
41
|
+
# config.title.update_content :prefix, 'CNN.com - '
|
42
|
+
# config.title.update_content :body, 'Breaking, World, Business, Sports and
|
43
|
+
# Entertainment News'
|
44
|
+
#
|
45
|
+
# this will result in a title like 'CNN.com - Breaking, World, Business,
|
46
|
+
# Sports and Entertainment News'
|
47
|
+
#
|
48
|
+
# > Title Defaults:
|
49
|
+
# prefix: ''
|
50
|
+
# body: 'Excalibur'
|
51
|
+
# suffix: ''
|
52
|
+
#
|
53
|
+
# > Description Defaults:
|
54
|
+
# prefix: ''
|
55
|
+
# body: 'Excalibur; a worthy title for a gem about titles.'
|
56
|
+
# suffix: ''
|
57
|
+
|
58
|
+
# => Title/Description options
|
59
|
+
# The default of the gem uses the options to truncate the content
|
60
|
+
# components in the correct way. :length is the length the content will be
|
61
|
+
# truncated to. :omission is used to indicate that the content is truncated
|
62
|
+
# and is added behind the section that has been truncated. :separator
|
63
|
+
# determines if truncation happens on a character or not,
|
64
|
+
# '' for every letter and ' ' for a space as it allows for breaking on words
|
65
|
+
# or characters.
|
66
|
+
#
|
67
|
+
# To set these add:
|
68
|
+
# config.title.update_option :length, 42
|
69
|
+
# config.title.update_option :omission, '... (continued)'
|
70
|
+
#
|
71
|
+
# > Title Defaults:
|
72
|
+
# length: 69
|
73
|
+
# omission: '...'
|
74
|
+
# separator: ''
|
75
|
+
#
|
76
|
+
# > Description Defaults:
|
77
|
+
# length: 155
|
78
|
+
# omission: '...'
|
79
|
+
# separator: ' '
|
80
|
+
|
81
|
+
# => Title/Description combinator
|
82
|
+
# The combinator is by default a proc that combines the content elements
|
83
|
+
# together and truncates them according to what the options have specified.
|
84
|
+
# If your not into it yet the combinator is to be changed with caution.
|
85
|
+
#
|
86
|
+
# Change the combinator by:
|
87
|
+
# config.title.update_combinator(
|
88
|
+
# proc do |object|
|
89
|
+
# # your code here
|
90
|
+
# #
|
91
|
+
# # the object var is passed when rendering and contains an object
|
92
|
+
# # with the current configuration for that render. It's advised when
|
93
|
+
# # using data from that configuration to use object.configuration to
|
94
|
+
# # access it.
|
95
|
+
# end
|
96
|
+
# )
|
97
|
+
#
|
98
|
+
# > Default behavior
|
99
|
+
# By default the combinator takes the 3 content sections but only truncates
|
100
|
+
# the :body section. This does take into account the length of the :prefix
|
101
|
+
# and :suffix when truncating the content so the overall length of the
|
102
|
+
# title stays within the length set in the options.
|
103
|
+
#
|
104
|
+
# > Title/Description configuration examples:
|
105
|
+
# content:
|
106
|
+
# prefix: 'Excalibur | '
|
107
|
+
# body: 'The Object oriented way of setting SEO and meta information'
|
108
|
+
# options:
|
109
|
+
# length: 69
|
110
|
+
# omission: '...'
|
111
|
+
# separator: ''
|
112
|
+
# result: 'Excalibur | The Object oriented way of setting SEO and meta
|
113
|
+
# inform...'
|
114
|
+
#
|
115
|
+
# options:
|
116
|
+
# separator: ' '
|
117
|
+
# result: 'Excalibur | The Object oriented way of setting SEO and meta...'
|
118
|
+
#
|
119
|
+
# content:
|
120
|
+
# body: 'Just another website'
|
121
|
+
# suffix: ' - site ID'
|
122
|
+
# result: 'Just another website - site ID'
|
123
|
+
|
124
|
+
# => Meta tags
|
125
|
+
# Meta tags are basically represented in the configuration as a double
|
126
|
+
# layered hash. The hash is converted to meta tags types and the values are
|
127
|
+
# the content of the meta tag.
|
128
|
+
#
|
129
|
+
# These can be easily set using:
|
130
|
+
# config.set_meta_tag(
|
131
|
+
# :name,
|
132
|
+
# :viewport,
|
133
|
+
# 'width=device-width, initial-scale=1')
|
134
|
+
|
135
|
+
# => General configuration:
|
136
|
+
# it is possible to roll your own configuration without using the accessors
|
137
|
+
# provided in the DSL. You can use the Excalibur::Configuration class to
|
138
|
+
# create your own config. Have a look at the code how to do so!
|
139
|
+
#
|
140
|
+
# you can set it by simply:
|
141
|
+
# config = MyCustomConfiguration.new
|
142
|
+
#
|
143
|
+
# If you want to keep using parts of the default configuration but want to
|
144
|
+
# replace others configuration has a #merge! method to merge another
|
145
|
+
# Excalibur::Configuration object into the default. Note that this will only
|
146
|
+
# work if the supplied object is a Excalibur::Configuration or an inherited
|
147
|
+
# object.
|
148
|
+
#
|
149
|
+
# use this by:
|
150
|
+
# config.merge!(MyCustomConfiguration.new)
|
151
|
+
end
|
@@ -0,0 +1,215 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
describe Configuration do
|
5
|
+
describe '#new' do
|
6
|
+
context 'when creating a configuration without parameters' do
|
7
|
+
let(:config) { Configuration.new }
|
8
|
+
|
9
|
+
it 'should create a title' do
|
10
|
+
expect(config.title).to be_instance_of TruncateableContent
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should create a describe' do
|
14
|
+
expect(config.description).to be_instance_of TruncateableContent
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should create an empty meta tags hash' do
|
18
|
+
expect(config.meta_tags).to be_instance_of ::HashWithIndifferentAccess
|
19
|
+
expect(config.meta_tags).to be_empty
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when supplying parameters' do
|
24
|
+
let(:config) { Configuration.new('foo', 'bar', 'baz') }
|
25
|
+
|
26
|
+
it 'should set the right values' do
|
27
|
+
expect(config.title).to eq('foo')
|
28
|
+
expect(config.description).to eq('bar')
|
29
|
+
expect(config.meta_tags).to eq('baz')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe '#merge!' do
|
35
|
+
context 'when trying to merge with the wrong type of object' do
|
36
|
+
let(:obj) { Configuration.new }
|
37
|
+
|
38
|
+
it 'should raise a TypeError' do
|
39
|
+
expect { obj.merge!('foo') }.to raise_error(TypeError, 'can only merge two Excalibur::Configuration objects')
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
context 'when merging two configurations' do
|
44
|
+
let(:obj_a) { Configuration.new(TruncateableContent.new, TruncateableContent.new) }
|
45
|
+
let(:obj_b) { Configuration.new(TruncateableContent.new, false, nil) }
|
46
|
+
|
47
|
+
context 'instance result' do
|
48
|
+
after do
|
49
|
+
obj_a.merge!(obj_b)
|
50
|
+
end
|
51
|
+
|
52
|
+
it 'should merge the instance variables' do
|
53
|
+
expect(obj_a).to receive(:merge_instance).exactly(3).times
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context 'function result' do
|
58
|
+
it 'should return itself' do
|
59
|
+
expect(obj_a.merge!(obj_b)).to be_instance_of Configuration
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
describe 'when merging content merging' do
|
65
|
+
let(:a) { 'foobar' }
|
66
|
+
let(:b) { 'baz' }
|
67
|
+
let(:config) { Configuration.new(a) }
|
68
|
+
let(:new_config) { Configuration.new(b) }
|
69
|
+
|
70
|
+
before do
|
71
|
+
config.merge!(new_config)
|
72
|
+
end
|
73
|
+
|
74
|
+
context 'when the objects are different classes' do
|
75
|
+
context 'when the new object is nil' do
|
76
|
+
let(:new_config) { Configuration.new(nil) }
|
77
|
+
|
78
|
+
it 'should not change the config variable' do
|
79
|
+
expect( config.title ).to eq(config.title)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'when the new object is a value' do
|
84
|
+
let(:new_config) { Configuration.new(true) }
|
85
|
+
|
86
|
+
it 'should change the config variable' do
|
87
|
+
expect( config.title ).to eq(true)
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context 'when the objects are the same class' do
|
93
|
+
context 'when merging two hashes' do
|
94
|
+
let(:a) { {foo: {bar: 'baz', other: 'old'}} }
|
95
|
+
let(:b) { {foo: {other: 'new'}} }
|
96
|
+
|
97
|
+
it 'should deep merge the two hashes' do
|
98
|
+
expect(config.title).to eq( { foo: {bar: 'baz', other: 'new'} } )
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'when merging two TruncatableContent' do
|
103
|
+
let(:config) { Configuration.new }
|
104
|
+
let(:b) { TruncateableContent.new({foo: 'bar'}, {foo: 'bar'}, 'foobar') }
|
105
|
+
|
106
|
+
it 'should merge the two TruncatableContent' do
|
107
|
+
expect(config.title.content).to eq( b.content )
|
108
|
+
expect(config.title.options).to eq( b.options )
|
109
|
+
expect(config.title.combinator).to eq( b.combinator )
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
context 'when merging two strings' do
|
114
|
+
let(:a) { 'foo' }
|
115
|
+
let(:b) { 'bar' }
|
116
|
+
|
117
|
+
it 'should concatenate the two strings' do
|
118
|
+
expect(config.title).to eq('foobar')
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
context 'when merging something else' do
|
123
|
+
let(:a) { true }
|
124
|
+
let(:b) { false }
|
125
|
+
|
126
|
+
it 'should replace it' do
|
127
|
+
expect(config.title).to eq(false)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '#dup' do
|
135
|
+
it 'should create a duplicate of the object and the attributes' do
|
136
|
+
expect(Excalibur.configuration.title).to receive(:dup)
|
137
|
+
expect(Excalibur.configuration.description).to receive(:dup)
|
138
|
+
expect(Excalibur.configuration.meta_tags).to receive(:dup)
|
139
|
+
|
140
|
+
Excalibur.configuration.dup
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should create a duplicate of the object and the attributes' do
|
144
|
+
expect(Excalibur.configuration).to receive(:dup_instance).exactly(3).times
|
145
|
+
|
146
|
+
Excalibur.configuration.dup
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe '#set_meta_tag' do
|
151
|
+
let(:config) do
|
152
|
+
c = Configuration.new
|
153
|
+
c.meta_tags = ::HashWithIndifferentAccess.new( name: ::HashWithIndifferentAccess.new( description: 'old' ) )
|
154
|
+
c
|
155
|
+
end
|
156
|
+
|
157
|
+
it 'should overwrite a tag' do
|
158
|
+
config.set_meta_tag(:name, :description, 'Description content')
|
159
|
+
|
160
|
+
expect(config.meta_tags).to eq(::HashWithIndifferentAccess.new(
|
161
|
+
name: ::HashWithIndifferentAccess.new( description: 'Description content' ))
|
162
|
+
)
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should create a new tag type when required' do
|
166
|
+
config.set_meta_tag(:foo, :bar, 'baz')
|
167
|
+
|
168
|
+
expect(config.meta_tags).to have_key(:foo)
|
169
|
+
expect(config.meta_tags[:foo]).to have_key(:bar)
|
170
|
+
expect(config.meta_tags[:foo]).to be_instance_of ::HashWithIndifferentAccess
|
171
|
+
expect(config.meta_tags[:foo][:bar]).to eq('baz')
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe '#remove_meta_tag' do
|
176
|
+
let(:config) do
|
177
|
+
c = Configuration.new
|
178
|
+
c.meta_tags = {
|
179
|
+
name: {
|
180
|
+
description: 'content',
|
181
|
+
other: 'content'
|
182
|
+
},
|
183
|
+
foo: {
|
184
|
+
bar: 'baz'
|
185
|
+
}
|
186
|
+
}
|
187
|
+
c
|
188
|
+
end
|
189
|
+
|
190
|
+
context 'when the type still has values left' do
|
191
|
+
before do
|
192
|
+
config.remove_meta_tag(:name, :other)
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should have removed the name' do
|
196
|
+
expect(config.meta_tags[:name]).to_not have_key(:other)
|
197
|
+
end
|
198
|
+
|
199
|
+
it 'should still have the type' do
|
200
|
+
expect(config.meta_tags).to have_key(:name)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
context 'when the type still has values left' do
|
205
|
+
before do
|
206
|
+
config.remove_meta_tag(:foo, :bar)
|
207
|
+
end
|
208
|
+
|
209
|
+
it 'should have removed the type' do
|
210
|
+
expect(config.meta_tags).to_not have_key(:foo)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Excalibur
|
4
|
+
class Dummy; end
|
5
|
+
class DummyDecorator < Decorator; end
|
6
|
+
|
7
|
+
describe Decorator do
|
8
|
+
before do
|
9
|
+
Excalibur.reset
|
10
|
+
DummyDecorator.configuration = nil
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
DummyDecorator.configuration = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#new' do
|
18
|
+
context 'when creating it without options' do
|
19
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new) }
|
20
|
+
|
21
|
+
it { expect(obj.context).to be_empty }
|
22
|
+
it { expect(obj.configuration).to be_present }
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'when creating it with options' do
|
26
|
+
context 'when providing context' do
|
27
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new, context: {foo: 'bar'}) }
|
28
|
+
|
29
|
+
it { expect(obj.context).to_not be_empty }
|
30
|
+
it { expect(obj.context).to eq({foo: 'bar'}) }
|
31
|
+
end
|
32
|
+
|
33
|
+
context 'when providing config' do
|
34
|
+
let(:config) { Configuration.new }
|
35
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new, config: config) }
|
36
|
+
|
37
|
+
it 'should try and merge the configuration' do
|
38
|
+
expect_any_instance_of(Configuration).to receive(:merge!).with(config)
|
39
|
+
|
40
|
+
obj
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when configuring a decorator with mixed methods' do
|
47
|
+
before do
|
48
|
+
DummyDecorator.configuration = nil
|
49
|
+
|
50
|
+
class DummyDecorator < Decorator
|
51
|
+
excalibur_set_meta_tag(:foo, :bar, proc { |obj| 'content' })
|
52
|
+
excalibur_set_meta_tag(:name, :other, 'content')
|
53
|
+
|
54
|
+
excalibur_set_title_content(:prefix, 'Exc / ')
|
55
|
+
excalibur_set_title_content(:body, 'title body')
|
56
|
+
excalibur_set_title_option(:length, 42)
|
57
|
+
excalibur_set_title_combinator proc{ |obj| "result #{obj}" }
|
58
|
+
|
59
|
+
excalibur_set_description_content(:body, 'title body')
|
60
|
+
excalibur_set_description_option(:length, 42)
|
61
|
+
excalibur_set_description_combinator proc{ |obj| "result #{obj}" }
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it { expect(DummyDecorator.configuration.meta_tags).to have_key(:name) }
|
66
|
+
it { expect(DummyDecorator.configuration.meta_tags[:name]).to have_key(:other) }
|
67
|
+
it { expect(DummyDecorator.configuration.meta_tags).to have_key(:foo) }
|
68
|
+
it { expect(DummyDecorator.configuration.meta_tags[:foo]).to have_key(:bar) }
|
69
|
+
|
70
|
+
it { expect(DummyDecorator.configuration.title.content[:prefix]).to eq 'Exc / ' }
|
71
|
+
it { expect(DummyDecorator.configuration.title.content[:body]).to eq 'title body' }
|
72
|
+
it { expect(DummyDecorator.configuration.title.options[:length]).to eq 42 }
|
73
|
+
it { expect(DummyDecorator.configuration.title.combinator).to be_instance_of Proc }
|
74
|
+
it { expect(DummyDecorator.configuration.title.combinator.call('foobar')).to eq('result foobar') }
|
75
|
+
|
76
|
+
it { expect(DummyDecorator.configuration.description.content[:body]).to eq 'title body' }
|
77
|
+
it { expect(DummyDecorator.configuration.description.options[:length]).to eq 42 }
|
78
|
+
it { expect(DummyDecorator.configuration.description.combinator).to be_instance_of Proc }
|
79
|
+
it { expect(DummyDecorator.configuration.description.combinator.call('foobar')).to eq('result foobar') }
|
80
|
+
end
|
81
|
+
|
82
|
+
describe '::excalibur_init' do
|
83
|
+
context 'when creating a decorator' do
|
84
|
+
context 'when setting the config without a value' do
|
85
|
+
before do
|
86
|
+
class DummyDecorator < Decorator
|
87
|
+
excalibur_init
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
it { expect(DummyDecorator.configuration).to be_present }
|
92
|
+
it { expect(DummyDecorator.configuration).to be_instance_of Configuration }
|
93
|
+
end
|
94
|
+
|
95
|
+
context 'when setting the config with a value' do
|
96
|
+
before do
|
97
|
+
class DummyDecorator < Decorator
|
98
|
+
excalibur_init true
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
it { expect(DummyDecorator.configuration).to be_present }
|
103
|
+
it { expect(DummyDecorator.configuration).to eq(true) }
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
describe '::configuration' do
|
109
|
+
it 'should duplicate the system wide configuration' do
|
110
|
+
expect( Excalibur.configuration ).to receive(:dup)
|
111
|
+
|
112
|
+
DummyDecorator.configuration
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'should only duplicate the system wide configuration once' do
|
116
|
+
DummyDecorator.configuration
|
117
|
+
|
118
|
+
expect( Excalibur.configuration ).to_not receive(:dup)
|
119
|
+
|
120
|
+
DummyDecorator.configuration
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
describe '::excalibur_set' do
|
125
|
+
describe '_title' do
|
126
|
+
describe '_content' do
|
127
|
+
it 'should change the title content' do
|
128
|
+
expect {
|
129
|
+
DummyDecorator.excalibur_set_title_content(:foo, 'bar')
|
130
|
+
}.to change(DummyDecorator.configuration.title, :content).to(body: 'Excalibur', foo: 'bar')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '_option' do
|
135
|
+
it 'should change the title options' do
|
136
|
+
expect {
|
137
|
+
DummyDecorator.excalibur_set_title_option(:foo, 'bar')
|
138
|
+
}.to change(DummyDecorator.configuration.title, :options).to(length: 69, omission: '...', separator: '', foo: 'bar')
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe '_combinator' do
|
143
|
+
it 'should change the title combinator' do
|
144
|
+
expect {
|
145
|
+
DummyDecorator.excalibur_set_title_combinator(true)
|
146
|
+
}.to change(DummyDecorator.configuration.title, :combinator).to(true)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
describe '_description' do
|
152
|
+
describe '_content' do
|
153
|
+
it 'should change the description content' do
|
154
|
+
expect {
|
155
|
+
DummyDecorator.excalibur_set_description_content(:foo, 'bar')
|
156
|
+
}.to change(DummyDecorator.configuration.description, :content).to(body: 'Excalibur; a worthy title for a gem about titles.', foo: 'bar')
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '_option' do
|
161
|
+
it 'should change the description options' do
|
162
|
+
expect {
|
163
|
+
DummyDecorator.excalibur_set_description_option(:foo, 'bar')
|
164
|
+
}.to change(DummyDecorator.configuration.description, :options).to(length: 155, omission: '...', separator: ' ', foo: 'bar')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe '_combinator' do
|
169
|
+
it 'should change the description combinator' do
|
170
|
+
expect {
|
171
|
+
DummyDecorator.excalibur_set_description_combinator(true)
|
172
|
+
}.to change(DummyDecorator.configuration.description, :combinator).to(true)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
describe '_meta_tag' do
|
178
|
+
it 'should change the meta tags' do
|
179
|
+
expect(DummyDecorator.configuration).to receive(:set_meta_tag).with(:name, :description, 'foobar')
|
180
|
+
|
181
|
+
DummyDecorator.excalibur_set_meta_tag(:name, :description, 'foobar')
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe '#configuration' do
|
187
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new) }
|
188
|
+
|
189
|
+
it "should duplicate the decorator's configuration" do
|
190
|
+
expect(obj.class.configuration).to receive(:dup)
|
191
|
+
|
192
|
+
obj.configuration
|
193
|
+
end
|
194
|
+
|
195
|
+
it 'should return a configuration' do
|
196
|
+
expect(obj.configuration).to be_instance_of Configuration
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
describe '#customize_configuration' do
|
201
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new) }
|
202
|
+
|
203
|
+
it 'should merge the input with the configuration' do
|
204
|
+
expect(obj.configuration).to receive(:merge!).with('foobar')
|
205
|
+
|
206
|
+
obj.customize_configuration('foobar')
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
describe '#render_title' do
|
211
|
+
let(:obj) { DummyDecorator.decorate(Dummy.new) }
|
212
|
+
|
213
|
+
context 'when using the default settings' do
|
214
|
+
it { expect(obj.render_title).to eq('Excalibur') }
|
215
|
+
|
216
|
+
it 'should call to_s in the title' do
|
217
|
+
expect(obj.configuration.title).to receive(:to_s)
|
218
|
+
|
219
|
+
obj.render_title
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|