sequenced 1.0.0 → 1.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.
@@ -1,3 +1,16 @@
1
+ 1.2.0 (April 11, 2013)
2
+ ----------------------
3
+
4
+ * Accept an array of symbols for the scope attribute to scope by multiple
5
+ columns.
6
+
7
+ 1.1.0 (July 5, 2012)
8
+ --------------------
9
+
10
+ * Raise ArgumentError instead of Sequenced::InvalidAttributeError
11
+ * Remove custom exceptions
12
+ * Stop calling it a "plugin"
13
+
1
14
  1.0.0 (March 7, 2012)
2
15
  ---------------------
3
16
 
data/README.md CHANGED
@@ -1,13 +1,11 @@
1
1
  # Sequenced
2
2
 
3
- Sequenced is a simple Rails 3 plugin that generates scoped sequential IDs for
3
+ Sequenced is a simple gem that generates scoped sequential IDs for
4
4
  ActiveRecord models. This gem provides an `acts_as_sequenced` macro that
5
- automatically assigns a unique, sequential ID to each record. This ID is
5
+ automatically assigns a unique, sequential ID to each record. The sequential ID is
6
6
  not a replacement for the database primary key, but rather adds another way to
7
7
  retrieve the object without exposing the primary key.
8
8
 
9
- Extracted from the [GuideKit](https://guidekit.com) codebase.
10
-
11
9
  ## Purpose
12
10
 
13
11
  It's generally a bad practice to expose your primary keys to the world
@@ -58,7 +56,15 @@ end
58
56
  ```
59
57
 
60
58
  The `:scope` option can be any attribute, but will typically be the foreign
61
- key of an associated parent object.
59
+ key of an associated parent object. You can even scope by multiple columns
60
+ for polymorphic relationships:
61
+
62
+ ```ruby
63
+ class Answer < ActiveRecord::Base
64
+ belongs_to :questionable, :polymorphic => true
65
+ acts_as_sequenced :scope => [:questionable_id, :questionable_type]
66
+ end
67
+ ```
62
68
 
63
69
  ## Configuration
64
70
 
@@ -1,4 +1,3 @@
1
- require 'sequenced/exceptions'
2
1
  require 'sequenced/acts_as_sequenced'
3
2
 
4
3
  ActiveRecord::Base.send(:include, Sequenced::ActsAsSequenced)
@@ -51,7 +51,7 @@ module Sequenced
51
51
  # defined.
52
52
  #
53
53
  # Returns nothing.
54
- # Raises Sequenced::InvalidAttributeError if
54
+ # Raises ArgumentError if
55
55
  # 1) The specified scope method is undefined,
56
56
  # 2) The specified scope method returns nil, or
57
57
  # 3) The sequential ID column is undefined.
@@ -60,39 +60,60 @@ module Sequenced
60
60
  column = self.class.sequenced_options[:column]
61
61
 
62
62
  if scope.present?
63
- if !self.respond_to?(scope)
64
- raise Sequenced::InvalidAttributeError.new(":scope method ##{scope.to_s} is undefined")
65
- elsif self.send(scope).nil?
66
- raise Sequenced::InvalidAttributeError.new(":scope method ##{scope.to_s} returned nil unexpectedly")
63
+ if scope.is_a?(Array)
64
+ scope.each { |s| verify_scope_method(s) }
65
+ else
66
+ verify_scope_method(scope)
67
67
  end
68
68
  end
69
69
 
70
70
  unless self.respond_to?(column)
71
- raise Sequenced::InvalidAttributeError.new(":column method ##{column.to_s} is undefined")
71
+ raise ArgumentError, "Column method ##{column.to_s} is undefined"
72
72
  end
73
73
 
74
74
  # Fetch the next ID unless it is already defined
75
75
  self.send(:"#{column}=", next_sequential_id) until sequential_id_is_unique?
76
76
  end
77
77
 
78
+ # Internal: Verify that the given scope method is defined and does not
79
+ # return nil unexpectedly.
80
+ #
81
+ # scope - A Symbol representing the scope method.
82
+ #
83
+ # Returns nothing.
84
+ # Raises an ArgumentError if
85
+ # 1) The specified scope method is undefined, or
86
+ # 2) The specified scope method returns nil
87
+ def verify_scope_method(scope)
88
+ if !self.respond_to?(scope)
89
+ raise ArgumentError, "Scope method ##{scope.to_s} is undefined"
90
+ elsif self.send(scope).nil?
91
+ raise ArgumentError, "Scope method ##{scope.to_s} returned nil unexpectedly"
92
+ end
93
+ end
94
+
78
95
  # Internal: Obtain the next sequential ID
79
96
  #
80
97
  # Returns Integer.
81
- # Raises Sequenced::InvalidAttributeError if the last sequential ID is not
82
- # an Integer.
98
+ # Raises ArgumentError if the last sequential ID is not an Integer.
83
99
  def next_sequential_id
84
100
  scope = self.class.sequenced_options[:scope]
85
101
  column = self.class.sequenced_options[:column]
86
102
  start_at = self.class.sequenced_options[:start_at]
87
103
 
88
104
  q = self.class.unscoped.where("#{column.to_s} IS NOT NULL").order("#{column.to_s} DESC")
89
- q = q.where(scope => self.send(scope)) if scope.is_a?(Symbol)
105
+
106
+ if scope.is_a?(Symbol)
107
+ q = q.where(scope => self.send(scope))
108
+ elsif scope.is_a?(Array)
109
+ scope.each { |s| q = q.where(s => self.send(s)) }
110
+ end
90
111
 
91
112
  return start_at unless last_record = q.first
92
113
  last_id = last_record.send(column)
93
114
 
94
115
  unless last_id.is_a?(Integer)
95
- raise Sequenced::InvalidAttributeError("The sequential ID column must contain Integer values")
116
+ raise ArgumentError, "The sequential ID column must contain Integer values"
96
117
  end
97
118
 
98
119
  last_id + 1 > start_at ? last_id + 1 : start_at
@@ -107,9 +128,14 @@ module Sequenced
107
128
  return false unless self.send(column).is_a?(Integer)
108
129
 
109
130
  q = self.class.unscoped.where(column => self.send(column))
110
- q = q.where(scope => self.send(scope)) if scope.is_a?(Symbol)
111
- q = q.where("NOT id = ?", self.id) if self.persisted?
112
131
 
132
+ if scope.is_a?(Symbol)
133
+ q = q.where(scope => self.send(scope))
134
+ elsif scope.is_a?(Array)
135
+ scope.each { |s| q = q.where(s => self.send(s)) }
136
+ end
137
+
138
+ q = q.where("NOT id = ?", self.id) if self.persisted?
113
139
  q.count > 0 ? false : true
114
140
  end
115
141
  end
@@ -1,3 +1,3 @@
1
1
  module Sequenced
2
- VERSION = "1.0.0"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -8,7 +8,7 @@ Gem::Specification.new do |s|
8
8
  s.email = ["derrickreimer@gmail.com"]
9
9
  s.homepage = "https://github.com/djreimer/sequenced"
10
10
  s.summary = "Generate scoped sequential IDs for ActiveRecord models"
11
- s.description = "Sequenced is a simple Rails 3 plugin that generates scoped sequential IDs for ActiveRecord models"
11
+ s.description = "Sequenced is a gem that generates scoped sequential IDs for ActiveRecord models."
12
12
 
13
13
  s.files = `git ls-files`.split("\n")
14
14
  s.test_files = Dir["test/**/*"]
@@ -0,0 +1,4 @@
1
+ class Email < ActiveRecord::Base
2
+ belongs_to :emailable, :polymorphic => true
3
+ acts_as_sequenced :scope => [:emailable_id, :emailable_type]
4
+ end
@@ -0,0 +1,12 @@
1
+ class CreateEmails < ActiveRecord::Migration
2
+ def change
3
+ create_table :emails do |t|
4
+ t.string :emailable_type
5
+ t.integer :emailable_id
6
+ t.integer :sequential_id
7
+ t.string :address
8
+
9
+ t.timestamps
10
+ end
11
+ end
12
+ end
@@ -8,6 +8,7 @@ require 'test_helper'
8
8
  # Order - :scope => :non_existent_column
9
9
  # User - :scope => :account_id, :column => :custom_sequential_id
10
10
  # Address - :scope => :account_id ('sequential_id' does not exist)
11
+ # Email - :scope => [:emailable_id, :emailable_type]
11
12
  # Subscription - no options
12
13
 
13
14
  class SequencedTest < ActiveSupport::TestCase
@@ -49,12 +50,12 @@ class SequencedTest < ActiveSupport::TestCase
49
50
  test "undefined scope method" do
50
51
  account = Account.create
51
52
  order = account.orders.build
52
- assert_raises(Sequenced::InvalidAttributeError) { order.save }
53
+ assert_raises(ArgumentError) { order.save }
53
54
  end
54
55
 
55
56
  test "scope method returns nil" do
56
57
  answer = Answer.new
57
- assert_raises(Sequenced::InvalidAttributeError) { answer.save }
58
+ assert_raises(ArgumentError) { answer.save }
58
59
  end
59
60
 
60
61
  test "custom sequential id column" do
@@ -77,7 +78,7 @@ class SequencedTest < ActiveSupport::TestCase
77
78
  test "undefined sequential id column" do
78
79
  account = Account.create
79
80
  address = account.addresses.build
80
- assert_raises(Sequenced::InvalidAttributeError) { address.save }
81
+ assert_raises(ArgumentError) { address.save }
81
82
  end
82
83
 
83
84
  test "manually setting sequential id" do
@@ -97,4 +98,11 @@ class SequencedTest < ActiveSupport::TestCase
97
98
  comment = question.comments.create
98
99
  assert_equal 4, comment.sequential_id
99
100
  end
101
+
102
+ test "multi-column scopes" do
103
+ Email.create(:emailable_id => 1, :emailable_type => "User", :sequential_id => 2)
104
+ Email.create(:emailable_id => 1, :emailable_type => "Question", :sequential_id => 3)
105
+ email = Email.create(:emailable_id => 1, :emailable_type => "User")
106
+ assert_equal 3, email.sequential_id
107
+ end
100
108
  end
metadata CHANGED
@@ -1,72 +1,88 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: sequenced
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.2.0
4
5
  prerelease:
5
- version: 1.0.0
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Derrick Reimer
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2012-03-07 00:00:00 -08:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
12
+ date: 2013-04-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
17
15
  name: activesupport
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
18
+ requirements:
21
19
  - - ~>
22
- - !ruby/object:Gem::Version
23
- version: "3.0"
20
+ - !ruby/object:Gem::Version
21
+ version: '3.0'
24
22
  type: :runtime
25
23
  prerelease: false
26
- version_requirements: *id001
27
- - !ruby/object:Gem::Dependency
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '3.0'
30
+ - !ruby/object:Gem::Dependency
28
31
  name: activerecord
29
- requirement: &id002 !ruby/object:Gem::Requirement
32
+ requirement: !ruby/object:Gem::Requirement
30
33
  none: false
31
- requirements:
34
+ requirements:
32
35
  - - ~>
33
- - !ruby/object:Gem::Version
34
- version: "3.0"
36
+ - !ruby/object:Gem::Version
37
+ version: '3.0'
35
38
  type: :runtime
36
39
  prerelease: false
37
- version_requirements: *id002
38
- - !ruby/object:Gem::Dependency
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ - !ruby/object:Gem::Dependency
39
47
  name: rails
40
- requirement: &id003 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
41
49
  none: false
42
- requirements:
50
+ requirements:
43
51
  - - ~>
44
- - !ruby/object:Gem::Version
45
- version: "3.1"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.1'
46
54
  type: :development
47
55
  prerelease: false
48
- version_requirements: *id003
49
- - !ruby/object:Gem::Dependency
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: '3.1'
62
+ - !ruby/object:Gem::Dependency
50
63
  name: sqlite3
51
- requirement: &id004 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
52
65
  none: false
53
- requirements:
54
- - - ">="
55
- - !ruby/object:Gem::Version
56
- version: "0"
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
57
70
  type: :development
58
71
  prerelease: false
59
- version_requirements: *id004
60
- description: Sequenced is a simple Rails 3 plugin that generates scoped sequential IDs for ActiveRecord models
61
- email:
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Sequenced is a gem that generates scoped sequential IDs for ActiveRecord
79
+ models.
80
+ email:
62
81
  - derrickreimer@gmail.com
63
82
  executables: []
64
-
65
83
  extensions: []
66
-
67
84
  extra_rdoc_files: []
68
-
69
- files:
85
+ files:
70
86
  - .gitignore
71
87
  - CHANGELOG.md
72
88
  - Gemfile
@@ -76,7 +92,6 @@ files:
76
92
  - TODO.md
77
93
  - lib/sequenced.rb
78
94
  - lib/sequenced/acts_as_sequenced.rb
79
- - lib/sequenced/exceptions.rb
80
95
  - lib/sequenced/version.rb
81
96
  - sequenced.gemspec
82
97
  - test/dummy/README.rdoc
@@ -91,6 +106,7 @@ files:
91
106
  - test/dummy/app/models/address.rb
92
107
  - test/dummy/app/models/answer.rb
93
108
  - test/dummy/app/models/comment.rb
109
+ - test/dummy/app/models/email.rb
94
110
  - test/dummy/app/models/invoice.rb
95
111
  - test/dummy/app/models/order.rb
96
112
  - test/dummy/app/models/question.rb
@@ -122,6 +138,7 @@ files:
122
138
  - test/dummy/db/migrate/20120219175744_create_users.rb
123
139
  - test/dummy/db/migrate/20120219232323_create_addresses.rb
124
140
  - test/dummy/db/migrate/20120220000804_create_comments.rb
141
+ - test/dummy/db/migrate/20130411225444_create_emails.rb
125
142
  - test/dummy/db/schema.rb
126
143
  - test/dummy/lib/assets/.gitkeep
127
144
  - test/dummy/log/.gitkeep
@@ -136,41 +153,31 @@ files:
136
153
  - test/dummy/db/test.sqlite3
137
154
  - test/dummy/log/development.log
138
155
  - test/dummy/log/test.log
139
- has_rdoc: true
140
156
  homepage: https://github.com/djreimer/sequenced
141
157
  licenses: []
142
-
143
158
  post_install_message:
144
159
  rdoc_options: []
145
-
146
- require_paths:
160
+ require_paths:
147
161
  - lib
148
- required_ruby_version: !ruby/object:Gem::Requirement
162
+ required_ruby_version: !ruby/object:Gem::Requirement
149
163
  none: false
150
- requirements:
151
- - - ">="
152
- - !ruby/object:Gem::Version
153
- hash: 2873144507435092407
154
- segments:
155
- - 0
156
- version: "0"
157
- required_rubygems_version: !ruby/object:Gem::Requirement
164
+ requirements:
165
+ - - ! '>='
166
+ - !ruby/object:Gem::Version
167
+ version: '0'
168
+ required_rubygems_version: !ruby/object:Gem::Requirement
158
169
  none: false
159
- requirements:
160
- - - ">="
161
- - !ruby/object:Gem::Version
162
- hash: 2873144507435092407
163
- segments:
164
- - 0
165
- version: "0"
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
166
174
  requirements: []
167
-
168
175
  rubyforge_project:
169
- rubygems_version: 1.6.1
176
+ rubygems_version: 1.8.23
170
177
  signing_key:
171
178
  specification_version: 3
172
179
  summary: Generate scoped sequential IDs for ActiveRecord models
173
- test_files:
180
+ test_files:
174
181
  - test/dummy/app/assets/javascripts/application.js
175
182
  - test/dummy/app/assets/stylesheets/application.css
176
183
  - test/dummy/app/controllers/application_controller.rb
@@ -179,6 +186,7 @@ test_files:
179
186
  - test/dummy/app/models/address.rb
180
187
  - test/dummy/app/models/answer.rb
181
188
  - test/dummy/app/models/comment.rb
189
+ - test/dummy/app/models/email.rb
182
190
  - test/dummy/app/models/invoice.rb
183
191
  - test/dummy/app/models/order.rb
184
192
  - test/dummy/app/models/question.rb
@@ -211,6 +219,7 @@ test_files:
211
219
  - test/dummy/db/migrate/20120219175744_create_users.rb
212
220
  - test/dummy/db/migrate/20120219232323_create_addresses.rb
213
221
  - test/dummy/db/migrate/20120220000804_create_comments.rb
222
+ - test/dummy/db/migrate/20130411225444_create_emails.rb
214
223
  - test/dummy/db/schema.rb
215
224
  - test/dummy/db/test.sqlite3
216
225
  - test/dummy/log/development.log
@@ -1,4 +0,0 @@
1
- module Sequenced
2
- class SequencedError < RuntimeError; end
3
- class InvalidAttributeError < SequencedError; end
4
- end