gretel 3.0.0.beta4 → 3.0.0.beta5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +1 -4
- data/README.md +28 -73
- data/gretel.gemspec +0 -2
- data/lib/generators/gretel/install_generator.rb +0 -7
- data/lib/gretel/crumb.rb +1 -1
- data/lib/gretel/deprecated/layout.rb +15 -0
- data/lib/gretel/deprecated/show_root_alone.rb +12 -0
- data/lib/gretel/deprecated.rb +2 -23
- data/lib/gretel/renderer.rb +61 -66
- data/lib/gretel/version.rb +1 -1
- data/lib/gretel/view_helpers.rb +4 -10
- data/lib/gretel.rb +22 -23
- data/test/dummy/db/schema.rb +1 -10
- data/test/gretel_test.rb +2 -2
- data/test/helper_methods_test.rb +0 -34
- data/test/test_helper.rb +0 -1
- metadata +4 -49
- data/lib/generators/gretel/templates/trail_migration.rb +0 -11
- data/lib/generators/gretel/trail/migration_generator.rb +0 -20
- data/lib/gretel/trail/stores/active_record_store.rb +0 -59
- data/lib/gretel/trail/stores/redis_store.rb +0 -54
- data/lib/gretel/trail/stores/store.rb +0 -49
- data/lib/gretel/trail/stores/url_store.rb +0 -57
- data/lib/gretel/trail/stores.rb +0 -4
- data/lib/gretel/trail/tasks.rb +0 -5
- data/lib/gretel/trail.rb +0 -68
- data/test/dummy/db/migrate/20131015194052_create_gretel_trails.rb +0 -11
- data/test/trails/active_record_store_test.rb +0 -52
- data/test/trails/redis_store_test.rb +0 -35
- data/test/trails/trail_test.rb +0 -27
- data/test/trails/url_store_test.rb +0 -38
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7e5effb851a29b8c853f52fcf5cc456cc08af59d
|
4
|
+
data.tar.gz: 27d7b1df1fd9d953b9a3e4d2a70ebd444b6d644f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6d578d14e488a1fa88d8e60b0bbe392b95a71871857134364a1af6fa2d0c35662cdfc3c4d51623b908b02a0b27795274a47509b4cf7adda7114b64c35e9dcb4d
|
7
|
+
data.tar.gz: 18f43e6bd65b469f517341ad6326224252e041f8ea9392b27dbacdce835137f48fea3256d7bc3b8f90e8e041962b5783655f1fc826295ed8aab24184bb2245a1
|
data/CHANGELOG.md
CHANGED
@@ -4,14 +4,11 @@ Changelog
|
|
4
4
|
Version 3.0
|
5
5
|
-----------
|
6
6
|
* Support for defining breadcrumbs using `Gretel::Crumbs.layout do ... end` in an initializer has been removed. See the readme for details on how to upgrade.
|
7
|
-
* Support for setting trails via the URL – `params[:trail]`. This makes it possible to link back to a different breadcrumb trail than the one specified in your breadcrumb,
|
8
|
-
for example if you have a store with products that have a default parent to their category, but when linking from the review section, you want to link back to the reviews instead.
|
9
|
-
See the readme for more info.
|
10
7
|
* Breadcrumbs rendering is now done in a separate class to unclutter the view with helpers. The public API is still the same.
|
11
8
|
* Support for rendering the breadcrumbs in different styles like ul- and ol lists, and for use with [Twitter Bootstrap](http://getbootstrap.com/). See the `:style` option in the readme for more info.
|
12
9
|
* The `:show_root_alone` option is now called `:display_single_fragment` and can be used to display the breadcrumbs only when there are more than one link, also if it is not the root breadcrumb.
|
13
10
|
* Links yielded from `<%= breadcrumbs do |links| %>` now have a `current?` helper that returns true if the link is the last in the trail.
|
14
|
-
* New view helper: `parent_breadcrumb` returns the parent breadcrumb link (with `#key`, `#text`, and `#url`). You can supply options like `:autoroot` etc.
|
11
|
+
* New view helper: `parent_breadcrumb` returns the parent breadcrumb link (with `#key`, `#text`, and `#url`). This can for example be used to create a dynamic back link. You can supply options like `:autoroot` etc.
|
15
12
|
If you supply a block, it will yield the parent breadcrumb if it is present.
|
16
13
|
|
17
14
|
Version 2.1
|
data/README.md
CHANGED
@@ -10,15 +10,13 @@ Have fun! And please do write, if you (dis)like it – [lassebunk@gmail.com](mai
|
|
10
10
|
New in version 3.0 :muscle:
|
11
11
|
---------------------------
|
12
12
|
|
13
|
-
* You can now set trails via the URL – `params[:trail]`. This makes it possible to link back to a different breadcrumb trail than the one specified in your breadcrumb,
|
14
|
-
for example if you have a store with products that have a default parent to their category, but when visiting from the reviews section, you want to link back to the reviews instead.
|
15
|
-
Read more about trails below.
|
16
13
|
* Breadcrumbs can now be rendered in different styles like ul- and ol lists, and for use with the [Twitter Bootstrap](http://getbootstrap.com/) framework. See the `:style` option below for more info.
|
17
14
|
* Defining breadcrumbs using `Gretel::Crumbs.layout do ... end` in an initializer has been removed. See below for details on how to upgrade.
|
18
15
|
* The `:show_root_alone` option is now called `:display_single_fragment` and can be used to hide the breadcrumbs when there is only one link, also if it is not the root breadcrumb.
|
19
16
|
The old `:show_root_alone` option is still supported until Gretel version 4.0 and will show a deprecation warning when it's used.
|
20
17
|
* Links yielded from `<%= breadcrumbs do |links| %>` now have a `current?` helper that returns true if the link is the last in the trail.
|
21
|
-
* New view helper: `parent_breadcrumb` returns the parent breadcrumb link (with `#key`, `#text`, and `#url`).
|
18
|
+
* New view helper: `parent_breadcrumb` returns the parent breadcrumb link (with `#key`, `#text`, and `#url`). This can for example be used to create a dynamic back link.
|
19
|
+
You can supply options like `:autoroot` etc.
|
22
20
|
If you supply a block, it will yield the parent breadcrumb if it is present.
|
23
21
|
|
24
22
|
I hope you find these changes as useful as I did – if you have more suggestions, please create an [Issue](https://github.com/lassebunk/gretel/issues) or [Pull Request](https://github.com/lassebunk/gretel/pulls).
|
@@ -31,7 +29,7 @@ Installation
|
|
31
29
|
In your *Gemfile*:
|
32
30
|
|
33
31
|
```ruby
|
34
|
-
gem "gretel", "3.0.0.
|
32
|
+
gem "gretel", "3.0.0.beta5"
|
35
33
|
```
|
36
34
|
|
37
35
|
And run:
|
@@ -157,28 +155,29 @@ crumb :issue do |issue|
|
|
157
155
|
parent :project_issues, issue.project
|
158
156
|
end
|
159
157
|
|
160
|
-
#
|
158
|
+
# Recursive parent categories
|
161
159
|
crumb :category do |category|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
end
|
168
|
-
|
169
|
-
parents.each do |category|
|
170
|
-
link category.name, category
|
160
|
+
link category.name, category
|
161
|
+
if category.parent
|
162
|
+
parent :category, category.parent
|
163
|
+
else
|
164
|
+
parent :categories
|
171
165
|
end
|
172
|
-
|
173
|
-
parent :categories
|
174
166
|
end
|
175
167
|
|
176
|
-
# Product crumb with recursive parent categories
|
168
|
+
# Product crumb with recursive parent categories (as defined above)
|
177
169
|
crumb :product do |product|
|
178
170
|
link product.name, product
|
179
171
|
parent :category, product.category
|
180
172
|
end
|
181
173
|
|
174
|
+
# Crumb with multiple links
|
175
|
+
crumb :test do
|
176
|
+
link "One", one_path
|
177
|
+
link "Two", two_path
|
178
|
+
parent :about
|
179
|
+
end
|
180
|
+
|
182
181
|
# Example of using params to alter the parent, e.g. to
|
183
182
|
# match the user's actual navigation path
|
184
183
|
# URL: /products/123?q=my+search
|
@@ -236,7 +235,7 @@ If you supply a block to the `breadcrumbs` method, it will yield an array with t
|
|
236
235
|
Getting the parent breadcrumb
|
237
236
|
-----------------------------
|
238
237
|
|
239
|
-
If you want to add a link to the parent breadcrumb
|
238
|
+
If you want to add a link to the parent breadcrumb, you can use the `parent_breadcrumb` view helper.
|
240
239
|
By default it returns a link instance that has the properties `#key`, `#text`, and `#url`.
|
241
240
|
You can supply options like `autoroot: false` etc.
|
242
241
|
|
@@ -248,60 +247,6 @@ If you supply a block, it will yield the link if it is present:
|
|
248
247
|
<% end %>
|
249
248
|
```
|
250
249
|
|
251
|
-
Setting breadcrumb trails
|
252
|
-
--------------------------------
|
253
|
-
|
254
|
-
You can set a breadcrumb trail via `params[:trail]`. This makes it possible to link back to a different breadcrumb trail than the one specified in your breadcrumb.
|
255
|
-
|
256
|
-
An example is if you have a store with products that have a default parent to their category, but when visiting from the reviews section, you want to link back to the reviews instead.
|
257
|
-
|
258
|
-
### Initial setup
|
259
|
-
|
260
|
-
To use breadcrumb trails, you must set a secret to be used when encoding the trails.
|
261
|
-
|
262
|
-
You can generate it using the installer:
|
263
|
-
|
264
|
-
```bash
|
265
|
-
$ rails generate gretel:install
|
266
|
-
```
|
267
|
-
|
268
|
-
This will create an initializer in *config/initializers/gretel.rb* that will contain a random secret key.
|
269
|
-
|
270
|
-
If you want to do it manually, you can put the following in *config/initializers/gretel.rb*:
|
271
|
-
|
272
|
-
```
|
273
|
-
Gretel::Trail::UrlStore.secret = 'your_key_here' # Must be changed to something else to be secure
|
274
|
-
```
|
275
|
-
|
276
|
-
You can generate a key using `SecureRandom.hex(64)`.
|
277
|
-
|
278
|
-
### Example
|
279
|
-
|
280
|
-
This example shows how to link to the trail in the view.
|
281
|
-
Gretel has a built-in view helper method named `breadcrumb_trail` that contains the current breadcrumb trail ready for use in a URL.
|
282
|
-
|
283
|
-
```erb
|
284
|
-
<% breadcrumb :reviews %>
|
285
|
-
...
|
286
|
-
<% @products.each do |product| %>
|
287
|
-
<%= link_to @product.name, product_path(product, trail: breadcrumb_trail) %>
|
288
|
-
<% end %>
|
289
|
-
```
|
290
|
-
|
291
|
-
The product view will now have the breadcrumb trail from the first page (reviews) instead of its default parent.
|
292
|
-
|
293
|
-
### Custom trail param
|
294
|
-
|
295
|
-
The default trail param is `params[:trail]`. You can change it in an initializer:
|
296
|
-
|
297
|
-
```ruby
|
298
|
-
Gretel.trail_param = :other_param
|
299
|
-
```
|
300
|
-
|
301
|
-
### Note
|
302
|
-
|
303
|
-
Please use the trail functionality with care; the trails can get very long.
|
304
|
-
|
305
250
|
Nice to know
|
306
251
|
------------
|
307
252
|
|
@@ -318,6 +263,16 @@ The format is the same as `config/breadcrumbs.rb` which is also loaded.
|
|
318
263
|
|
319
264
|
Since Gretel version 2.1.0, the breadcrumb configuration files are now reloaded in the Rails development environment if they change. In other environments, like production, the files are loaded once, when first needed.
|
320
265
|
|
266
|
+
### Setting breadcrumb trails
|
267
|
+
|
268
|
+
The [gretel-trails](https://github.com/lassebunk/gretel-trails) gem can handle adding and hiding trails from the URL automatically. This makes it possible to link back to a different breadcrumb trail than the one specified in your breadcrumb, for example if you have a
|
269
|
+
store with products that have a default parent to the category breadcrumb, but when visiting from the reviews section, you want to link back to the reviews instead.
|
270
|
+
|
271
|
+
You can apply trails to select links by adding a simple JS selector (`js-append-trail` or another you choose), and after each page load it hides the trail from the URL, so the server sees it but the users don't.
|
272
|
+
|
273
|
+
Check out the gem [here](https://github.com/lassebunk/gretel-trails).
|
274
|
+
|
275
|
+
|
321
276
|
Upgrading from version 2.0 or below
|
322
277
|
-----------------------------------
|
323
278
|
|
data/gretel.gemspec
CHANGED
@@ -20,6 +20,4 @@ Gem::Specification.new do |gem|
|
|
20
20
|
gem.add_dependency "rails", ">= 3.2.0"
|
21
21
|
gem.add_development_dependency "rails", "~> 3.2.13"
|
22
22
|
gem.add_development_dependency "sqlite3"
|
23
|
-
gem.add_development_dependency "fakeredis", "~> 0.4.2"
|
24
|
-
gem.add_development_dependency "timecop", "~> 0.6.3"
|
25
23
|
end
|
@@ -8,12 +8,5 @@ module Gretel
|
|
8
8
|
def create_config_file
|
9
9
|
copy_file "breadcrumbs.rb", "config/breadcrumbs.rb"
|
10
10
|
end
|
11
|
-
|
12
|
-
desc "Creates an initializer with trail secret"
|
13
|
-
def create_initializer
|
14
|
-
initializer "gretel.rb" do
|
15
|
-
%{Gretel::Trail::UrlStore.secret = '#{SecureRandom.hex(64)}'}
|
16
|
-
end
|
17
|
-
end
|
18
11
|
end
|
19
12
|
end
|
data/lib/gretel/crumb.rb
CHANGED
@@ -0,0 +1,15 @@
|
|
1
|
+
Gretel::Crumbs.class_eval do
|
2
|
+
class << self
|
3
|
+
# Was used to lay out breadcrumbs in an initializer. Deprecated since v2.1.0
|
4
|
+
# and removed in v3.0.0. Will raise an exception if used. Put breadcrumbs in
|
5
|
+
# +config/breadcrumbs.rb+ instead (see
|
6
|
+
# https://github.com/lassebunk/gretel/blob/master/README.md for details).
|
7
|
+
def layout(&block)
|
8
|
+
raise (
|
9
|
+
"Gretel::Crumbs.layout was removed in Gretel version 3.0. " +
|
10
|
+
"Please put your breadcrumbs in `config/breadcrumbs.rb`. " +
|
11
|
+
"This will also automatically reload your breadcrumbs when you change them in the development environment. " +
|
12
|
+
"See https://github.com/lassebunk/gretel/blob/master/README.md for details.")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Gretel::Renderer.class_eval do
|
2
|
+
def options_for_render_with_show_root_alone(options = {})
|
3
|
+
options = options_for_render_without_show_root_alone(options)
|
4
|
+
if show_root_alone = options.delete(:show_root_alone)
|
5
|
+
Gretel.show_deprecation_warning "The :show_root_alone option is deprecated and will be removed in Gretel v4.0.0. Use `breadcrumbs(display_single_fragment: #{show_root_alone.inspect})` instead."
|
6
|
+
options[:display_single_fragment] = show_root_alone
|
7
|
+
end
|
8
|
+
options
|
9
|
+
end
|
10
|
+
|
11
|
+
alias_method_chain :options_for_render, :show_root_alone
|
12
|
+
end
|
data/lib/gretel/deprecated.rb
CHANGED
@@ -1,23 +1,2 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
class << self
|
4
|
-
# Lay out the breadcrumbs.
|
5
|
-
# Deprecated since v2.1.0 and removed in v3.0.0.
|
6
|
-
# Put breadcrumbs in +config/breadcrumbs.rb+ instead (see https://github.com/lassebunk/gretel/blob/master/README.md for details).
|
7
|
-
#
|
8
|
-
# Example:
|
9
|
-
# Gretel::Crumbs.layout do
|
10
|
-
# crumb :root do
|
11
|
-
# link "Home", root_path
|
12
|
-
# end
|
13
|
-
# end
|
14
|
-
def layout(&block)
|
15
|
-
raise (
|
16
|
-
"Gretel::Crumbs.layout has been deprecated was removed in Gretel version 3.0. " +
|
17
|
-
"Please put your breadcrumbs in `config/breadcrumbs.rb`. " +
|
18
|
-
"This will also automatically reload your breadcrumbs when you change them in the development environment. " +
|
19
|
-
"See https://github.com/lassebunk/gretel/blob/master/README.md for details.")
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
1
|
+
require "gretel/deprecated/layout"
|
2
|
+
require "gretel/deprecated/show_root_alone"
|
data/lib/gretel/renderer.rb
CHANGED
@@ -14,20 +14,6 @@ module Gretel
|
|
14
14
|
id: nil
|
15
15
|
}
|
16
16
|
|
17
|
-
STYLES = {
|
18
|
-
# Default style
|
19
|
-
default: { container_tag: :div, separator: " › " },
|
20
|
-
|
21
|
-
# Ordered list
|
22
|
-
ol: { container_tag: :ol, fragment_tag: :li },
|
23
|
-
|
24
|
-
# Unordered list
|
25
|
-
ul: { container_tag: :ul, fragment_tag: :li },
|
26
|
-
|
27
|
-
# Twitter Bootstrap
|
28
|
-
bootstrap: { container_tag: :ol, fragment_tag: :li, class: "breadcrumb", current_class: "active" }
|
29
|
-
}
|
30
|
-
|
31
17
|
def initialize(context, breadcrumb_key, *breadcrumb_args)
|
32
18
|
@context = context
|
33
19
|
@breadcrumb_key = breadcrumb_key
|
@@ -58,19 +44,27 @@ module Gretel
|
|
58
44
|
content_tag(options[:container_tag], html, id: options[:id], class: options[:class])
|
59
45
|
end
|
60
46
|
|
61
|
-
#
|
62
|
-
def
|
47
|
+
# Yields links with applied options.
|
48
|
+
def yield_links(options = {})
|
63
49
|
options = options_for_render(options)
|
64
|
-
links_for_render(options)
|
50
|
+
yield links_for_render(options)
|
65
51
|
end
|
66
52
|
|
67
|
-
# Returns
|
68
|
-
def
|
69
|
-
|
53
|
+
# Returns the parent breadcrumb.
|
54
|
+
def parent_breadcrumb(options = {})
|
55
|
+
options = options_for_render(options)
|
56
|
+
links_for_render(options)[-2]
|
57
|
+
end
|
58
|
+
|
59
|
+
# Yields the parent breadcrumb if any.
|
60
|
+
def yield_parent_breadcrumb(options = {})
|
61
|
+
if parent = parent_breadcrumb(options)
|
62
|
+
yield parent
|
63
|
+
end
|
70
64
|
end
|
71
65
|
|
72
66
|
# Proxy for +context.link_to+ that can be overridden by plugins.
|
73
|
-
def
|
67
|
+
def breadcrumb_link_to(*args)
|
74
68
|
context.link_to(*args)
|
75
69
|
end
|
76
70
|
|
@@ -81,25 +75,35 @@ module Gretel
|
|
81
75
|
# Returns merged options for rendering breadcrumbs.
|
82
76
|
def options_for_render(options = {})
|
83
77
|
style = options_for_style(options[:style] || DEFAULT_OPTIONS[:style])
|
84
|
-
|
85
|
-
|
86
|
-
if show_root_alone = options.delete(:show_root_alone)
|
87
|
-
Gretel.show_deprecation_warning "The :show_root_alone option is deprecated. Use `breadcrumbs(display_single_fragment: #{show_root_alone.inspect})` instead."
|
88
|
-
options[:display_single_fragment] = show_root_alone
|
89
|
-
end
|
90
|
-
|
91
|
-
options
|
78
|
+
DEFAULT_OPTIONS.merge(style).merge(options)
|
92
79
|
end
|
93
80
|
|
94
81
|
# Returns options for the given +style_key+ and raises an exception if it's not found.
|
95
82
|
def options_for_style(style_key)
|
96
|
-
if style =
|
83
|
+
if style = self.class.styles[style_key]
|
97
84
|
style
|
98
85
|
else
|
99
|
-
raise ArgumentError, "Breadcrumbs style #{style_key.inspect} not found. Use any of #{
|
86
|
+
raise ArgumentError, "Breadcrumbs style #{style_key.inspect} not found. Use any of #{self.class.styles.keys.inspect}."
|
100
87
|
end
|
101
88
|
end
|
102
89
|
|
90
|
+
# Array of links with applied options.
|
91
|
+
def links_for_render(options = {})
|
92
|
+
out = links.dup
|
93
|
+
|
94
|
+
# Handle autoroot
|
95
|
+
if options[:autoroot] && out.map(&:key).exclude?(:root) && Gretel::Crumbs.crumb_defined?(:root)
|
96
|
+
out.unshift *Gretel::Crumb.new(context, :root).links
|
97
|
+
end
|
98
|
+
|
99
|
+
# Handle show root alone
|
100
|
+
if out.size == 1 && !options[:display_single_fragment]
|
101
|
+
out.shift
|
102
|
+
end
|
103
|
+
|
104
|
+
out
|
105
|
+
end
|
106
|
+
|
103
107
|
# Array of links for the path of the breadcrumb.
|
104
108
|
# Also reloads the breadcrumb configuration if needed.
|
105
109
|
def links
|
@@ -119,8 +123,8 @@ module Gretel
|
|
119
123
|
links.last.url = request.fullpath
|
120
124
|
end
|
121
125
|
|
122
|
-
# Get
|
123
|
-
links.unshift *
|
126
|
+
# Get parent links
|
127
|
+
links.unshift *parent_links_for(crumb)
|
124
128
|
|
125
129
|
# Set last link to current
|
126
130
|
links.last.try(:current!)
|
@@ -132,36 +136,13 @@ module Gretel
|
|
132
136
|
end
|
133
137
|
end
|
134
138
|
|
135
|
-
# Returns parent links for the crumb
|
136
|
-
def
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
else
|
141
|
-
# Build parents
|
142
|
-
links = []
|
143
|
-
while crumb = crumb.parent
|
144
|
-
links.unshift *crumb.links
|
145
|
-
end
|
146
|
-
links
|
139
|
+
# Returns parent links for the crumb.
|
140
|
+
def parent_links_for(crumb)
|
141
|
+
links = []
|
142
|
+
while crumb = crumb.parent
|
143
|
+
links.unshift *crumb.links
|
147
144
|
end
|
148
|
-
|
149
|
-
|
150
|
-
# Array of links with applied options.
|
151
|
-
def links_for_render(options = {})
|
152
|
-
out = links.dup
|
153
|
-
|
154
|
-
# Handle autoroot
|
155
|
-
if options[:autoroot] && out.map(&:key).exclude?(:root) && Gretel::Crumbs.crumb_defined?(:root)
|
156
|
-
out.unshift *Gretel::Crumb.new(context, :root).links
|
157
|
-
end
|
158
|
-
|
159
|
-
# Handle show root alone
|
160
|
-
if out.size == 1 && !options[:display_single_fragment]
|
161
|
-
out.shift
|
162
|
-
end
|
163
|
-
|
164
|
-
out
|
145
|
+
links
|
165
146
|
end
|
166
147
|
|
167
148
|
# Renders HTML for a breadcrumb fragment, i.e. a breadcrumb link.
|
@@ -177,10 +158,10 @@ module Gretel
|
|
177
158
|
def render_semantic_fragment(fragment_tag, text, url, options = {})
|
178
159
|
if fragment_tag
|
179
160
|
text = content_tag(:span, text, itemprop: "title")
|
180
|
-
text =
|
161
|
+
text = breadcrumb_link_to(text, url, itemprop: "url") if url.present?
|
181
162
|
content_tag(fragment_tag, text, class: options[:class], itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
|
182
163
|
elsif url.present?
|
183
|
-
content_tag(:div,
|
164
|
+
content_tag(:div, breadcrumb_link_to(content_tag(:span, text, itemprop: "title"), url, class: options[:class], itemprop: "url"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
|
184
165
|
else
|
185
166
|
content_tag(:div, content_tag(:span, text, class: options[:class], itemprop: "title"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
|
186
167
|
end
|
@@ -189,10 +170,10 @@ module Gretel
|
|
189
170
|
# Renders regular, non-semantic fragment HTML.
|
190
171
|
def render_nonsemantic_fragment(fragment_tag, text, url, options = {})
|
191
172
|
if fragment_tag
|
192
|
-
text =
|
173
|
+
text = breadcrumb_link_to(text, url) if url.present?
|
193
174
|
content_tag(fragment_tag, text, class: options[:class])
|
194
175
|
elsif url.present?
|
195
|
-
|
176
|
+
breadcrumb_link_to(text, url, class: options[:class])
|
196
177
|
elsif options[:class].present?
|
197
178
|
content_tag(:span, text, class: options[:class])
|
198
179
|
else
|
@@ -204,5 +185,19 @@ module Gretel
|
|
204
185
|
def method_missing(method, *args, &block)
|
205
186
|
context.send(method, *args, &block)
|
206
187
|
end
|
188
|
+
|
189
|
+
class << self
|
190
|
+
# Registers a style for later use.
|
191
|
+
#
|
192
|
+
# Gretel::Renderer.register_style :ul, { container_tag: :ul, fragment_tag: :li }
|
193
|
+
def register_style(style_key, options)
|
194
|
+
styles[style_key] = options
|
195
|
+
end
|
196
|
+
|
197
|
+
# Hash of registered styles.
|
198
|
+
def styles
|
199
|
+
@styles ||= {}
|
200
|
+
end
|
201
|
+
end
|
207
202
|
end
|
208
203
|
end
|
data/lib/gretel/version.rb
CHANGED
data/lib/gretel/view_helpers.rb
CHANGED
@@ -25,7 +25,7 @@ module Gretel
|
|
25
25
|
# <% end %>
|
26
26
|
def breadcrumbs(options = {}, &block)
|
27
27
|
if block_given?
|
28
|
-
|
28
|
+
gretel_renderer.yield_links(options, &block)
|
29
29
|
else
|
30
30
|
gretel_renderer.render(options)
|
31
31
|
end
|
@@ -33,19 +33,13 @@ module Gretel
|
|
33
33
|
|
34
34
|
# Returns or yields parent breadcrumb (second-to-last in the trail) if it is present.
|
35
35
|
def parent_breadcrumb(options = {}, &block)
|
36
|
-
|
37
|
-
|
38
|
-
yield parent
|
36
|
+
if block_given?
|
37
|
+
gretel_renderer.yield_parent_breadcrumb(options, &block)
|
39
38
|
else
|
40
|
-
|
39
|
+
gretel_renderer.parent_breadcrumb(options)
|
41
40
|
end
|
42
41
|
end
|
43
42
|
|
44
|
-
# Encoded breadcrumb trail to be used in URLs.
|
45
|
-
def breadcrumb_trail
|
46
|
-
gretel_renderer.trail
|
47
|
-
end
|
48
|
-
|
49
43
|
private
|
50
44
|
|
51
45
|
# Reference to the Gretel breadcrumbs renderer.
|
data/lib/gretel.rb
CHANGED
@@ -2,7 +2,6 @@ require 'gretel/version'
|
|
2
2
|
require 'gretel/crumbs'
|
3
3
|
require 'gretel/crumb'
|
4
4
|
require 'gretel/link'
|
5
|
-
require 'gretel/trail'
|
6
5
|
require 'gretel/renderer'
|
7
6
|
require 'gretel/view_helpers'
|
8
7
|
require 'gretel/deprecated'
|
@@ -19,26 +18,6 @@ module Gretel
|
|
19
18
|
@breadcrumb_paths = paths
|
20
19
|
end
|
21
20
|
|
22
|
-
# Param to contain trail. See +Gretel::Trail.trail_param+ for details.
|
23
|
-
def trail_param
|
24
|
-
Gretel::Trail.trail_param
|
25
|
-
end
|
26
|
-
|
27
|
-
# Sets the trail param. See +Gretel::Trail.trail_param+ for details.
|
28
|
-
def trail_param=(param)
|
29
|
-
Gretel::Trail.trail_param = param
|
30
|
-
end
|
31
|
-
|
32
|
-
# Trail store. See +Gretel::Trail.store+ for details.
|
33
|
-
def trail_store
|
34
|
-
Gretel::Trail.store
|
35
|
-
end
|
36
|
-
|
37
|
-
# Sets the trail store. See +Gretel::Trail.store+ for details.
|
38
|
-
def trail_store=(store)
|
39
|
-
Gretel::Trail.store = store
|
40
|
-
end
|
41
|
-
|
42
21
|
# Whether to suppress deprecation warnings.
|
43
22
|
def suppress_deprecation_warnings?
|
44
23
|
!!@suppress_deprecation_warnings
|
@@ -62,13 +41,20 @@ module Gretel
|
|
62
41
|
@reload_environments ||= ["development"]
|
63
42
|
end
|
64
43
|
|
44
|
+
# Registers a style for later use.
|
45
|
+
#
|
46
|
+
# Gretel.register_style :ul, { container_tag: :ul, fragment_tag: :li }
|
47
|
+
def register_style(style, options)
|
48
|
+
Gretel::Renderer.register_style style, options
|
49
|
+
end
|
50
|
+
|
65
51
|
# Sets the Rails environment names with automatic configuration reload. Default is +["development"]+.
|
66
52
|
attr_writer :reload_environments
|
67
53
|
|
68
54
|
# Yields this +Gretel+ to be configured.
|
69
55
|
#
|
70
56
|
# Gretel.configure do |config|
|
71
|
-
# config.
|
57
|
+
# config.reload_environments << "staging"
|
72
58
|
# end
|
73
59
|
def configure
|
74
60
|
yield self
|
@@ -78,9 +64,22 @@ module Gretel
|
|
78
64
|
def reset!
|
79
65
|
instance_variables.each { |var| remove_instance_variable var }
|
80
66
|
Crumbs.reset!
|
81
|
-
Trail.reset!
|
82
67
|
end
|
83
68
|
end
|
84
69
|
end
|
85
70
|
|
71
|
+
Gretel.configure do |config|
|
72
|
+
# Default style
|
73
|
+
config.register_style :default, { container_tag: :div, separator: " › " }
|
74
|
+
|
75
|
+
# Ordered list
|
76
|
+
config.register_style :ol, { container_tag: :ol, fragment_tag: :li }
|
77
|
+
|
78
|
+
# Unordered list
|
79
|
+
config.register_style :ul, { container_tag: :ul, fragment_tag: :li }
|
80
|
+
|
81
|
+
# Twitter Bootstrap
|
82
|
+
config.register_style :bootstrap, { container_tag: :ol, fragment_tag: :li, class: "breadcrumb", current_class: "active" }
|
83
|
+
end
|
84
|
+
|
86
85
|
ActionView::Base.send :include, Gretel::ViewHelpers
|
data/test/dummy/db/schema.rb
CHANGED
@@ -11,16 +11,7 @@
|
|
11
11
|
#
|
12
12
|
# It's strongly recommended to check this file into your version control system.
|
13
13
|
|
14
|
-
ActiveRecord::Schema.define(:version =>
|
15
|
-
|
16
|
-
create_table "gretel_trails", :force => true do |t|
|
17
|
-
t.string "key", :limit => 40
|
18
|
-
t.text "value"
|
19
|
-
t.datetime "expires_at"
|
20
|
-
end
|
21
|
-
|
22
|
-
add_index "gretel_trails", ["expires_at"], :name => "index_gretel_trails_on_expires_at"
|
23
|
-
add_index "gretel_trails", ["key"], :name => "index_gretel_trails_on_key", :unique => true
|
14
|
+
ActiveRecord::Schema.define(:version => 20130122163051) do
|
24
15
|
|
25
16
|
create_table "issues", :force => true do |t|
|
26
17
|
t.string "title"
|
data/test/gretel_test.rb
CHANGED
@@ -14,9 +14,9 @@ class GretelTest < ActiveSupport::TestCase
|
|
14
14
|
|
15
15
|
test "configuration block" do
|
16
16
|
Gretel.configure do |config|
|
17
|
-
config.
|
17
|
+
config.reload_environments << "staging"
|
18
18
|
end
|
19
19
|
|
20
|
-
assert_equal
|
20
|
+
assert_equal ["development", "staging"], Gretel.reload_environments
|
21
21
|
end
|
22
22
|
end
|
data/test/helper_methods_test.rb
CHANGED
@@ -7,7 +7,6 @@ class HelperMethodsTest < ActionView::TestCase
|
|
7
7
|
|
8
8
|
setup do
|
9
9
|
Gretel.reset!
|
10
|
-
Gretel::Trail::UrlStore.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
|
11
10
|
end
|
12
11
|
|
13
12
|
# Breadcrumb generation
|
@@ -296,39 +295,6 @@ class HelperMethodsTest < ActionView::TestCase
|
|
296
295
|
end
|
297
296
|
end
|
298
297
|
|
299
|
-
# Trails
|
300
|
-
|
301
|
-
test "trail helper" do
|
302
|
-
breadcrumb :basic
|
303
|
-
|
304
|
-
assert_equal "667ea523f92bdb3a086494575b18f587170e482b_W1siYmFzaWMiLCJBYm91dCIsMCwiL2Fib3V0Il1d", breadcrumb_trail
|
305
|
-
end
|
306
|
-
|
307
|
-
test "loading trail" do
|
308
|
-
params[:trail] = "667ea523f92bdb3a086494575b18f587170e482b_W1siYmFzaWMiLCJBYm91dCIsMCwiL2Fib3V0Il1d"
|
309
|
-
breadcrumb :multiple_links
|
310
|
-
|
311
|
-
assert_equal %{<div class="breadcrumbs"><a href="/">Home</a> › <a href="/about">About</a> › <a href="/about/contact">Contact</a> › <span class="current">Contact form</span></div>},
|
312
|
-
breadcrumbs
|
313
|
-
end
|
314
|
-
|
315
|
-
test "different trail param" do
|
316
|
-
Gretel.trail_param = :mytest
|
317
|
-
params[:mytest] = "667ea523f92bdb3a086494575b18f587170e482b_W1siYmFzaWMiLCJBYm91dCIsMCwiL2Fib3V0Il1d"
|
318
|
-
breadcrumb :multiple_links
|
319
|
-
|
320
|
-
assert_equal %{<div class="breadcrumbs"><a href="/">Home</a> › <a href="/about">About</a> › <a href="/about/contact">Contact</a> › <span class="current">Contact form</span></div>},
|
321
|
-
breadcrumbs
|
322
|
-
end
|
323
|
-
|
324
|
-
test "unknown trail" do
|
325
|
-
params[:trail] = "notfound"
|
326
|
-
breadcrumb :multiple_links
|
327
|
-
|
328
|
-
assert_equal %{<div class="breadcrumbs"><a href="/">Home</a> › <a href="/about/contact">Contact</a> › <span class="current">Contact form</span></div>},
|
329
|
-
breadcrumbs
|
330
|
-
end
|
331
|
-
|
332
298
|
# Configuration reload
|
333
299
|
|
334
300
|
test "reload configuration when file is changed" do
|
data/test/test_helper.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: gretel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0.
|
4
|
+
version: 3.0.0.beta5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Lasse Bunk
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-10-
|
11
|
+
date: 2013-10-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rails
|
@@ -52,34 +52,6 @@ dependencies:
|
|
52
52
|
- - '>='
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
|
-
- !ruby/object:Gem::Dependency
|
56
|
-
name: fakeredis
|
57
|
-
requirement: !ruby/object:Gem::Requirement
|
58
|
-
requirements:
|
59
|
-
- - ~>
|
60
|
-
- !ruby/object:Gem::Version
|
61
|
-
version: 0.4.2
|
62
|
-
type: :development
|
63
|
-
prerelease: false
|
64
|
-
version_requirements: !ruby/object:Gem::Requirement
|
65
|
-
requirements:
|
66
|
-
- - ~>
|
67
|
-
- !ruby/object:Gem::Version
|
68
|
-
version: 0.4.2
|
69
|
-
- !ruby/object:Gem::Dependency
|
70
|
-
name: timecop
|
71
|
-
requirement: !ruby/object:Gem::Requirement
|
72
|
-
requirements:
|
73
|
-
- - ~>
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: 0.6.3
|
76
|
-
type: :development
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ~>
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: 0.6.3
|
83
55
|
description: Gretel is a Ruby on Rails plugin that makes it easy yet flexible to create
|
84
56
|
breadcrumbs.
|
85
57
|
email:
|
@@ -99,21 +71,14 @@ files:
|
|
99
71
|
- lib/generators/gretel/USAGE
|
100
72
|
- lib/generators/gretel/install_generator.rb
|
101
73
|
- lib/generators/gretel/templates/breadcrumbs.rb
|
102
|
-
- lib/generators/gretel/templates/trail_migration.rb
|
103
|
-
- lib/generators/gretel/trail/migration_generator.rb
|
104
74
|
- lib/gretel.rb
|
105
75
|
- lib/gretel/crumb.rb
|
106
76
|
- lib/gretel/crumbs.rb
|
107
77
|
- lib/gretel/deprecated.rb
|
78
|
+
- lib/gretel/deprecated/layout.rb
|
79
|
+
- lib/gretel/deprecated/show_root_alone.rb
|
108
80
|
- lib/gretel/link.rb
|
109
81
|
- lib/gretel/renderer.rb
|
110
|
-
- lib/gretel/trail.rb
|
111
|
-
- lib/gretel/trail/stores.rb
|
112
|
-
- lib/gretel/trail/stores/active_record_store.rb
|
113
|
-
- lib/gretel/trail/stores/redis_store.rb
|
114
|
-
- lib/gretel/trail/stores/store.rb
|
115
|
-
- lib/gretel/trail/stores/url_store.rb
|
116
|
-
- lib/gretel/trail/tasks.rb
|
117
82
|
- lib/gretel/version.rb
|
118
83
|
- lib/gretel/view_helpers.rb
|
119
84
|
- test/deprecated_test.rb
|
@@ -146,7 +111,6 @@ files:
|
|
146
111
|
- test/dummy/config/routes.rb
|
147
112
|
- test/dummy/db/migrate/20130122163007_create_projects.rb
|
148
113
|
- test/dummy/db/migrate/20130122163051_create_issues.rb
|
149
|
-
- test/dummy/db/migrate/20131015194052_create_gretel_trails.rb
|
150
114
|
- test/dummy/db/schema.rb
|
151
115
|
- test/dummy/lib/assets/.gitkeep
|
152
116
|
- test/dummy/log/.gitkeep
|
@@ -160,10 +124,6 @@ files:
|
|
160
124
|
- test/gretel_test.rb
|
161
125
|
- test/helper_methods_test.rb
|
162
126
|
- test/test_helper.rb
|
163
|
-
- test/trails/active_record_store_test.rb
|
164
|
-
- test/trails/redis_store_test.rb
|
165
|
-
- test/trails/trail_test.rb
|
166
|
-
- test/trails/url_store_test.rb
|
167
127
|
homepage: http://github.com/lassebunk/gretel
|
168
128
|
licenses:
|
169
129
|
- MIT
|
@@ -219,7 +179,6 @@ test_files:
|
|
219
179
|
- test/dummy/config/routes.rb
|
220
180
|
- test/dummy/db/migrate/20130122163007_create_projects.rb
|
221
181
|
- test/dummy/db/migrate/20130122163051_create_issues.rb
|
222
|
-
- test/dummy/db/migrate/20131015194052_create_gretel_trails.rb
|
223
182
|
- test/dummy/db/schema.rb
|
224
183
|
- test/dummy/lib/assets/.gitkeep
|
225
184
|
- test/dummy/log/.gitkeep
|
@@ -233,7 +192,3 @@ test_files:
|
|
233
192
|
- test/gretel_test.rb
|
234
193
|
- test/helper_methods_test.rb
|
235
194
|
- test/test_helper.rb
|
236
|
-
- test/trails/active_record_store_test.rb
|
237
|
-
- test/trails/redis_store_test.rb
|
238
|
-
- test/trails/trail_test.rb
|
239
|
-
- test/trails/url_store_test.rb
|
@@ -1,11 +0,0 @@
|
|
1
|
-
class CreateGretelTrails < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :gretel_trails do |t|
|
4
|
-
t.string :key, limit: 40
|
5
|
-
t.text :value
|
6
|
-
t.datetime :expires_at
|
7
|
-
end
|
8
|
-
add_index :gretel_trails, :key, unique: true
|
9
|
-
add_index :gretel_trails, :expires_at
|
10
|
-
end
|
11
|
-
end
|
@@ -1,20 +0,0 @@
|
|
1
|
-
require 'rails/generators'
|
2
|
-
|
3
|
-
module Gretel
|
4
|
-
module Trail
|
5
|
-
class MigrationGenerator < Rails::Generators::Base
|
6
|
-
include Rails::Generators::Migration
|
7
|
-
|
8
|
-
source_root File.expand_path('../../templates', __FILE__)
|
9
|
-
|
10
|
-
def self.next_migration_number(path)
|
11
|
-
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
12
|
-
end
|
13
|
-
|
14
|
-
desc "Creates a migration for a table to store trail data"
|
15
|
-
def create_migration
|
16
|
-
migration_template "trail_migration.rb", "db/migrate/create_gretel_trails.rb"
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Gretel
|
2
|
-
module Trail
|
3
|
-
class ActiveRecordStore < Store
|
4
|
-
class << self
|
5
|
-
# Number of seconds to keep the trails in the database.
|
6
|
-
# Default: +1.day+
|
7
|
-
def expires_in
|
8
|
-
@expires_in ||= 1.day
|
9
|
-
end
|
10
|
-
|
11
|
-
# Sets the number of seconds to keep the trails in the database.
|
12
|
-
attr_writer :expires_in
|
13
|
-
|
14
|
-
# Save array to database.
|
15
|
-
def save(array)
|
16
|
-
json = array.to_json
|
17
|
-
key = Digest::SHA1.hexdigest(json)
|
18
|
-
GretelTrail.set(key, array, expires_in.from_now)
|
19
|
-
key
|
20
|
-
end
|
21
|
-
|
22
|
-
# Retrieve array from database.
|
23
|
-
def retrieve(key)
|
24
|
-
GretelTrail.get(key)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Delete expired keys.
|
28
|
-
def delete_expired
|
29
|
-
GretelTrail.delete_expired
|
30
|
-
end
|
31
|
-
|
32
|
-
# Gets the number of trails stored in the database.
|
33
|
-
def key_count
|
34
|
-
GretelTrail.count
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
class GretelTrail < ActiveRecord::Base
|
39
|
-
serialize :value, Array
|
40
|
-
|
41
|
-
def self.get(key)
|
42
|
-
find_by_key(key).try(:value)
|
43
|
-
end
|
44
|
-
|
45
|
-
def self.set(key, value, expires_at)
|
46
|
-
find_or_initialize_by_key(key).tap do |rec|
|
47
|
-
rec.value = value
|
48
|
-
rec.expires_at = expires_at
|
49
|
-
rec.save
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def self.delete_expired
|
54
|
-
delete_all(["expires_at < ?", Time.now])
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
end
|
59
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module Gretel
|
2
|
-
module Trail
|
3
|
-
class RedisStore < Store
|
4
|
-
class << self
|
5
|
-
# Options to connect to Redis.
|
6
|
-
def connect_options
|
7
|
-
@connect_options ||= {}
|
8
|
-
end
|
9
|
-
|
10
|
-
# Sets the Redis connect options.
|
11
|
-
attr_writer :connect_options
|
12
|
-
|
13
|
-
# Number of seconds to keep the trails in Redis.
|
14
|
-
# Default: +1.day+
|
15
|
-
def expires_in
|
16
|
-
@expires_in ||= 1.day
|
17
|
-
end
|
18
|
-
|
19
|
-
# Sets the number of seconds to keep the trails in Redis.
|
20
|
-
attr_writer :expires_in
|
21
|
-
|
22
|
-
# Save array to Redis.
|
23
|
-
def save(array)
|
24
|
-
json = array.to_json
|
25
|
-
key = Digest::SHA1.hexdigest(json)
|
26
|
-
redis.setex redis_key_for(key), expires_in, json
|
27
|
-
key
|
28
|
-
end
|
29
|
-
|
30
|
-
# Retrieve array from Redis.
|
31
|
-
def retrieve(key)
|
32
|
-
if json = redis.get(redis_key_for(key))
|
33
|
-
JSON.parse(json)
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
# Reference to the Redis connection.
|
38
|
-
def redis
|
39
|
-
@redis ||= begin
|
40
|
-
raise "Redis needs to be installed in order for #{name} to use it. Please add `gem \"redis\"` to your Gemfile." unless defined?(Redis)
|
41
|
-
Redis.new(connect_options)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
# Key to be stored in Redis.
|
48
|
-
def redis_key_for(key)
|
49
|
-
"gretel:trail:#{key}"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
@@ -1,49 +0,0 @@
|
|
1
|
-
module Gretel
|
2
|
-
module Trail
|
3
|
-
class Store
|
4
|
-
class << self
|
5
|
-
# Save an encoded array to the store. It must return the trail key that
|
6
|
-
# can later be used to retrieve the array from the store.
|
7
|
-
def save(array)
|
8
|
-
raise "#{name} must implement #save to be able to save trails."
|
9
|
-
end
|
10
|
-
|
11
|
-
# Retrieve an encoded array from the store based on the saved key.
|
12
|
-
# It must return either the array, or nil if the key was not found.
|
13
|
-
def retrieve(key)
|
14
|
-
raise "#{name} must implement #retrieve to be able to retrieve trails."
|
15
|
-
end
|
16
|
-
|
17
|
-
# Deletes expired keys from the store.
|
18
|
-
def delete_expired
|
19
|
-
raise "#{name} doesn't support deleting expired keys."
|
20
|
-
end
|
21
|
-
|
22
|
-
# Gets the number of stored trail keys.
|
23
|
-
def key_count
|
24
|
-
raise "#{name} doesn't support counting trail keys."
|
25
|
-
end
|
26
|
-
|
27
|
-
# Encode array of +links+ to unique trail key.
|
28
|
-
def encode(links)
|
29
|
-
arr = links.map { |link| [link.key, link.text, (link.text.html_safe? ? 1 : 0), link.url] }
|
30
|
-
save(arr)
|
31
|
-
end
|
32
|
-
|
33
|
-
# Decode unique trail key to array of links.
|
34
|
-
def decode(key)
|
35
|
-
if arr = retrieve(key)
|
36
|
-
arr.map { |key, text, html_safe, url| Link.new(key.to_sym, (html_safe == 1 ? text.html_safe : text), url) }
|
37
|
-
else
|
38
|
-
[]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
# Resets all changes made to the store. Used for testing.
|
43
|
-
def reset!
|
44
|
-
instance_variables.each { |var| remove_instance_variable var }
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
@@ -1,57 +0,0 @@
|
|
1
|
-
module Gretel
|
2
|
-
module Trail
|
3
|
-
class UrlStore < Store
|
4
|
-
class << self
|
5
|
-
# Secret used for crypting trail in URL that should be set to something
|
6
|
-
# unguessable. This is required when using trails, for the reason that
|
7
|
-
# unencrypted trails would be vulnerable to cross-site scripting attacks.
|
8
|
-
attr_accessor :secret
|
9
|
-
|
10
|
-
# Securely encodes encoded array to a trail string to be used in URL.
|
11
|
-
def save(array)
|
12
|
-
base64 = encode_base64(array)
|
13
|
-
hash = generate_hash(base64)
|
14
|
-
|
15
|
-
[hash, base64].join("_")
|
16
|
-
end
|
17
|
-
|
18
|
-
# Securely decodes a URL trail string to encoded array.
|
19
|
-
def retrieve(key)
|
20
|
-
hash, base64 = key.split("_", 2)
|
21
|
-
|
22
|
-
if base64.blank?
|
23
|
-
Rails.logger.info "[Gretel] Trail decode failed: No Base64 in trail"
|
24
|
-
[]
|
25
|
-
elsif hash == generate_hash(base64)
|
26
|
-
decode_base64(base64)
|
27
|
-
else
|
28
|
-
Rails.logger.info "[Gretel] Trail decode failed: Invalid hash '#{hash}' in trail"
|
29
|
-
[]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
private
|
34
|
-
|
35
|
-
# Encodes links array to Base64, internally using JSON for serialization.
|
36
|
-
def encode_base64(array)
|
37
|
-
Base64.urlsafe_encode64(array.to_json)
|
38
|
-
end
|
39
|
-
|
40
|
-
# Decodes links array from Base64.
|
41
|
-
def decode_base64(base64)
|
42
|
-
json = Base64.urlsafe_decode64(base64)
|
43
|
-
JSON.parse(json)
|
44
|
-
rescue
|
45
|
-
Rails.logger.info "[Gretel] Trail decode failed: Invalid Base64 '#{base64}' in trail"
|
46
|
-
[]
|
47
|
-
end
|
48
|
-
|
49
|
-
# Generates a salted hash of +base64+.
|
50
|
-
def generate_hash(base64)
|
51
|
-
raise "#{name}.secret is not set. Please set it to an unguessable string, e.g. from `rake secret`, or use `rails generate gretel:install` to generate and set it automatically." if secret.blank?
|
52
|
-
Digest::SHA1.hexdigest([base64, secret].join)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
data/lib/gretel/trail/stores.rb
DELETED
data/lib/gretel/trail/tasks.rb
DELETED
data/lib/gretel/trail.rb
DELETED
@@ -1,68 +0,0 @@
|
|
1
|
-
require "gretel/trail/stores"
|
2
|
-
require "gretel/trail/tasks"
|
3
|
-
|
4
|
-
module Gretel
|
5
|
-
module Trail
|
6
|
-
STORES = {
|
7
|
-
url: UrlStore,
|
8
|
-
db: ActiveRecordStore,
|
9
|
-
redis: RedisStore
|
10
|
-
}
|
11
|
-
|
12
|
-
class << self
|
13
|
-
# Gets the store that is used to encode and decode trails.
|
14
|
-
# Default: +Gretel::Trail::UrlStore+
|
15
|
-
def store
|
16
|
-
@store ||= UrlStore
|
17
|
-
end
|
18
|
-
|
19
|
-
# Sets the store that is used to encode and decode trails.
|
20
|
-
# Can be a subclass of +Gretel::Trail::Store+, or a symbol: +:url+, +:db+, or +:redis+.
|
21
|
-
def store=(value)
|
22
|
-
if value.is_a?(Symbol)
|
23
|
-
klass = STORES[value]
|
24
|
-
raise ArgumentError, "Unknown Gretel::Trail.store #{value.inspect}. Use any of #{STORES.inspect}." unless klass
|
25
|
-
self.store = klass
|
26
|
-
else
|
27
|
-
@store = value
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
# Uses the store to encode an array of links to a unique key that can be used in URLs.
|
32
|
-
def encode(links)
|
33
|
-
store.encode(links)
|
34
|
-
end
|
35
|
-
|
36
|
-
# Uses the store to decode a unique key to an array of links.
|
37
|
-
def decode(key)
|
38
|
-
store.decode(key)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Deletes expired keys from the store.
|
42
|
-
# Not all stores support expiring keys, and will raise an exception if they don't.
|
43
|
-
def delete_expired
|
44
|
-
store.delete_expired
|
45
|
-
end
|
46
|
-
|
47
|
-
# Returns the current number of trails in the store.
|
48
|
-
# Not all stores support counting keys, and will raise an exception if they don't.
|
49
|
-
def count
|
50
|
-
store.key_count
|
51
|
-
end
|
52
|
-
|
53
|
-
# Name of trail param. Default: +:trail+.
|
54
|
-
def trail_param
|
55
|
-
@trail_param ||= :trail
|
56
|
-
end
|
57
|
-
|
58
|
-
# Sets the trail param.
|
59
|
-
attr_writer :trail_param
|
60
|
-
|
61
|
-
# Resets all changes made to +Gretel::Trail+. Used for testing.
|
62
|
-
def reset!
|
63
|
-
instance_variables.each { |var| remove_instance_variable var }
|
64
|
-
STORES.each_value(&:reset!)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,11 +0,0 @@
|
|
1
|
-
class CreateGretelTrails < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :gretel_trails do |t|
|
4
|
-
t.string :key, limit: 40
|
5
|
-
t.text :value
|
6
|
-
t.datetime :expires_at
|
7
|
-
end
|
8
|
-
add_index :gretel_trails, :key, unique: true
|
9
|
-
add_index :gretel_trails, :expires_at
|
10
|
-
end
|
11
|
-
end
|
@@ -1,52 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class ActiveRecordStoreTest < ActiveSupport::TestCase
|
4
|
-
setup do
|
5
|
-
Gretel.reset!
|
6
|
-
Gretel::Trail.store = :db
|
7
|
-
|
8
|
-
@links = [
|
9
|
-
[:root, "Home", "/"],
|
10
|
-
[:store, "Store <b>Test</b>".html_safe, "/store"],
|
11
|
-
[:search, "Search", "/store/search?q=test"]
|
12
|
-
]
|
13
|
-
end
|
14
|
-
|
15
|
-
test "defaults" do
|
16
|
-
assert_equal 1.day, Gretel::Trail::ActiveRecordStore.expires_in
|
17
|
-
end
|
18
|
-
|
19
|
-
test "encoding" do
|
20
|
-
assert_equal "684c211441e72225cee89477a2d1f59e657c9e26",
|
21
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
22
|
-
end
|
23
|
-
|
24
|
-
test "decoding" do
|
25
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
26
|
-
decoded = Gretel::Trail.decode("684c211441e72225cee89477a2d1f59e657c9e26")
|
27
|
-
assert_equal @links, decoded.map { |link| [link.key, link.text, link.url] }
|
28
|
-
assert_equal [false, true, false], decoded.map { |link| link.text.html_safe? }
|
29
|
-
end
|
30
|
-
|
31
|
-
test "invalid trail" do
|
32
|
-
assert_equal [], Gretel::Trail.decode("asdgasdg")
|
33
|
-
end
|
34
|
-
|
35
|
-
test "delete expired" do
|
36
|
-
10.times { Gretel::Trail.encode([Gretel::Link.new(:test, SecureRandom.hex(20), "/test")]) }
|
37
|
-
assert_equal 10, Gretel::Trail.count
|
38
|
-
|
39
|
-
Gretel::Trail.delete_expired
|
40
|
-
assert_equal 10, Gretel::Trail.count
|
41
|
-
|
42
|
-
Timecop.travel(14.hours.from_now) do
|
43
|
-
5.times { Gretel::Trail.encode([Gretel::Link.new(:test, SecureRandom.hex(20), "/test")]) }
|
44
|
-
assert_equal 15, Gretel::Trail.count
|
45
|
-
end
|
46
|
-
|
47
|
-
Timecop.travel(25.hours.from_now) do
|
48
|
-
Gretel::Trail.delete_expired
|
49
|
-
assert_equal 5, Gretel::Trail.count
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
require 'fakeredis'
|
3
|
-
|
4
|
-
class RedisStoreTest < ActiveSupport::TestCase
|
5
|
-
setup do
|
6
|
-
Gretel.reset!
|
7
|
-
Gretel::Trail.store = :redis
|
8
|
-
|
9
|
-
@links = [
|
10
|
-
[:root, "Home", "/"],
|
11
|
-
[:store, "Store <b>Test</b>".html_safe, "/store"],
|
12
|
-
[:search, "Search", "/store/search?q=test"]
|
13
|
-
]
|
14
|
-
end
|
15
|
-
|
16
|
-
test "defaults" do
|
17
|
-
assert_equal 1.day, Gretel::Trail::RedisStore.expires_in
|
18
|
-
end
|
19
|
-
|
20
|
-
test "encoding" do
|
21
|
-
assert_equal "684c211441e72225cee89477a2d1f59e657c9e26",
|
22
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
23
|
-
end
|
24
|
-
|
25
|
-
test "decoding" do
|
26
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
27
|
-
decoded = Gretel::Trail.decode("684c211441e72225cee89477a2d1f59e657c9e26")
|
28
|
-
assert_equal @links, decoded.map { |link| [link.key, link.text, link.url] }
|
29
|
-
assert_equal [false, true, false], decoded.map { |link| link.text.html_safe? }
|
30
|
-
end
|
31
|
-
|
32
|
-
test "invalid trail" do
|
33
|
-
assert_equal [], Gretel::Trail.decode("asdgasdg")
|
34
|
-
end
|
35
|
-
end
|
data/test/trails/trail_test.rb
DELETED
@@ -1,27 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class TrailTest < ActiveSupport::TestCase
|
4
|
-
setup do
|
5
|
-
Gretel.reset!
|
6
|
-
end
|
7
|
-
|
8
|
-
test "defaults" do
|
9
|
-
assert_equal :trail, Gretel::Trail.trail_param
|
10
|
-
end
|
11
|
-
|
12
|
-
test "setting invalid store" do
|
13
|
-
assert_raises ArgumentError do
|
14
|
-
Gretel::Trail.store = :xx
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
test "setting store options on main module" do
|
19
|
-
assert_equal :trail, Gretel.trail_param
|
20
|
-
Gretel.trail_param = :other_param
|
21
|
-
assert_equal :other_param, Gretel::Trail.trail_param
|
22
|
-
|
23
|
-
assert_equal Gretel::Trail::UrlStore, Gretel.trail_store
|
24
|
-
Gretel.trail_store = :redis
|
25
|
-
assert_equal Gretel::Trail::RedisStore, Gretel::Trail.store
|
26
|
-
end
|
27
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'test_helper'
|
2
|
-
|
3
|
-
class UrlStoreTest < ActiveSupport::TestCase
|
4
|
-
setup do
|
5
|
-
Gretel.reset!
|
6
|
-
|
7
|
-
Gretel::Trail.store = :url
|
8
|
-
Gretel::Trail::UrlStore.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
|
9
|
-
|
10
|
-
@links = [
|
11
|
-
[:root, "Home", "/"],
|
12
|
-
[:store, "Store <b>Test</b>".html_safe, "/store"],
|
13
|
-
[:search, "Search", "/store/search?q=test"]
|
14
|
-
]
|
15
|
-
end
|
16
|
-
|
17
|
-
test "encoding" do
|
18
|
-
assert_equal "5543214e6d7bbc3ba5209b2362cd7513d500f61b_W1sicm9vdCIsIkhvbWUiLDAsIi8iXSxbInN0b3JlIiwiU3RvcmUgPGI-VGVzdDwvYj4iLDEsIi9zdG9yZSJdLFsic2VhcmNoIiwiU2VhcmNoIiwwLCIvc3RvcmUvc2VhcmNoP3E9dGVzdCJdXQ==",
|
19
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
20
|
-
end
|
21
|
-
|
22
|
-
test "decoding" do
|
23
|
-
decoded = Gretel::Trail.decode("5543214e6d7bbc3ba5209b2362cd7513d500f61b_W1sicm9vdCIsIkhvbWUiLDAsIi8iXSxbInN0b3JlIiwiU3RvcmUgPGI-VGVzdDwvYj4iLDEsIi9zdG9yZSJdLFsic2VhcmNoIiwiU2VhcmNoIiwwLCIvc3RvcmUvc2VhcmNoP3E9dGVzdCJdXQ==")
|
24
|
-
assert_equal @links, decoded.map { |link| [link.key, link.text, link.url] }
|
25
|
-
assert_equal [false, true, false], decoded.map { |link| link.text.html_safe? }
|
26
|
-
end
|
27
|
-
|
28
|
-
test "invalid trail" do
|
29
|
-
assert_equal [], Gretel::Trail.decode("28f104524f5eaf6b3bd035710432fd2b9cbfd62c_X1sicm9vdCIsIkhvbWUiLDAsIi8iXSxbInN0b3JlIiwiU3RvcmUiLDAsIi9zdG9yZSJdLFsic2VhcmNoIiwiU2VhcmNoIiwwLCIvc3RvcmUvc2VhcmNoP3E9dGVzdCJdXQ==")
|
30
|
-
end
|
31
|
-
|
32
|
-
test "raises error if no secret set" do
|
33
|
-
Gretel::Trail::UrlStore.secret = nil
|
34
|
-
assert_raises RuntimeError do
|
35
|
-
Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
|
36
|
-
end
|
37
|
-
end
|
38
|
-
end
|