uniqueable 0.0.2

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