unique_id 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MDY2MWMyMDNlNjRlY2U1MWJjNjY4NDM3MjRjNTJkN2E5MmRmMDNjNw==
5
+ data.tar.gz: !binary |-
6
+ YzY5ZWEyMzE5NjIwZmJkYjZlZWJiYTA0ZTIxMGJiYzg4OWM5MzIyYg==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ OGM1YjcxZmIwNmFkZjM3YzExM2EzMGFlZDNlY2U1MzM5Zjg1ZTE5Mjk5YmEz
10
+ MDQ4NzYxMzQyZWZkMmFlMWRiZDJlZjE1NDEyODJhZjc3N2JlMWYwZDAwNjUx
11
+ NzcxNWUzNGJmODNlYTBhZTZjZjU0YTFlN2VhOTllZWRhZWI0MGE=
12
+ data.tar.gz: !binary |-
13
+ NTZiOWRlYjU2YjU0NTIyMjcyNmUyZjYwN2YyYzQ1NWZmZmZmM2M1MjM0ZmMx
14
+ MzNlZWYxNjMxYzgwZTEwNDMyMzJlYzQwOTMyZDUyN2VhM2NlNTk0YmNkM2Ji
15
+ NjJlYzc1Y2JiNDMxMjU1NTAyYTUwNTczY2I4NTc3Y2Y3NjgwY2Q=
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ spec/internal/db/*.sqlite3
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
19
+ *.bundle
20
+ *.so
21
+ *.o
22
+ *.a
23
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format progress
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ unique-id
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ ruby-1.9.3-p484
data/.travis.yml ADDED
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.1
6
+ env:
7
+ - DB=sqlite
8
+ - DB=mysql
9
+ - DB=postgresql
10
+ before_script:
11
+ - mysql -e 'create database myapp_test'
12
+ - psql -c 'create database myapp_test' -U postgres
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in unique_id.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'sqlite3'
8
+ gem 'mysql2'
9
+ gem 'pg'
10
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Christian Aust
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # UniqueId
2
+
3
+ [![Build Status](https://travis-ci.org/datenimperator/unique-id.svg?branch=master)](https://travis-ci.org/datenimperator/unique-id)
4
+
5
+ This ActiveRecord extension keeps track of number ranges and easily creates valid sequential formatted identifiers which could be used for invoices, orders, personnel numbers and the like. Did you ever want to generate invoice IDs like this?
6
+
7
+ * INV-0001
8
+ * INV-0002
9
+ * INV-0002
10
+
11
+ That's what UniqueId does. It support multiple independent number ranges, each of them can be scoped and formatted as needed.
12
+
13
+ ## Installation
14
+
15
+ Add this line to your application's Gemfile:
16
+
17
+ gem 'unique_id'
18
+
19
+ And then execute:
20
+
21
+ $ bundle
22
+
23
+ Or install it yourself as:
24
+
25
+ $ gem install unique_id
26
+
27
+ ### Requirements
28
+
29
+ The gem has been tested with these configurations:
30
+
31
+ * MRI Ruby 1.9.3, 2.0.0 and 2.1.1
32
+ * sqlite3, mySQL or PostgreSQL
33
+ * ActiveRecord 3.1 and higher
34
+
35
+ ## Usage
36
+
37
+ Create a database attribute which will hold the id, and activate unique_id to use it:
38
+
39
+ ````ruby
40
+ class Invoice
41
+ has_unique :inv_id
42
+ end
43
+ ````
44
+
45
+ In this example, `inv_id` is the attribute of our model `Invoice` which will hold the id once it has been created. It needs to be a string attribute, your application is responsible for creating and indexing it as appropriate.
46
+
47
+ There are some options that can be set. These are the defaults:
48
+
49
+ ````ruby
50
+ class Invoice
51
+ has_unique :inv_id,
52
+ start: 1,
53
+ scoped_by: nil,
54
+ formatter: nil
55
+ end
56
+ ````
57
+
58
+ #### start
59
+
60
+ Numbering starts with this value for each number range. Can be set per model.
61
+
62
+ #### scoped_by
63
+
64
+ A number is guaranteed to be unique within a *scope*. While the default is blank, this could be used for revolving number ranges, eg. to start over the numbers each year:
65
+
66
+ ````ruby
67
+ class Invoice
68
+ has_unique :inv_id,
69
+ scoped_by: proc { Time.now.year }
70
+ end
71
+ ````
72
+
73
+ The scope will be set to the return value of the proc and will be evaluated every time a new identifier is generated. In this example numbering will start over each year.
74
+
75
+ #### formatter
76
+
77
+ By default a unique_id will simple be an integer value. You can define a formatter to make it look however you like:
78
+
79
+ ````ruby
80
+ class Invoice
81
+ has_unique :inv_id,
82
+ formatter: proc { |scope, value| sprintf("INV-%04d", value) }
83
+ end
84
+ ````
85
+
86
+ Like this, the `uid` attribute will be filled with values `INV-0001`, `INV-0002`, `INV-0003`…
87
+
88
+
89
+ ## Contributing
90
+
91
+ 1. Fork it ( https://github.com/[my-github-username]/unique_id/fork )
92
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
93
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
94
+ 4. Push to the branch (`git push origin my-new-feature`)
95
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,46 @@
1
+ require 'active_support/concern'
2
+
3
+ module UniqueId
4
+ module Base
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ before_create :generate_unique
9
+ end
10
+
11
+ def unique_type
12
+ self.class.to_s
13
+ end
14
+
15
+ def unique_scope
16
+ if scope = self.class.unique.scope
17
+ @unique_scope ||= case scope
18
+ when Symbol then self.send(scope)
19
+ when Proc then self.instance_exec(&scope)
20
+ else scope
21
+ end
22
+ end
23
+ end
24
+
25
+ def unique_format(value)
26
+ if f = self.class.unique.formatter
27
+ return self.instance_exec(self.unique_scope, value, &f)
28
+ end
29
+ value
30
+ end
31
+
32
+ def generate_unique
33
+ value = unique_format(self.class.unique.next(scope:self.unique_scope, type:self.unique_type))
34
+ write_attribute self.class.unique.attribute, value
35
+ end
36
+
37
+ module ClassMethods
38
+ attr_accessor :unique
39
+
40
+ def has_unique(*args)
41
+ opts = args.extract_options!
42
+ @unique = ::Unique::Generator.new(args.first, opts)
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,11 @@
1
+ require 'rails/engine'
2
+
3
+ module UniqueId
4
+ class Engine < Rails::Engine
5
+ engine_name :unique_id
6
+
7
+ ActiveSupport.on_load :active_record do
8
+ include ::UniqueId::Base
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,46 @@
1
+ module Unique
2
+ class Generator
3
+ attr_reader :attribute
4
+
5
+ DEFAULT = {
6
+ start: 1,
7
+ scoped_by: nil,
8
+ formatter: nil
9
+ }
10
+
11
+ def initialize(attribute, opts={})
12
+ @attribute = attribute
13
+ @opts = DEFAULT.merge(opts)
14
+ end
15
+
16
+ def scope
17
+ @opts[:scoped_by]
18
+ end
19
+
20
+ def formatter
21
+ @opts[:formatter]
22
+ end
23
+
24
+ def next(opts)
25
+ _scope = opts[:scope].nil? ? nil : opts[:scope].to_s
26
+ _type = opts[:type]
27
+
28
+ table = Arel::Table.new(:unique_ids)
29
+ query = table
30
+ .project( table[:value].maximum.as('v1') )
31
+ .where( table[:type].eq(_type).and(table[:scope].eq(_scope)) )
32
+ result = ActiveRecord::Base.connection.execute(query.to_sql).first
33
+ max = result.is_a?(Hash) ? result['v1'] : result[0]
34
+
35
+ next_value = max.nil? ? @opts[:start] : max.to_i + 1
36
+
37
+ ins_query = table.create_insert
38
+ ins_query.into table
39
+ ins_query.insert table[:type]=>_type, table[:scope]=>_scope, table[:value]=>next_value
40
+ result = ActiveRecord::Base.connection.insert(ins_query, nil, nil, next_value)
41
+ raise "Unexpected result: #{result}" unless result == next_value
42
+
43
+ next_value
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module UniqueId
2
+ VERSION = "0.0.1"
3
+ end
data/lib/unique_id.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'unique_id/version'
2
+ require 'unique_id/base'
3
+ require 'unique_id/generator'
4
+ require 'unique_id/engine'
@@ -0,0 +1,28 @@
1
+ sqlite: &sqlite
2
+ adapter: sqlite3
3
+ database: db/<%= Rails.env %>.sqlite3
4
+
5
+ mysql: &mysql
6
+ adapter: mysql2
7
+ username: root
8
+ password:
9
+ database: myapp_<%= Rails.env %>
10
+
11
+ postgresql: &postgresql
12
+ adapter: postgresql
13
+ username: postgres
14
+ password:
15
+ database: myapp_<%= Rails.env %>
16
+ min_messages: ERROR
17
+
18
+ defaults: &defaults
19
+ pool: 5
20
+ timeout: 5000
21
+ host: localhost
22
+ <<: *<%= ENV['DB'] || "sqlite" %>
23
+
24
+ development:
25
+ <<: *defaults
26
+
27
+ test:
28
+ <<: *defaults
@@ -0,0 +1,3 @@
1
+ Rails.application.routes.draw do
2
+ #
3
+ end
@@ -0,0 +1,8 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table(:unique_ids, id:false, force:true) do |t|
3
+ t.string :type, default:nil, null:false
4
+ t.string :scope
5
+ t.integer :value, default:nil, null:false
6
+ end
7
+ add_index :unique_ids, [:type, :scope, :value], unique: true
8
+ end
@@ -0,0 +1 @@
1
+ *.log
File without changes
@@ -0,0 +1,87 @@
1
+ require "spec_helper"
2
+
3
+ describe UniqueId do
4
+
5
+ it "should be set" do
6
+ build_model :invoice do
7
+ string :inv_id
8
+ has_unique :inv_id
9
+ end
10
+
11
+ inv = Invoice.create
12
+ inv.inv_id.should_not be_nil
13
+ end
14
+
15
+ it "can have a different start value" do
16
+ build_model :invoice do
17
+ string :inv_id
18
+ has_unique :inv_id,
19
+ start:1000
20
+ end
21
+
22
+ inv = Invoice.create
23
+ inv.inv_id.should == 1000
24
+ end
25
+
26
+ it "handles multiple models" do
27
+ build_model :model1 do
28
+ string :uid
29
+ has_unique :uid
30
+ end
31
+
32
+ build_model :model2 do
33
+ string :uid
34
+ has_unique :uid
35
+ end
36
+
37
+ m1_1 = Model1.create
38
+ m1_2 = Model1.create
39
+ m2 = Model2.create
40
+
41
+ m1_1.uid.should == 1
42
+ m1_2.uid.should == 2
43
+ m2.uid.should == 1
44
+ end
45
+
46
+ it "formats the unique value" do
47
+ build_model :model3 do
48
+ string :uid
49
+ has_unique :uid,
50
+ scoped_by: proc { Time.now.year },
51
+ formatter: proc { |scope, value| sprintf("inv_%4d_%04d", scope, value) }
52
+ end
53
+
54
+ m = Model3.create
55
+ m.uid.should == "inv_#{Time.now.year}_0001"
56
+ end
57
+
58
+ context "with scopes" do
59
+ it "supports scopes given as procs" do
60
+ build_model :model4 do
61
+ string :uid
62
+ has_unique :uid,
63
+ scoped_by: proc { Time.now.year }
64
+ end
65
+
66
+ m = Model4.create
67
+ m.uid.should == 1
68
+ end
69
+
70
+ describe "scopes can access the current instance" do
71
+ build_model :model5 do
72
+ string :uid
73
+ has_unique :uid,
74
+ scoped_by: :current_year
75
+
76
+ def current_year
77
+ Time.now.year
78
+ end
79
+ end
80
+
81
+ m = Model5.create
82
+ m.uid.should == 1
83
+ end
84
+
85
+ end
86
+
87
+ end
@@ -0,0 +1,29 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+
7
+ require 'bundler'
8
+
9
+ Bundler.require :default, :development
10
+
11
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
12
+ Combustion.initialize! :active_record
13
+
14
+ require 'rspec/rails'
15
+
16
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
17
+ RSpec.configure do |config|
18
+ config.include ActsAsFu
19
+ config.use_transactional_fixtures = true
20
+ config.treat_symbols_as_metadata_keys_with_true_values = true
21
+ config.run_all_when_everything_filtered = true
22
+
23
+ # Run specs in random order to surface order dependencies. If you find an
24
+ # order dependency and want to debug it, you can fix the order by providing
25
+ # the seed, which is printed after each run.
26
+ # --seed 1234
27
+ config.order = 'random'
28
+ end
29
+
data/unique_id.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'unique_id/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "unique_id"
8
+ spec.version = UniqueId::VERSION
9
+ spec.authors = ["Christian Aust"]
10
+ spec.email = ["git@kontakt.software-consultant.net"]
11
+ spec.summary = %q{An ActiveRecord extension which creates unique, sequentially numbered values.}
12
+ spec.description = %q{This extension creates values which can be used for invoice numbers, personnel numbers and the like.}
13
+ spec.homepage = "https://github.com/datenimperator/unique-id"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "activerecord", ">= 3.1"
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.3"
24
+ spec.add_development_dependency "rake"
25
+ spec.add_development_dependency "combustion"
26
+ spec.add_development_dependency "acts_as_fu"
27
+ spec.add_development_dependency "rspec-rails"
28
+ end
metadata ADDED
@@ -0,0 +1,158 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: unique_id
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Christian Aust
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-30 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activerecord
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ! '>='
18
+ - !ruby/object:Gem::Version
19
+ version: '3.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ! '>='
25
+ - !ruby/object:Gem::Version
26
+ version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.3'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: combustion
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ! '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: acts_as_fu
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ! '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rspec-rails
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ! '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ! '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: This extension creates values which can be used for invoice numbers,
98
+ personnel numbers and the like.
99
+ email:
100
+ - git@kontakt.software-consultant.net
101
+ executables: []
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - .gitignore
106
+ - .rspec
107
+ - .ruby-gemset
108
+ - .ruby-version
109
+ - .travis.yml
110
+ - Gemfile
111
+ - LICENSE.txt
112
+ - README.md
113
+ - Rakefile
114
+ - lib/unique_id.rb
115
+ - lib/unique_id/base.rb
116
+ - lib/unique_id/engine.rb
117
+ - lib/unique_id/generator.rb
118
+ - lib/unique_id/version.rb
119
+ - spec/internal/config/database.yml
120
+ - spec/internal/config/routes.rb
121
+ - spec/internal/db/schema.rb
122
+ - spec/internal/log/.gitignore
123
+ - spec/internal/public/favicon.ico
124
+ - spec/lib/unique_id_spec.rb
125
+ - spec/spec_helper.rb
126
+ - unique_id.gemspec
127
+ homepage: https://github.com/datenimperator/unique-id
128
+ licenses:
129
+ - MIT
130
+ metadata: {}
131
+ post_install_message:
132
+ rdoc_options: []
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ! '>='
138
+ - !ruby/object:Gem::Version
139
+ version: '0'
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ! '>='
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ requirements: []
146
+ rubyforge_project:
147
+ rubygems_version: 2.2.2
148
+ signing_key:
149
+ specification_version: 4
150
+ summary: An ActiveRecord extension which creates unique, sequentially numbered values.
151
+ test_files:
152
+ - spec/internal/config/database.yml
153
+ - spec/internal/config/routes.rb
154
+ - spec/internal/db/schema.rb
155
+ - spec/internal/log/.gitignore
156
+ - spec/internal/public/favicon.ico
157
+ - spec/lib/unique_id_spec.rb
158
+ - spec/spec_helper.rb