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 CHANGED
@@ -1 +1 @@
1
- 0.1.1
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
- include UrlKeyedObject::ActiveRecord
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 using mass-assignment:
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
- # the stuff that actually gets included.
7
- # comment included for my benefit, in case I forget (quite likely)
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
- def self.included(model)
24
- model.class_eval do
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
- protected
30
-
31
- def generate_valid_url_key
32
- new_url_key = UrlKeyedObject.generate_checked_url_key { |value| self.valid_url_key?(value) }
33
- write_attribute(:url_key, new_url_key)
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 valid_url_key?(url_key)
37
- self.class.find_by_url_key(url_key).nil?
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
@@ -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..5).collect do
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(/^[0-9abcdefghjkmnpqrsvwxyz]{5}$/).nil?
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
@@ -37,7 +37,7 @@ describe UrlKeyedObject::ActiveRecord do
37
37
  'UrlKeyedObjectMock'
38
38
  end
39
39
 
40
- include UrlKeyedObject::ActiveRecord
40
+ acts_as_url_keyed
41
41
  end
42
42
 
43
43
  ActiveRecord::Base.logger = Logger.new(STDERR)
@@ -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).in_sequence(valid_key_sequence).returns('a1url')
46
- UrlKeyedObject.expects(:generate_unchecked_url_key).in_sequence(valid_key_sequence).returns('a2url')
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.1.1
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-19 00:00:00 +00:00
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