uniqueable 0.0.2

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/.gitignore ADDED
@@ -0,0 +1,17 @@
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
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in uniqueable.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Gary Rennie
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,88 @@
1
+ # Uniqueable
2
+
3
+ Uniqueable allows the checking of cross-model and cross-attribute uniqueness check. For example, if you have users and organisations, both of which have a name that is used in the url, then you will want to check uniqueness across both of the models.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'uniqueable'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install uniqueable
18
+
19
+ ## Usage
20
+
21
+ Uniqueable adds a uniqueable method and validator to your models. Uniqueable is most useful when validating uniqueness across multiple Models and fields.
22
+
23
+ class User < ActiveRecord::Base
24
+ include Uniqueable
25
+ uniquable :username
26
+ validates :username, :uniqueable => true
27
+ end
28
+
29
+ class Organsation < ActiveRecord::Base
30
+ include Uniqueable
31
+ uniquable :alias
32
+ validates :alias, :uniqueable => true
33
+ end
34
+
35
+ user = User.create(:username => "gazler") # Works
36
+ organisation = Organisation.create(:alias => "gazler") # Does not work
37
+ organisation.errors.messages # {:alias => ["has already been taken"]}
38
+
39
+ ### Groups
40
+ In a large application, you may want to check uniqueness for multiple groups of things. Uniqueable includes groups for this:
41
+
42
+ class User < ActiveRecord::Base
43
+ include Uniqueable
44
+ uniquable :username, :group => :names_for_stuff
45
+ validates :username, :uniqueable => {:group => :names_for_stuff}
46
+ end
47
+
48
+ class Organsation < ActiveRecord::Base
49
+ include Uniqueable
50
+ uniquable :alias, :group => :names_for_stuff
51
+ validates :alias, :uniqueable => {:group => :names_for_stuff}
52
+ end
53
+
54
+ class Organsation < ActiveRecord::Base
55
+ include Uniqueable
56
+ uniquable :alias, :group => :names_for_stuff
57
+ uniquable :alias
58
+ validates :uniquable
59
+ end
60
+
61
+
62
+ admin_user = AdminUser.create(:alias => "gazler") # Works
63
+ user = User.create(:username => "gazler") # Does not work
64
+ organisation = Organisation.create(:alias => "gazler") # Does not work
65
+ admin_user = AdminUser.create(:alias => "gazler") # Does not work
66
+
67
+ ### Other options
68
+
69
+ The `case_sensitive` option can be sent through with the validator. It defaults to true.
70
+
71
+ class User < ActiveRecord::Base
72
+ include Uniqueable
73
+ uniquable :username, :group => :names_for_stuff
74
+ validates :username, :uniqueable => {:group => :names_for_stuff, :case_sensitive => false}
75
+ end
76
+
77
+ ### Notes
78
+
79
+ If a group is not set then everything will act in the nil group. That is, all uniqueables without a group will validate against all other uniqueables without a group.
80
+
81
+
82
+ ## Contributing
83
+
84
+ 1. Fork it
85
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
86
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
87
+ 4. Push to the branch (`git push origin my-new-feature`)
88
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rspec/core/rake_task"
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ task :default => [:spec]
@@ -0,0 +1,30 @@
1
+ module Uniqueable
2
+ class UniqueableValidator < ActiveModel::EachValidator
3
+ def initialize(options)
4
+ super(options.reverse_merge(:case_sensitive => true))
5
+ end
6
+
7
+ def validate_each(record, attribute, value)
8
+ checks = record.class.uniqueable_checks[options[:group]]
9
+ #Make the check atomic
10
+ return unless checks
11
+ checks.each do |klass, keys|
12
+ keys.each do |key|
13
+ #Check nothing exists with this pair
14
+ if options[:case_sensitive]
15
+ result = klass.where(key => value)
16
+ else
17
+ result = klass.find(:all, :conditions => ["lower(#{key}) = ?", value.downcase])
18
+ end
19
+
20
+ unless result.empty?
21
+ #Check that the result isn't the record we are validating
22
+ unless result.first == record
23
+ return record.errors.add(attribute, :taken)
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,3 @@
1
+ module Uniqueable
2
+ VERSION = "0.0.2"
3
+ end
data/lib/uniqueable.rb ADDED
@@ -0,0 +1,31 @@
1
+ require 'active_model'
2
+ require "uniqueable/version"
3
+ require "uniqueable/validates_uniqueable"
4
+
5
+ module Uniqueable
6
+ class << self
7
+ attr_accessor :uniqueable_checks
8
+
9
+ def included(base)
10
+ base.extend(ClassMethods)
11
+ @uniqueable_checks ||= {}
12
+ base.uniqueable_checks = uniqueable_checks
13
+ end
14
+ end
15
+
16
+ module ClassMethods
17
+ attr_accessor :uniqueable_checks
18
+ def uniqueable(*values)
19
+ options = values.last.kind_of?(Hash) ? values.pop : {}
20
+ group = options[:group]
21
+ klass = self
22
+ uniqueable_checks[group] ||= {}
23
+ uniqueable_checks[group][klass] ||= []
24
+ values.each do |value|
25
+ uniqueable_checks[group][klass] << value
26
+ end
27
+ uniqueable_checks[group][klass].uniq!
28
+ end
29
+
30
+ end
31
+ end
data/spec/schema.rb ADDED
@@ -0,0 +1,16 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :users do |t|
3
+ t.string :username
4
+ t.string :name
5
+ end
6
+
7
+ create_table :organisations do |t|
8
+ t.string :alias
9
+ t.string :name
10
+ end
11
+
12
+ create_table :admin_users do |t|
13
+ t.string :username
14
+ t.string :name
15
+ end
16
+ end
@@ -0,0 +1,14 @@
1
+ require "active_record"
2
+ require './lib/uniqueable'
3
+
4
+ ActiveRecord::Base.establish_connection :adapter => "sqlite3", :database => ":memory:"
5
+ load "schema.rb"
6
+
7
+ RSpec.configure do |config|
8
+ config.before do
9
+ ActiveRecord::Base.descendants.each do |model|
10
+ model.delete_all
11
+ Object.class_eval { remove_const model.name if const_defined?(model.name) }
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,63 @@
1
+ require 'spec_helper'
2
+
3
+ describe Uniqueable do
4
+ before(:each) do
5
+ # Reset the value
6
+ Uniqueable.instance_variable_set(:@uniqueable_checks, nil)
7
+ class Tester
8
+ include Uniqueable
9
+ end
10
+
11
+ class AnotherTester
12
+ include Uniqueable
13
+ end
14
+ end
15
+
16
+ context "when included" do
17
+ it "should default the checks variable to a hash" do
18
+ Tester.uniqueable_checks.should eql({})
19
+ end
20
+
21
+ end
22
+
23
+ context "uniqueable class method" do
24
+
25
+ context "without a group" do
26
+
27
+ it "should add a uniqueable value when called" do
28
+ Tester.uniqueable(:name)
29
+ Tester.uniqueable_checks.should eql({nil => {Tester => [:name]}})
30
+ end
31
+
32
+ it "should add multiple values when called" do
33
+ Tester.uniqueable(:name, :another)
34
+ Tester.uniqueable_checks.should eql({nil => {Tester => [:name, :another]}})
35
+ end
36
+
37
+ it "should add multiple values from multiple classes" do
38
+ Tester.uniqueable(:name, :another)
39
+ AnotherTester.uniqueable(:alias, :email)
40
+ Tester.uniqueable_checks.should eql({nil => {Tester => [:name, :another], AnotherTester => [:alias, :email]}})
41
+ AnotherTester.uniqueable_checks.should eql({nil => {Tester => [:name, :another], AnotherTester => [:alias, :email]}})
42
+ end
43
+
44
+ it "should not add the same value more than once" do
45
+ Tester.uniqueable(:name, :name)
46
+ Tester.uniqueable(:name, :name)
47
+ Tester.uniqueable_checks.should eql({nil => {Tester => [:name]}})
48
+ end
49
+
50
+ end
51
+
52
+ it "should add a uniqueable with a group" do
53
+ Tester.uniqueable(:name, :group => :aliases)
54
+ Tester.uniqueable_checks.should eql({:aliases => {Tester => [:name]}})
55
+ end
56
+
57
+ it "should add multiple values to the same group" do
58
+ Tester.uniqueable(:name, :group => :aliases)
59
+ AnotherTester.uniqueable(:name, :group => :aliases)
60
+ Tester.uniqueable_checks.should eql({:aliases => {Tester => [:name], AnotherTester=> [:name]}})
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Uniqueable::UniqueableValidator do
5
+ context "without a group identifier" do
6
+ before(:each) do
7
+ Uniqueable.instance_variable_set(:@uniqueable_checks, nil)
8
+ class User < ActiveRecord::Base
9
+ include Uniqueable
10
+ uniqueable :username
11
+ validates :username, :uniqueable => true
12
+ end
13
+
14
+ class Organisation < ActiveRecord::Base
15
+ include Uniqueable
16
+ uniqueable :alias
17
+ validates :alias, :uniqueable => true
18
+ end
19
+ User.delete_all
20
+ Organisation.delete_all
21
+
22
+ user = User.create(:name => "Gazler", :username => "gazler")
23
+ user.should be_valid
24
+ end
25
+
26
+ it "should validate against the global group (nil)" do
27
+ user2 = User.create(:name => "Gazler2", :username => "gazler")
28
+ user2.should_not be_valid
29
+ end
30
+
31
+ it "should not affect an organisation with a unique alias" do
32
+ organisation = Organisation.create(:name => "Organisation", :alias => "org")
33
+ organisation.should be_valid
34
+ end
35
+
36
+ it "should affect an organistion that clashes with a user" do
37
+ organisation = Organisation.create(:name => "Organisation", :alias => "gazler")
38
+ organisation.should_not be_valid
39
+ end
40
+
41
+ end
42
+
43
+ context "with a group identifier" do
44
+ before(:each) do
45
+ Uniqueable.instance_variable_set(:@uniqueable_checks, nil)
46
+ class User < ActiveRecord::Base
47
+ include Uniqueable
48
+ uniqueable :username, :group => :aliases
49
+ validates :username, :uniqueable => {:group => :aliases}
50
+ end
51
+
52
+ class Organisation < ActiveRecord::Base
53
+ include Uniqueable
54
+ uniqueable :alias, :group => :aliases
55
+ validates :alias, :uniqueable => {:group => :aliases}
56
+ end
57
+
58
+ class AdminUser < ActiveRecord::Base
59
+ include Uniqueable
60
+ uniqueable :username, :group => :aliases
61
+ validates :username, :uniqueable => true
62
+ end
63
+ User.delete_all
64
+ Organisation.delete_all
65
+
66
+ user = User.create(:name => "Gazler", :username => "gazler")
67
+ user.should be_valid
68
+ end
69
+
70
+ it "should validate against the aliases group" do
71
+ user2 = User.create(:name => "Gazler2", :username => "gazler")
72
+ user2.should_not be_valid
73
+ end
74
+
75
+ it "should not affect an organisation with a unique alias" do
76
+ organisation = Organisation.create(:name => "Organisation", :alias => "org")
77
+ organisation.should be_valid
78
+ end
79
+
80
+ it "should affect an organistion that clashes with a user" do
81
+ organisation = Organisation.create(:name => "Organisation", :alias => "gazler")
82
+ organisation.should_not be_valid
83
+ end
84
+
85
+ it "should not affect items validated against a different group" do
86
+ admin_user = AdminUser.create(:name => "Gazler", :username => "gazler")
87
+ admin_user.should be_valid
88
+ end
89
+
90
+ end
91
+
92
+ context "case insensitivity" do
93
+ before(:each) do
94
+ Uniqueable.instance_variable_set(:@uniqueable_checks, nil)
95
+ class User < ActiveRecord::Base
96
+ include Uniqueable
97
+ uniqueable :username
98
+ validates :username, :uniqueable => {:case_sensitive => false}
99
+ end
100
+
101
+ class Organisation < ActiveRecord::Base
102
+ include Uniqueable
103
+ uniqueable :alias
104
+ validates :alias, :uniqueable => true
105
+ end
106
+ User.delete_all
107
+ Organisation.delete_all
108
+ end
109
+
110
+ it "should be case sensitive by default" do
111
+ user = User.create(:name => "Gazler", :username => "Gazler")
112
+ user.should be_valid
113
+ organisation = Organisation.create(:name => "gazler", :alias => "Gazler")
114
+ organisation.should_not be_valid
115
+ organisation = Organisation.create(:name => "gazler", :alias => "gazler")
116
+ organisation.should be_valid
117
+ end
118
+
119
+ it "should allow for case insensitivity" do
120
+ user = User.create(:name => "Gazler", :username => "Gazler")
121
+ user.should be_valid
122
+ user = User.create(:name => "gazler", :username => "gazler")
123
+ user.should_not be_valid
124
+ end
125
+ end
126
+
127
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/uniqueable/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["Gary Rennie"]
6
+ gem.email = ["webmaster@gazler.com"]
7
+ gem.description = %q{Check uniqueness across models}
8
+ gem.summary = %q{Check uniqueness across models}
9
+ gem.homepage = ""
10
+
11
+ gem.files = `git ls-files`.split($\)
12
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
14
+ gem.name = "uniqueable"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Uniqueable::VERSION
17
+
18
+ gem.add_development_dependency "rspec", "~>2.8.0"
19
+ gem.add_development_dependency "activemodel", ">3.0.0"
20
+ gem.add_development_dependency "activerecord", ">3.0.0"
21
+ gem.add_development_dependency "sqlite3"
22
+ gem.add_development_dependency "rake"
23
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uniqueable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Gary Rennie
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-10-02 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 2.8.0
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 2.8.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: activemodel
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>'
36
+ - !ruby/object:Gem::Version
37
+ version: 3.0.0
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>'
44
+ - !ruby/object:Gem::Version
45
+ version: 3.0.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: activerecord
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>'
52
+ - !ruby/object:Gem::Version
53
+ version: 3.0.0
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>'
60
+ - !ruby/object:Gem::Version
61
+ version: 3.0.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: sqlite3
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: rake
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ description: Check uniqueness across models
95
+ email:
96
+ - webmaster@gazler.com
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - .gitignore
102
+ - .rspec
103
+ - Gemfile
104
+ - LICENSE
105
+ - README.md
106
+ - Rakefile
107
+ - lib/uniqueable.rb
108
+ - lib/uniqueable/validates_uniqueable.rb
109
+ - lib/uniqueable/version.rb
110
+ - spec/schema.rb
111
+ - spec/spec_helper.rb
112
+ - spec/uniqueable/module_spec.rb
113
+ - spec/uniqueable/validates_uniqueable_spec.rb
114
+ - uniqueable.gemspec
115
+ homepage: ''
116
+ licenses: []
117
+ post_install_message:
118
+ rdoc_options: []
119
+ require_paths:
120
+ - lib
121
+ required_ruby_version: !ruby/object:Gem::Requirement
122
+ none: false
123
+ requirements:
124
+ - - ! '>='
125
+ - !ruby/object:Gem::Version
126
+ version: '0'
127
+ segments:
128
+ - 0
129
+ hash: 1837060293483589432
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ none: false
132
+ requirements:
133
+ - - ! '>='
134
+ - !ruby/object:Gem::Version
135
+ version: '0'
136
+ segments:
137
+ - 0
138
+ hash: 1837060293483589432
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.24
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: Check uniqueness across models
145
+ test_files:
146
+ - spec/schema.rb
147
+ - spec/spec_helper.rb
148
+ - spec/uniqueable/module_spec.rb
149
+ - spec/uniqueable/validates_uniqueable_spec.rb