sequenced 1.0.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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