gretel 3.0.0.beta2 → 3.0.0.beta3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db6f1e42bb6b92c94ae0ea17349b87d352fda758
4
- data.tar.gz: 0b5532349fe0a6829bfd2e7d4c52aad04f7232a1
3
+ metadata.gz: a6dea6327b14a40f5f88c344548da90536778701
4
+ data.tar.gz: c527f98356d7ca6febb761ea239c030d3cfc6365
5
5
  SHA512:
6
- metadata.gz: a8975240e40fa018ba3a80ff04782657d3a13045ba293162655e0ddd8f3b33b2d6685e9695d4a8f46e7dd9e25cc169e183ee75412aae95d84282ea625c01a35c
7
- data.tar.gz: 105977c62283b95aa4c886ed38f85d2be8bb0ef64793c68673032bb416b9de284964de94d34a7cbe661d04c4a9f48bf7aeb3024c60c9e26f02ed84c2b59b1c4e
6
+ metadata.gz: e99745536c8bae608186322664689156e303f569961b1086278da7d66cf7ea2cdbcd84565eef961d6ab966d6044988057861656f7c6cec5b35e5a2905dfa53f6
7
+ data.tar.gz: e3eb531c6e4ac7e1a9e9c0d2e914f9b1321bdbb48f9aead24ff9a611fb3e5a4c93e6a26974c7fabd0529da3d6a89dbc7958af3f12449ad559c3b223f54610dc0
data/CHANGELOG.md CHANGED
@@ -10,6 +10,7 @@ Version 3.0
10
10
  * Breadcrumbs rendering is now done in a separate class to unclutter the view with helpers. The public API is still the same.
11
11
  * 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
12
  * 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
+ * Links yielded from `<%= breadcrumbs do |links| %>` now have a `current?` helper that returns true if the link is the last in the trail.
13
14
 
14
15
  Version 2.1
15
16
  -----------
data/README.md CHANGED
@@ -7,15 +7,15 @@ Gretel also supports [semantic breadcrumbs](http://support.google.com/webmasters
7
7
 
8
8
  Have fun! And please do write, if you (dis)like it – [lassebunk@gmail.com](mailto:lassebunk@gmail.com).
9
9
 
10
- New in version 3.0
11
- ------------------
10
+ New in version 3.0 :muscle:
11
+ ---------------------------
12
12
 
13
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 linking from the reviews section, you want to link back to the reviews instead.
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
15
  Read more about trails below.
16
16
  * 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
17
  * Defining breadcrumbs using `Gretel::Crumbs.layout do ... end` in an initializer has been removed. See below for details on how to upgrade.
18
- * The `:show_root_alone` option is now called `:display_single_fragment` and can be used to hide the breadcrumbs when there are only one link, also if it is not the root breadcrumb.
18
+ * 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
19
  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
20
 
21
21
  I hope you find these changes as useful as I did when I made them – if you have more suggestions, please create an [Issue](https://github.com/lassebunk/gretel/issues) or [Pull Request](https://github.com/lassebunk/gretel/pulls).
@@ -224,7 +224,7 @@ If you supply a block to the `breadcrumbs` method, it will yield an array with t
224
224
  <% if links.any? %>
225
225
  You are here:
226
226
  <% links.each do |link| %>
227
- <%= link_to link.text, link.url %> (<%= link.key %>)
227
+ <%= link_to link.text, link.url, class: (link.current? ? "current" : nil) %> (<%= link.key %>)
228
228
  <% end %>
229
229
  <% end %>
230
230
  <% end %>
@@ -235,7 +235,7 @@ Setting breadcrumb trails
235
235
 
236
236
  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.
237
237
 
238
- An example is if you have a store with products that have a default parent to their category, but when linking from the reviews section, you want to link back to the reviews instead.
238
+ 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.
239
239
 
240
240
  ### Initial setup
241
241
 
@@ -252,7 +252,7 @@ This will create an initializer in *config/initializers/gretel.rb* that will con
252
252
  If you want to do it manually, you can put the following in *config/initializers/gretel.rb*:
253
253
 
254
254
  ```
255
- Gretel::Trail.secret = 'your_key_here' # Must be changed to something else to be secure
255
+ Gretel::Trail::UrlStore.secret = 'your_key_here' # Must be changed to something else to be secure
256
256
  ```
257
257
 
258
258
  You can generate a key using `SecureRandom.hex(64)`.
@@ -287,9 +287,9 @@ Please use the trail functionality with care; the trails can get very long.
287
287
  Nice to know
288
288
  ------------
289
289
 
290
- ### Access to view helpers
290
+ ### Access to view methods
291
291
 
292
- When configuring breadcrumbs, you have access to all view helpers of the view where the breadcrumbs are inserted.
292
+ When configuring breadcrumbs inside a `crumb :xx do ... end` block, you have access to all methods that are normally accessible in the view where the breadcrumbs are inserted. This includes your view helpers, `params`, `request`, etc.
293
293
 
294
294
  ### Using multiple breadcrumb configuration files
295
295
 
data/Rakefile CHANGED
@@ -3,7 +3,7 @@ require 'bundler/gem_tasks'
3
3
 
4
4
  Rake::TestTask.new do |t|
5
5
  t.libs << "test"
6
- t.test_files = FileList['test/*_test.rb']
6
+ t.test_files = FileList['test/**/*_test.rb']
7
7
  t.verbose = true
8
8
  end
9
9
 
data/gretel.gemspec CHANGED
@@ -17,6 +17,9 @@ Gem::Specification.new do |gem|
17
17
  gem.test_files = gem.files.grep(%r{^test/})
18
18
  gem.require_paths = ["lib"]
19
19
 
20
+ gem.add_dependency "rails", ">= 3.2.0"
20
21
  gem.add_development_dependency "rails", "~> 3.2.13"
21
22
  gem.add_development_dependency "sqlite3"
23
+ gem.add_development_dependency "fakeredis", "~> 0.4.2"
24
+ gem.add_development_dependency "timecop", "~> 0.6.3"
22
25
  end
@@ -12,7 +12,7 @@ module Gretel
12
12
  desc "Creates an initializer with trail secret"
13
13
  def create_initializer
14
14
  initializer "gretel.rb" do
15
- %{Gretel::Trail.secret = '#{SecureRandom.hex(64)}'}
15
+ %{Gretel::Trail::UrlStore.secret = '#{SecureRandom.hex(64)}'}
16
16
  end
17
17
  end
18
18
  end
@@ -0,0 +1,11 @@
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
@@ -0,0 +1,20 @@
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
data/lib/gretel.rb CHANGED
@@ -19,6 +19,26 @@ module Gretel
19
19
  @breadcrumb_paths = paths
20
20
  end
21
21
 
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
+
22
42
  # Whether to suppress deprecation warnings.
23
43
  def suppress_deprecation_warnings?
24
44
  !!@suppress_deprecation_warnings
@@ -45,6 +65,15 @@ module Gretel
45
65
  # Sets the Rails environment names with automatic configuration reload. Default is +["development"]+.
46
66
  attr_writer :reload_environments
47
67
 
68
+ # Yields this +Gretel+ to be configured.
69
+ #
70
+ # Gretel.configure do |config|
71
+ # config.trail_param = :other_param
72
+ # end
73
+ def configure
74
+ yield self
75
+ end
76
+
48
77
  # Resets all changes made to +Gretel+, +Gretel::Crumbs+, and +Gretel::Trail+. Used for testing.
49
78
  def reset!
50
79
  instance_variables.each { |var| remove_instance_variable var }
data/lib/gretel/link.rb CHANGED
@@ -5,5 +5,13 @@ module Gretel
5
5
  def initialize(key, text, url)
6
6
  @key, @text, @url = key, text, url
7
7
  end
8
+
9
+ def current!
10
+ @current = true
11
+ end
12
+
13
+ def current?
14
+ !!@current
15
+ end
8
16
  end
9
17
  end
@@ -117,6 +117,9 @@ module Gretel
117
117
  # Get trail
118
118
  links.unshift *trail_for(crumb)
119
119
 
120
+ # Set last link to current
121
+ links.last.try(:current!)
122
+
120
123
  links
121
124
  else
122
125
  []
@@ -169,10 +172,10 @@ module Gretel
169
172
  def render_semantic_fragment(fragment_tag, text, url, options = {})
170
173
  if fragment_tag
171
174
  text = content_tag(:span, text, itemprop: "title")
172
- text = link_to(text, url, itemprop: "url") if url.present?
175
+ text = render_link(text, url, itemprop: "url") if url.present?
173
176
  content_tag(fragment_tag, text, class: options[:class], itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
174
177
  elsif url.present?
175
- content_tag(:div, link_to(content_tag(:span, text, itemprop: "title"), url, class: options[:class], itemprop: "url"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
178
+ content_tag(:div, render_link(content_tag(:span, text, itemprop: "title"), url, class: options[:class], itemprop: "url"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
176
179
  else
177
180
  content_tag(:div, content_tag(:span, text, class: options[:class], itemprop: "title"), itemscope: "", itemtype: "http://data-vocabulary.org/Breadcrumb")
178
181
  end
@@ -181,10 +184,10 @@ module Gretel
181
184
  # Renders regular, non-semantic fragment HTML.
182
185
  def render_nonsemantic_fragment(fragment_tag, text, url, options = {})
183
186
  if fragment_tag
184
- text = link_to(text, url) if url.present?
187
+ text = render_link(text, url) if url.present?
185
188
  content_tag(fragment_tag, text, class: options[:class])
186
189
  elsif url.present?
187
- link_to(text, url, class: options[:class])
190
+ render_link(text, url, class: options[:class])
188
191
  elsif options[:class].present?
189
192
  content_tag(:span, text, class: options[:class])
190
193
  else
@@ -192,6 +195,12 @@ module Gretel
192
195
  end
193
196
  end
194
197
 
198
+ # Renders a link. It is really just a proxy for +link_to+, but this can be
199
+ # used in plugins that want to change how links are rendered.
200
+ def render_link(name, url, options = {})
201
+ link_to(name, url, options)
202
+ end
203
+
195
204
  # Proxy to view context
196
205
  def method_missing(method, *args, &block)
197
206
  context.send(method, *args, &block)
data/lib/gretel/trail.rb CHANGED
@@ -1,32 +1,53 @@
1
+ require "gretel/trail/stores"
2
+ require "gretel/trail/tasks"
3
+
1
4
  module Gretel
2
5
  module Trail
6
+ STORES = {
7
+ url: UrlStore,
8
+ db: ActiveRecordStore,
9
+ redis: RedisStore
10
+ }
11
+
3
12
  class << self
4
- # Secret used for crypting trail in URL that should be set to something
5
- # unguessable. This is required when using trails, for the reason that
6
- # unencrypted trails would be vulnerable to cross-site scripting attacks.
7
- attr_accessor :secret
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
8
18
 
9
- # Securely encodes array of links to a trail string to be used in URL.
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.
10
32
  def encode(links)
11
- base64 = encode_base64(links)
12
- hash = generate_hash(base64)
33
+ store.encode(links)
34
+ end
13
35
 
14
- [hash, base64].join("_")
36
+ # Uses the store to decode a unique key to an array of links.
37
+ def decode(key)
38
+ store.decode(key)
15
39
  end
16
40
 
17
- # Securely decodes a URL trail string to array of links.
18
- def decode(trail)
19
- hash, base64 = trail.split("_", 2)
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
20
46
 
21
- if base64.blank?
22
- Rails.logger.info "[Gretel] Trail decode failed: No Base64 in trail"
23
- []
24
- elsif hash == generate_hash(base64)
25
- decode_base64(base64)
26
- else
27
- Rails.logger.info "[Gretel] Trail decode failed: Invalid hash '#{hash}' in trail"
28
- []
29
- end
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
30
51
  end
31
52
 
32
53
  # Name of trail param. Default: +:trail+.
@@ -34,35 +55,13 @@ module Gretel
34
55
  @trail_param ||= :trail
35
56
  end
36
57
 
58
+ # Sets the trail param.
37
59
  attr_writer :trail_param
38
60
 
39
61
  # Resets all changes made to +Gretel::Trail+. Used for testing.
40
62
  def reset!
41
63
  instance_variables.each { |var| remove_instance_variable var }
42
- end
43
-
44
- private
45
-
46
- # Encodes links array to Base64, internally using JSON for serialization.
47
- def encode_base64(links)
48
- arr = links.map { |link| [link.key, link.text, (link.text.html_safe? ? 1 : 0), link.url] }
49
- Base64.urlsafe_encode64(arr.to_json)
50
- end
51
-
52
- # Decodes links array from Base64.
53
- def decode_base64(base64)
54
- json = Base64.urlsafe_decode64(base64)
55
- arr = JSON.parse(json)
56
- arr.map { |key, text, html_safe, url| Link.new(key.to_sym, (html_safe == 1 ? text.html_safe : text), url) }
57
- rescue
58
- Rails.logger.info "[Gretel] Trail decode failed: Invalid Base64 '#{base64}' in trail"
59
- []
60
- end
61
-
62
- # Generates a salted hash of +base64+.
63
- def generate_hash(base64)
64
- raise "Gretel::Trail.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?
65
- Digest::SHA1.hexdigest([base64, secret].join)
64
+ STORES.each_value(&:reset!)
66
65
  end
67
66
  end
68
67
  end
@@ -0,0 +1,4 @@
1
+ require "gretel/trail/stores/store"
2
+ require "gretel/trail/stores/url_store"
3
+ require "gretel/trail/stores/active_record_store"
4
+ require "gretel/trail/stores/redis_store"
@@ -0,0 +1,59 @@
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
@@ -0,0 +1,54 @@
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
@@ -0,0 +1,49 @@
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
@@ -0,0 +1,57 @@
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
@@ -0,0 +1,5 @@
1
+ require "rake"
2
+
3
+ Rake::Task.define_task("gretel:trails:delete_expired" => :environment) do
4
+ Gretel::Trail.delete_expired
5
+ end
@@ -1,3 +1,3 @@
1
1
  module Gretel
2
- VERSION = "3.0.0.beta2"
2
+ VERSION = "3.0.0.beta3"
3
3
  end
@@ -0,0 +1,11 @@
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
@@ -11,19 +11,28 @@
11
11
  #
12
12
  # It's strongly recommended to check this file into your version control system.
13
13
 
14
- ActiveRecord::Schema.define(version: 20130122163051) do
14
+ ActiveRecord::Schema.define(:version => 20131015194052) do
15
15
 
16
- create_table "issues", force: true do |t|
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
24
+
25
+ create_table "issues", :force => true do |t|
17
26
  t.string "title"
18
27
  t.integer "project_id"
19
- t.datetime "created_at", null: false
20
- t.datetime "updated_at", null: false
28
+ t.datetime "created_at", :null => false
29
+ t.datetime "updated_at", :null => false
21
30
  end
22
31
 
23
- create_table "projects", force: true do |t|
32
+ create_table "projects", :force => true do |t|
24
33
  t.string "name"
25
- t.datetime "created_at", null: false
26
- t.datetime "updated_at", null: false
34
+ t.datetime "created_at", :null => false
35
+ t.datetime "updated_at", :null => false
27
36
  end
28
37
 
29
38
  end
data/test/gretel_test.rb CHANGED
@@ -11,4 +11,12 @@ class GretelTest < ActiveSupport::TestCase
11
11
  assert_equal ["development"], Gretel.reload_environments
12
12
  assert !Gretel.suppress_deprecation_warnings?
13
13
  end
14
+
15
+ test "configuration block" do
16
+ Gretel.configure do |config|
17
+ config.trail_param = :other_param
18
+ end
19
+
20
+ assert_equal :other_param, Gretel.trail_param
21
+ end
14
22
  end
@@ -7,7 +7,7 @@ class HelperMethodsTest < ActionView::TestCase
7
7
 
8
8
  setup do
9
9
  Gretel.reset!
10
- Gretel::Trail.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
10
+ Gretel::Trail::UrlStore.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
11
11
  end
12
12
 
13
13
  # Breadcrumb generation
@@ -153,6 +153,16 @@ class HelperMethodsTest < ActionView::TestCase
153
153
  [:multiple_links_with_parent, "Contact form", "/about/contact/form"]], out
154
154
  end
155
155
 
156
+ test "sets current on last link in array" do
157
+ breadcrumb :multiple_links_with_parent
158
+
159
+ out = breadcrumbs do |links|
160
+ links.map(&:current?)
161
+ end
162
+
163
+ assert_equal [false, false, false, true], out
164
+ end
165
+
156
166
  test "without link" do
157
167
  breadcrumb :without_link
158
168
  assert_equal %{<div class="breadcrumbs"><a href="/">Home</a> &rsaquo; Also without link &rsaquo; <span class="current">Without link</span></div>},
data/test/test_helper.rb CHANGED
@@ -3,6 +3,7 @@ ENV["RAILS_ENV"] = "test"
3
3
 
4
4
  require File.expand_path("../dummy/config/environment.rb", __FILE__)
5
5
  require "rails/test_help"
6
+ require "timecop"
6
7
 
7
8
  Rails.backtrace_cleaner.remove_silencers!
8
9
 
@@ -0,0 +1,52 @@
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
@@ -0,0 +1,35 @@
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
@@ -0,0 +1,27 @@
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,8 +1,12 @@
1
1
  require 'test_helper'
2
2
 
3
- class TrailTest < ActiveSupport::TestCase
3
+ class UrlStoreTest < ActiveSupport::TestCase
4
4
  setup do
5
- Gretel::Trail.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
5
+ Gretel.reset!
6
+
7
+ Gretel::Trail.store = :url
8
+ Gretel::Trail::UrlStore.secret = "128107d341e912db791d98bbe874a8250f784b0a0b4dbc5d5032c0fc1ca7bda9c6ece667bd18d23736ee833ea79384176faeb54d2e0d21012898dde78631cdf1"
9
+
6
10
  @links = [
7
11
  [:root, "Home", "/"],
8
12
  [:store, "Store <b>Test</b>".html_safe, "/store"],
@@ -10,10 +14,6 @@ class TrailTest < ActiveSupport::TestCase
10
14
  ]
11
15
  end
12
16
 
13
- test "defaults" do
14
- assert_equal :trail, Gretel::Trail.trail_param
15
- end
16
-
17
17
  test "encoding" do
18
18
  assert_equal "5543214e6d7bbc3ba5209b2362cd7513d500f61b_W1sicm9vdCIsIkhvbWUiLDAsIi8iXSxbInN0b3JlIiwiU3RvcmUgPGI-VGVzdDwvYj4iLDEsIi9zdG9yZSJdLFsic2VhcmNoIiwiU2VhcmNoIiwwLCIvc3RvcmUvc2VhcmNoP3E9dGVzdCJdXQ==",
19
19
  Gretel::Trail.encode(@links.map { |key, text, url| Gretel::Link.new(key, text, url) })
@@ -28,4 +28,11 @@ class TrailTest < ActiveSupport::TestCase
28
28
  test "invalid trail" do
29
29
  assert_equal [], Gretel::Trail.decode("28f104524f5eaf6b3bd035710432fd2b9cbfd62c_X1sicm9vdCIsIkhvbWUiLDAsIi8iXSxbInN0b3JlIiwiU3RvcmUiLDAsIi9zdG9yZSJdLFsic2VhcmNoIiwiU2VhcmNoIiwwLCIvc3RvcmUvc2VhcmNoP3E9dGVzdCJdXQ==")
30
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
31
38
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gretel
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.0.0.beta2
4
+ version: 3.0.0.beta3
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-12 00:00:00.000000000 Z
11
+ date: 2013-10-17 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.2.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.2.0
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rails
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,34 @@ dependencies:
38
52
  - - '>='
39
53
  - !ruby/object:Gem::Version
40
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
41
83
  description: Gretel is a Ruby on Rails plugin that makes it easy yet flexible to create
42
84
  breadcrumbs.
43
85
  email:
@@ -57,6 +99,8 @@ files:
57
99
  - lib/generators/gretel/USAGE
58
100
  - lib/generators/gretel/install_generator.rb
59
101
  - lib/generators/gretel/templates/breadcrumbs.rb
102
+ - lib/generators/gretel/templates/trail_migration.rb
103
+ - lib/generators/gretel/trail/migration_generator.rb
60
104
  - lib/gretel.rb
61
105
  - lib/gretel/crumb.rb
62
106
  - lib/gretel/crumbs.rb
@@ -64,6 +108,12 @@ files:
64
108
  - lib/gretel/link.rb
65
109
  - lib/gretel/renderer.rb
66
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
67
117
  - lib/gretel/version.rb
68
118
  - lib/gretel/view_helpers.rb
69
119
  - test/deprecated_test.rb
@@ -96,6 +146,7 @@ files:
96
146
  - test/dummy/config/routes.rb
97
147
  - test/dummy/db/migrate/20130122163007_create_projects.rb
98
148
  - test/dummy/db/migrate/20130122163051_create_issues.rb
149
+ - test/dummy/db/migrate/20131015194052_create_gretel_trails.rb
99
150
  - test/dummy/db/schema.rb
100
151
  - test/dummy/lib/assets/.gitkeep
101
152
  - test/dummy/log/.gitkeep
@@ -109,7 +160,10 @@ files:
109
160
  - test/gretel_test.rb
110
161
  - test/helper_methods_test.rb
111
162
  - test/test_helper.rb
112
- - test/trail_test.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
113
167
  homepage: http://github.com/lassebunk/gretel
114
168
  licenses:
115
169
  - MIT
@@ -165,6 +219,7 @@ test_files:
165
219
  - test/dummy/config/routes.rb
166
220
  - test/dummy/db/migrate/20130122163007_create_projects.rb
167
221
  - test/dummy/db/migrate/20130122163051_create_issues.rb
222
+ - test/dummy/db/migrate/20131015194052_create_gretel_trails.rb
168
223
  - test/dummy/db/schema.rb
169
224
  - test/dummy/lib/assets/.gitkeep
170
225
  - test/dummy/log/.gitkeep
@@ -178,4 +233,7 @@ test_files:
178
233
  - test/gretel_test.rb
179
234
  - test/helper_methods_test.rb
180
235
  - test/test_helper.rb
181
- - test/trail_test.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