url_keyed_object 0.1.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/VERSION +1 -1
- data/features/for_url_key-like_uses.feature +15 -1
- data/features/with_active_record-basic.feature +2 -2
- data/features/with_active_record-more-advanced.feature +58 -0
- data/lib/url_keyed_object/active_record.rb +53 -26
- data/lib/url_keyed_object.rb +7 -7
- data/spec/spec_helper.rb +2 -2
- data/spec/url_keyed_object/active_record_spec.rb +1 -1
- data/spec/url_keyed_object_spec.rb +16 -2
- metadata +3 -2
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
@@ -11,7 +11,7 @@ Feature: Using for things which are URL key-like
|
|
11
11
|
"""
|
12
12
|
Then @value should match /^[a-z0-9]{5}$/
|
13
13
|
|
14
|
-
Scenario: Generating and validating a URL key value
|
14
|
+
Scenario: Generating and validating a URL key value as part of object initialisation
|
15
15
|
Given this class:
|
16
16
|
"""
|
17
17
|
class HasOpaqueId
|
@@ -42,3 +42,17 @@ Feature: Using for things which are URL key-like
|
|
42
42
|
@object_with_id = HasOpaqueId.new
|
43
43
|
"""
|
44
44
|
Then @object_with_id.id should match /^[a-z0-9]{5}$/
|
45
|
+
|
46
|
+
Scenario: Generating a URL key value of arbitrary length
|
47
|
+
When I make a call to generate a URL key:
|
48
|
+
"""
|
49
|
+
@value = UrlKeyedObject.generate_unchecked_url_key(18)
|
50
|
+
"""
|
51
|
+
Then @value should match /^[a-z0-9]{18}$/
|
52
|
+
|
53
|
+
Scenario: Generating and validating a URL key value of arbitrary length
|
54
|
+
When I make a call to generate a valid URL key (with an admittedly nonsensical validation block):
|
55
|
+
"""
|
56
|
+
@value = UrlKeyedObject.generate_checked_url_key(18) { |value| value.length == 18 }
|
57
|
+
"""
|
58
|
+
Then @value should match /^[a-z0-9]{18}$/
|
@@ -20,7 +20,7 @@ Feature: Using with ActiveRecord
|
|
20
20
|
And this class:
|
21
21
|
"""
|
22
22
|
class Thing < ActiveRecord::Base
|
23
|
-
|
23
|
+
acts_as_url_keyed
|
24
24
|
end
|
25
25
|
"""
|
26
26
|
|
@@ -48,7 +48,7 @@ Feature: Using with ActiveRecord
|
|
48
48
|
And a warning should have been logged
|
49
49
|
|
50
50
|
Scenario: Attempting to set url_key ought to fail
|
51
|
-
When I make an instance
|
51
|
+
When I make an instance and set the value of url_key manually:
|
52
52
|
"""
|
53
53
|
@instance = Thing.new
|
54
54
|
@instance.url_key = 'abcde'
|
@@ -0,0 +1,58 @@
|
|
1
|
+
@db
|
2
|
+
Feature: More advanced usage with ActiveRecord
|
3
|
+
In order to generate and validate non-standard URL keys in an ActiveRecord::Base object
|
4
|
+
A Rails developer
|
5
|
+
Wants to include and use UrlKeyedObject
|
6
|
+
|
7
|
+
# The default case is for a 5-character ID, stored in the url_key column.
|
8
|
+
# Here we have an existing column we want to re-use, and we want a longer URL key
|
9
|
+
#
|
10
|
+
# calling acts_as_url_keyed and setting a couple of options is all that's needed.
|
11
|
+
Background:
|
12
|
+
Given a database, with this table defined:
|
13
|
+
"""
|
14
|
+
create_table :advanced_things do |t|
|
15
|
+
t.string :opaque_id, :null => false
|
16
|
+
|
17
|
+
t.timestamps
|
18
|
+
end
|
19
|
+
"""
|
20
|
+
And this class:
|
21
|
+
"""
|
22
|
+
class AdvancedThing < ActiveRecord::Base
|
23
|
+
acts_as_url_keyed :url_key_column => :opaque_id, :url_key_length => 8
|
24
|
+
end
|
25
|
+
"""
|
26
|
+
|
27
|
+
Scenario: An unsaved model object with UrlKeyedObject included
|
28
|
+
When I make a bare instance:
|
29
|
+
"""
|
30
|
+
@instance = AdvancedThing.new
|
31
|
+
"""
|
32
|
+
Then @instance.opaque_id should be nil
|
33
|
+
|
34
|
+
Scenario: A saved model object with an 8-character URL key on #opaque_id
|
35
|
+
When I make and save an instance:
|
36
|
+
"""
|
37
|
+
@instance = AdvancedThing.new
|
38
|
+
@instance.save!
|
39
|
+
"""
|
40
|
+
Then @instance.opaque_id should match /^[a-z0-9]{8}$/
|
41
|
+
|
42
|
+
Scenario: Attempting to mass-assign opaque_id ought to fail
|
43
|
+
When I make an instance using mass-assignment:
|
44
|
+
"""
|
45
|
+
@instance = AdvancedThing.new(:opaque_id => 'abcde')
|
46
|
+
"""
|
47
|
+
Then @instance.opaque_id should be nil
|
48
|
+
And a warning should have been logged
|
49
|
+
|
50
|
+
Scenario: Attempting to set opaque_id ought to fail
|
51
|
+
When I make an instance and set the value of opaque_id manually:
|
52
|
+
"""
|
53
|
+
@instance = AdvancedThing.new
|
54
|
+
@instance.opaque_id = 'abcde'
|
55
|
+
"""
|
56
|
+
Then @instance.opaque_id should be nil
|
57
|
+
And a warning should have been logged
|
58
|
+
|
@@ -1,40 +1,67 @@
|
|
1
1
|
require 'url_keyed_object'
|
2
|
+
require 'active_record'
|
2
3
|
|
3
4
|
module UrlKeyedObject
|
4
5
|
module ActiveRecord
|
5
|
-
#
|
6
|
-
#
|
7
|
-
#
|
8
|
-
#
|
6
|
+
# def url_key=(value)
|
7
|
+
# logger.warn('Attempt to set #url_key!')
|
8
|
+
# nil
|
9
|
+
# end
|
9
10
|
|
10
|
-
def to_param
|
11
|
-
url_key
|
12
|
-
end
|
13
|
-
|
14
|
-
def url_key=(value)
|
15
|
-
logger.warn('Attempt to set #url_key!')
|
16
|
-
nil
|
17
|
-
end
|
18
|
-
|
19
|
-
# --
|
20
|
-
# Validations
|
21
|
-
# ++
|
22
11
|
|
23
|
-
|
24
|
-
|
12
|
+
module ClassMethods
|
13
|
+
def url_key_column
|
14
|
+
@url_key_column ||= :url_key
|
15
|
+
end
|
16
|
+
|
17
|
+
def url_key_length
|
18
|
+
@url_key_length ||= 5
|
19
|
+
end
|
20
|
+
|
21
|
+
def acts_as_url_keyed(opts = {})
|
22
|
+
attr_writer :url_key_length
|
23
|
+
|
24
|
+
@url_key_column = opts[:url_key_column] if opts.has_key?(:url_key_column)
|
25
|
+
@url_key_length = opts[:url_key_length] if opts.has_key?(:url_key_length)
|
26
|
+
|
25
27
|
before_create :generate_valid_url_key
|
28
|
+
|
29
|
+
include InstanceMethods
|
30
|
+
|
31
|
+
define_method("#{url_key_column}=") { |value| logger.warn("Attempt to set ##{url_key_column}!"); nil }
|
26
32
|
end
|
27
33
|
end
|
28
34
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
35
|
+
module InstanceMethods
|
36
|
+
protected
|
37
|
+
|
38
|
+
def generate_valid_url_key
|
39
|
+
new_url_key = UrlKeyedObject.generate_checked_url_key(url_key_length) { |value| self.valid_url_key?(value) }
|
40
|
+
write_attribute(url_key_column, new_url_key)
|
41
|
+
end
|
42
|
+
|
43
|
+
def valid_url_key?(url_key)
|
44
|
+
self.class.send("find_by_#{url_key_column}", url_key).nil?
|
45
|
+
end
|
46
|
+
|
47
|
+
def url_key_column
|
48
|
+
self.class.url_key_column
|
49
|
+
end
|
50
|
+
|
51
|
+
def url_key_length
|
52
|
+
self.class.url_key_length
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
def to_param
|
57
|
+
send url_key_column
|
58
|
+
end
|
34
59
|
end
|
35
60
|
|
36
|
-
def
|
37
|
-
|
61
|
+
def self.included(model_class)
|
62
|
+
model_class.extend ClassMethods
|
38
63
|
end
|
39
64
|
end
|
40
|
-
end
|
65
|
+
end
|
66
|
+
|
67
|
+
ActiveRecord::Base.send :include, UrlKeyedObject::ActiveRecord
|
data/lib/url_keyed_object.rb
CHANGED
@@ -13,24 +13,24 @@ module UrlKeyedObject
|
|
13
13
|
end
|
14
14
|
|
15
15
|
# generates a url key from a random 5-digit base 31 number, without checking its uniqueness
|
16
|
-
def generate_unchecked_url_key
|
17
|
-
(1..
|
16
|
+
def generate_unchecked_url_key(length = 5)
|
17
|
+
(1..length).collect do
|
18
18
|
key_encoding[rand(31)]
|
19
19
|
end.join('')
|
20
20
|
end
|
21
21
|
|
22
22
|
# Keeps generating new URL keys until the passed-in block returns true
|
23
|
-
def generate_checked_url_key
|
24
|
-
key = generate_unchecked_url_key
|
23
|
+
def generate_checked_url_key(length = 5)
|
24
|
+
key = generate_unchecked_url_key(length)
|
25
25
|
while !yield(key)
|
26
|
-
key = generate_unchecked_url_key
|
26
|
+
key = generate_unchecked_url_key(length)
|
27
27
|
end
|
28
28
|
key
|
29
29
|
end
|
30
30
|
|
31
31
|
# Validate the well-formedness of a URL key
|
32
|
-
def well_formed_url_key?(url_key)
|
33
|
-
!url_key.match(
|
32
|
+
def well_formed_url_key?(url_key, length = 5)
|
33
|
+
!url_key.match(Regexp.new("^[0-9abcdefghjkmnpqrsvwxyz]{#{length.to_i}}$")).nil?
|
34
34
|
end
|
35
35
|
|
36
36
|
# decode a moderately dirty URL key
|
data/spec/spec_helper.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
$:.unshift(File.dirname(__FILE__))
|
2
2
|
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
require 'rubygems'
|
4
|
+
gem 'activerecord'
|
3
5
|
require 'url_keyed_object'
|
4
6
|
require 'url_keyed_object/active_record'
|
5
7
|
require 'spec'
|
6
8
|
require 'spec/autorun'
|
7
|
-
require 'rubygems'
|
8
|
-
gem 'activerecord'
|
9
9
|
|
10
10
|
Spec::Runner.configure do |config|
|
11
11
|
config.mock_with :mocha
|
@@ -5,6 +5,10 @@ describe UrlKeyedObject do
|
|
5
5
|
UrlKeyedObject.generate_unchecked_url_key.should match(/[0-9abcdefghjkmnpqrsvwxyz]{5}/)
|
6
6
|
end
|
7
7
|
|
8
|
+
it "should be able to generate an arbitrary-length URL key" do
|
9
|
+
UrlKeyedObject.generate_unchecked_url_key(7).should match(/[0-9abcdefghjkmnpqrsvwxyz]{7}/)
|
10
|
+
end
|
11
|
+
|
8
12
|
describe "decoding a moderately dirty URL key (case insensitivity, ambiguous character replacement)" do
|
9
13
|
it "should be able to cope with O o 0 ambiguity" do
|
10
14
|
UrlKeyedObject.decode_url_key('Oo0ab').should == '000ab'
|
@@ -37,17 +41,27 @@ describe UrlKeyedObject do
|
|
37
41
|
UrlKeyedObject.well_formed_url_key?(malformed_url_key).should be_false
|
38
42
|
end
|
39
43
|
end
|
44
|
+
|
45
|
+
it "should be able to cope with a different specified length" do
|
46
|
+
UrlKeyedObject.well_formed_url_key?('abcdef', 6).should be_true
|
47
|
+
end
|
40
48
|
end
|
41
49
|
|
42
50
|
describe "validity of URL keys" do
|
43
51
|
it "should be able to evaluate a block to check key validity, repeating until it generates one" do
|
44
52
|
valid_key_sequence = sequence('valid key sequence')
|
45
|
-
UrlKeyedObject.expects(:generate_unchecked_url_key).
|
46
|
-
|
53
|
+
UrlKeyedObject.expects(:generate_unchecked_url_key).with(5).returns('a1url').
|
54
|
+
in_sequence(valid_key_sequence)
|
55
|
+
UrlKeyedObject.expects(:generate_unchecked_url_key).with(5).returns('a2url').
|
56
|
+
in_sequence(valid_key_sequence)
|
47
57
|
|
48
58
|
key = UrlKeyedObject.generate_checked_url_key { |value| value != 'a1url' }
|
49
59
|
|
50
60
|
key.should == 'a2url'
|
51
61
|
end
|
62
|
+
|
63
|
+
it "should description" do
|
64
|
+
|
65
|
+
end
|
52
66
|
end
|
53
67
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: url_keyed_object
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Matt Patterson
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-01-
|
12
|
+
date: 2010-01-20 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -68,6 +68,7 @@ files:
|
|
68
68
|
- features/support/hooks.rb
|
69
69
|
- features/support/recording_logger.rb
|
70
70
|
- features/with_active_record-basic.feature
|
71
|
+
- features/with_active_record-more-advanced.feature
|
71
72
|
- lib/url_keyed_object.rb
|
72
73
|
- lib/url_keyed_object/active_record.rb
|
73
74
|
- spec/spec.opts
|