globalize3-validations 0.1.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.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/.travis.yml +10 -0
- data/Appraisals +11 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +64 -0
- data/Rakefile +18 -0
- data/gemfiles/rails_3.0.gemfile +7 -0
- data/gemfiles/rails_3.1.gemfile +7 -0
- data/gemfiles/rails_3.2.gemfile +7 -0
- data/globalize3-validations.gemspec +30 -0
- data/lib/active_record/compatibility.rb +45 -0
- data/lib/active_record/validations/globalized_uniqueness.rb +135 -0
- data/lib/globalize3-validations.rb +8 -0
- data/lib/globalize3/validations/version.rb +5 -0
- data/spec/data/models.rb +12 -0
- data/spec/data/schema.rb +22 -0
- data/spec/spec_helper.rb +37 -0
- data/spec/validations/globalized_uniqueness_spec.rb +188 -0
- metadata +180 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzVlMTVhMjEyMmI4YTg1NjM3OTgzY2RmMmIxNjc2YjRjZmExOWI2Ng==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZGMxOGM1M2UyNDlkMGU3NGRmODI0MDIyNjk4ODE5YjJmMzI4MTYxMw==
|
7
|
+
!binary "U0hBNTEy":
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YWZmNDg3ZmQ0MjZiZjA0MWNhODEyYWI5MWJlNGExODk5YTBmYmJmZDA0MmNi
|
10
|
+
MmNhMmM5OTZjMWU3MjExNmM4MWU4MTBjMWJlMzE0YmUyNTZhM2ZiMjM0OWZi
|
11
|
+
OWE1ODAwMDgzYWVkNmNkY2I0MTIxM2UzNTUwNmIwNjY4ODUyOTE=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
NGM3OWVkNTIyZTU2MDNhZDI4NTQ5YmY5MDg3MjA5ZGNhMmM3NjdlYWYwMDRj
|
14
|
+
ZDYyMGFlYWUyNWIzODJmZDlhZWI3NTQ1MzU4ZDY4ZWQwMzg0NmQyNDhhZjUy
|
15
|
+
YjZlZGNlNjZlOTZkZjkxYmNlNjdkN2MwZjY2OTRhMjM1MWE4YjA=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.travis.yml
ADDED
data/Appraisals
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 emjot GmbH & Co. KG
|
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,64 @@
|
|
1
|
+
# globalize3-validations
|
2
|
+
|
3
|
+
[](https://travis-ci.org/emjot/globalize3-validations)
|
4
|
+
|
5
|
+
Provides a uniqueness validator which can be used with globalize3 translated models.
|
6
|
+
|
7
|
+
## Compatibility
|
8
|
+
|
9
|
+
Works with rails 3.1 and 3.2.
|
10
|
+
|
11
|
+
Rails 3.0 is not fully supported (case insensitive validation doesn't work).
|
12
|
+
|
13
|
+
The validator is based on the activerecord 3.2 uniqueness validator.
|
14
|
+
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
Add this line to your application's Gemfile:
|
19
|
+
|
20
|
+
gem 'globalize3-validations'
|
21
|
+
|
22
|
+
And then execute:
|
23
|
+
|
24
|
+
$ bundle
|
25
|
+
|
26
|
+
Or install it yourself as:
|
27
|
+
|
28
|
+
$ gem install globalize3-validations
|
29
|
+
|
30
|
+
## Usage
|
31
|
+
|
32
|
+
Provides a `GlobalizedUniquenessValidator` which checks whether
|
33
|
+
the value of the specified attributes are unique across the system.
|
34
|
+
|
35
|
+
See validates_uniqueness_of in ActiveRecord::Validations::ClassMethods for further explanation.
|
36
|
+
This validator basically works the same, but additionally respects globalize3 model translations.
|
37
|
+
Also, you can use :locale when specifying the :scope option. This will scope the validation to the current locale.
|
38
|
+
|
39
|
+
For instance, if you want to validate that a product title is unique in each locale:
|
40
|
+
|
41
|
+
class Product < ActiveRecord::Base
|
42
|
+
translates :title
|
43
|
+
validates_globalized_uniqueness_of :title, :scope => :locale
|
44
|
+
end
|
45
|
+
|
46
|
+
Or:
|
47
|
+
|
48
|
+
class Product < ActiveRecord::Base
|
49
|
+
translates :title
|
50
|
+
validates :title, :globalized_uniqueness => {:scope => :locale}
|
51
|
+
end
|
52
|
+
|
53
|
+
## Todo / Known Issues
|
54
|
+
|
55
|
+
* Doesn't work yet with rails 3.0 when case_sensitive is set to false
|
56
|
+
* Might not yet work correctly with serialized attributes which are translated (haven't tested that yet)
|
57
|
+
|
58
|
+
## Contributing
|
59
|
+
|
60
|
+
1. Fork it
|
61
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
62
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
63
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
64
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'appraisal'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
5
|
+
require 'bundler/gem_tasks'
|
6
|
+
|
7
|
+
desc 'Default: run all tests with all supported Rails versions'
|
8
|
+
task :default => :all
|
9
|
+
|
10
|
+
desc 'Run tests with all supported Rails versions.'
|
11
|
+
task :all => ["appraisal:install"] do
|
12
|
+
exec('rake appraisal spec')
|
13
|
+
end
|
14
|
+
|
15
|
+
desc 'Run all tests'
|
16
|
+
RSpec::Core::RakeTask.new('spec') do |t|
|
17
|
+
t.pattern = FileList['spec/**/*_spec.rb']
|
18
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'globalize3/validations/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "globalize3-validations"
|
8
|
+
spec.version = Globalize3::Validations::VERSION
|
9
|
+
spec.authors = ["Maximilian Herold"]
|
10
|
+
spec.email = ["herold@emjot.de"]
|
11
|
+
spec.description = %q{adds a uniqueness validator for globalize3 translated models}
|
12
|
+
spec.summary = %q{uniqueness validation for globalize3 translated models}
|
13
|
+
spec.homepage = "https://github.com/emjot/globalize3-validations"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
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_runtime_dependency "rails", "~> 3.0"
|
22
|
+
spec.add_runtime_dependency "globalize3", "~> 0.3"
|
23
|
+
|
24
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
25
|
+
spec.add_development_dependency "appraisal", "~> 0.5.1"
|
26
|
+
spec.add_development_dependency "rspec-rails", "~> 2.8.0"
|
27
|
+
spec.add_development_dependency "database_cleaner", "~> 0.6"
|
28
|
+
spec.add_development_dependency "sqlite3"
|
29
|
+
spec.add_development_dependency "rake"
|
30
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
ActiveSupport.on_load(:active_record) do
|
2
|
+
|
3
|
+
# backport 'case_sensitive_modifier' for AR 3.0.x connection adapters
|
4
|
+
if ActiveRecord::VERSION::STRING.start_with?("3.0.")
|
5
|
+
|
6
|
+
method_name = 'case_sensitive_modifier'
|
7
|
+
|
8
|
+
if defined?(ActiveRecord::ConnectionAdapters::AbstractAdapter)
|
9
|
+
|
10
|
+
abstract_adapter = ActiveRecord::ConnectionAdapters::AbstractAdapter
|
11
|
+
unless abstract_adapter.instance_methods.collect(&:to_s).include?(method_name)
|
12
|
+
abstract_adapter.class_eval do
|
13
|
+
def case_sensitive_modifier(node)
|
14
|
+
node
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
if defined?(ActiveRecord::ConnectionAdapters::MysqlAdapter)
|
20
|
+
adapter = ActiveRecord::ConnectionAdapters::MysqlAdapter
|
21
|
+
unless adapter.instance_methods.collect(&:to_s).include?(method_name)
|
22
|
+
adapter.class_eval do
|
23
|
+
def case_sensitive_modifier(node)
|
24
|
+
Arel::Nodes::Bin.new(node)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
if defined?(ActiveRecord::ConnectionAdapters::Mysql2Adapter)
|
31
|
+
adapter = ActiveRecord::ConnectionAdapters::Mysql2Adapter
|
32
|
+
unless adapter.instance_methods.collect(&:to_s).include?(method_name)
|
33
|
+
adapter.class_eval do
|
34
|
+
def case_sensitive_modifier(node)
|
35
|
+
Arel::Nodes::Bin.new(node)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Validations
|
3
|
+
|
4
|
+
class GlobalizedUniquenessValidator < ActiveModel::EachValidator
|
5
|
+
|
6
|
+
def initialize(options)
|
7
|
+
super(options.reverse_merge(:case_sensitive => true))
|
8
|
+
end
|
9
|
+
|
10
|
+
# Unfortunately, we have to tie Uniqueness validators to a class.
|
11
|
+
def setup(klass)
|
12
|
+
@klass = klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate_each(record, attribute, value)
|
16
|
+
finder_class = find_finder_class_for(record)
|
17
|
+
table = finder_class.arel_table
|
18
|
+
|
19
|
+
coder = record.class.serialized_attributes[attribute.to_s] # FIXME add/test support for serialized attributes with globalize3
|
20
|
+
|
21
|
+
# determine table / attr_column_class respecting globalize3 translations
|
22
|
+
globalized_column_class = find_globalized_column_class_for(record, attribute)
|
23
|
+
globalized = globalized_column_class.present?
|
24
|
+
if globalized
|
25
|
+
attr_column_class = globalized_column_class
|
26
|
+
table = attr_column_class.arel_table
|
27
|
+
else
|
28
|
+
attr_column_class = finder_class
|
29
|
+
end
|
30
|
+
|
31
|
+
if value && coder
|
32
|
+
value = coder.dump value
|
33
|
+
end
|
34
|
+
|
35
|
+
# build relation respecting globalize3 translations
|
36
|
+
relation = build_relation(attr_column_class, table, attribute, value)
|
37
|
+
relation = relation.and(finder_class.arel_table[finder_class.primary_key.to_sym].not_eq(record.send(:id))) if record.persisted?
|
38
|
+
|
39
|
+
Array.wrap(options[:scope]).each do |scope_item|
|
40
|
+
if globalized && ([:locale] | record.class.translated_attribute_names).include?(scope_item)
|
41
|
+
# handle globalize3 translated attribute scopes and :locale scope
|
42
|
+
scope_value = if scope_item == :locale
|
43
|
+
Globalize.locale.to_s
|
44
|
+
else
|
45
|
+
record.read_attribute(scope_item)
|
46
|
+
end
|
47
|
+
relation = relation.and(table[scope_item].eq(scope_value))
|
48
|
+
else
|
49
|
+
scope_value = record.read_attribute(scope_item)
|
50
|
+
relation = relation.and(finder_class.arel_table[scope_item].eq(scope_value))
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# finalize building & execute query (respecting globalize3 translations)
|
55
|
+
scoped = finder_class.unscoped
|
56
|
+
scoped = scoped.joins(:translations) if globalized
|
57
|
+
if scoped.where(relation).exists?
|
58
|
+
record.errors.add(attribute, :taken, options.except(:case_sensitive, :scope).merge(:value => value))
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
protected
|
63
|
+
|
64
|
+
# The check for an existing value should be run from a class that
|
65
|
+
# isn't abstract. This means working down from the current class
|
66
|
+
# (self), to the first non-abstract class. Since classes don't know
|
67
|
+
# their subclasses, we have to build the hierarchy between self and
|
68
|
+
# the record's class.
|
69
|
+
def find_finder_class_for(record) #:nodoc:
|
70
|
+
class_hierarchy = [record.class]
|
71
|
+
|
72
|
+
while class_hierarchy.first != @klass
|
73
|
+
class_hierarchy.insert(0, class_hierarchy.first.superclass)
|
74
|
+
end
|
75
|
+
|
76
|
+
class_hierarchy.detect { |klass| !klass.abstract_class? }
|
77
|
+
end
|
78
|
+
|
79
|
+
# If the attribute of the record is globalized, returns the translation class; otherwise, returns nil.
|
80
|
+
def find_globalized_column_class_for(record, attribute)
|
81
|
+
class_hierarchy = [record.class]
|
82
|
+
|
83
|
+
while class_hierarchy.first != @klass
|
84
|
+
class_hierarchy.insert(0, class_hierarchy.first.superclass)
|
85
|
+
end
|
86
|
+
|
87
|
+
klass = class_hierarchy.detect { |klass| !klass.abstract_class? && klass.respond_to?(:translation_class) }
|
88
|
+
|
89
|
+
if klass && record.class.translated_attribute_names.include?(attribute)
|
90
|
+
klass.translation_class
|
91
|
+
else
|
92
|
+
nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def build_relation(klass, table, attribute, value) #:nodoc:
|
97
|
+
column = klass.columns_hash[attribute.to_s]
|
98
|
+
value = column.limit ? value.to_s.mb_chars[0, column.limit] : value.to_s if value && column.text?
|
99
|
+
|
100
|
+
if !options[:case_sensitive] && value && column.text?
|
101
|
+
# will use SQL LOWER function before comparison
|
102
|
+
relation = table[attribute].lower.eq(table.lower(value))
|
103
|
+
else
|
104
|
+
value = klass.connection.case_sensitive_modifier(value)
|
105
|
+
relation = table[attribute].eq(value)
|
106
|
+
end
|
107
|
+
|
108
|
+
relation
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
module ClassMethods
|
113
|
+
# Validates whether the value of the specified attributes are unique across the system.
|
114
|
+
# Useful for making sure that only one user
|
115
|
+
# can be named "davidhh".
|
116
|
+
#
|
117
|
+
# See validates_uniqueness_of in ActiveRecord::Validations::ClassMethods for further explanation.
|
118
|
+
#
|
119
|
+
# This validator works the same, but additionally respects globalize3 model translations.
|
120
|
+
# Also, you can use :locale as value for :scope.
|
121
|
+
#
|
122
|
+
# For instance, if you want to validate that a product title is unique in each locale:
|
123
|
+
#
|
124
|
+
# class Product < ActiveRecord::Base
|
125
|
+
# translates :title
|
126
|
+
#
|
127
|
+
# validates_globalized_uniqueness_of :title, :scope => :locale
|
128
|
+
# end
|
129
|
+
def validates_globalized_uniqueness_of(*attr_names)
|
130
|
+
validates_with GlobalizedUniquenessValidator, _merge_attributes(attr_names)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
data/spec/data/models.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
class Topic < ActiveRecord::Base
|
2
|
+
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
|
3
|
+
translates :title, :content
|
4
|
+
end
|
5
|
+
|
6
|
+
class Reply < Topic
|
7
|
+
belongs_to :topic, :foreign_key => "parent_id"
|
8
|
+
end
|
9
|
+
|
10
|
+
class Untranslated < ActiveRecord::Base
|
11
|
+
end
|
12
|
+
|
data/spec/data/schema.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
ActiveRecord::Migration.verbose = false
|
2
|
+
|
3
|
+
ActiveRecord::Schema.define do
|
4
|
+
|
5
|
+
create_table :untranslateds, :force => true do |t|
|
6
|
+
t.string :name
|
7
|
+
t.text :content
|
8
|
+
end
|
9
|
+
|
10
|
+
create_table :topics, :force => true do |t|
|
11
|
+
t.integer :parent_id
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :topic_translations, :force => true do |t|
|
16
|
+
t.string :locale
|
17
|
+
t.references :topic
|
18
|
+
t.string :title
|
19
|
+
t.text :content
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require File.expand_path(File.join(File.dirname(__FILE__), '../lib/globalize3-validations'))
|
2
|
+
|
3
|
+
require 'fileutils'
|
4
|
+
require 'logger'
|
5
|
+
require 'database_cleaner'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
|
9
|
+
config.mock_with :rspec
|
10
|
+
|
11
|
+
config.before(:suite) do
|
12
|
+
DatabaseCleaner.strategy = :truncation
|
13
|
+
|
14
|
+
tmpdir = File.join(File.dirname(__FILE__), "../tmp")
|
15
|
+
FileUtils.mkdir(tmpdir) unless File.exist?(tmpdir)
|
16
|
+
log = File.expand_path(File.join(tmpdir, "globalize3_test.log"))
|
17
|
+
FileUtils.touch(log) unless File.exists?(log)
|
18
|
+
ActiveRecord::Base.logger = Logger.new(log)
|
19
|
+
ActiveRecord::LogSubscriber.attach_to(:active_record)
|
20
|
+
|
21
|
+
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
22
|
+
require File.expand_path('../data/schema', __FILE__)
|
23
|
+
require File.expand_path('../data/models', __FILE__)
|
24
|
+
|
25
|
+
I18n.locale = I18n.default_locale = :en
|
26
|
+
Globalize.locale = nil
|
27
|
+
end
|
28
|
+
|
29
|
+
config.before(:each) do
|
30
|
+
DatabaseCleaner.start
|
31
|
+
end
|
32
|
+
|
33
|
+
config.after(:each) do
|
34
|
+
DatabaseCleaner.clean
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
require 'spec_helper'
|
3
|
+
|
4
|
+
describe 'ActiveRecord::Validations::GlobalizedUniquenessValidator' do
|
5
|
+
|
6
|
+
before(:each) do
|
7
|
+
[Untranslated, Topic, Reply].each do |k|
|
8
|
+
k.reset_callbacks(:validate)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
it "should validate uniqueness on an untranslated model" do
|
13
|
+
Untranslated.validates_globalized_uniqueness_of(:name)
|
14
|
+
|
15
|
+
u = Untranslated.new("name" => "I'm uniqué!")
|
16
|
+
u.save.should be_true
|
17
|
+
|
18
|
+
u.content = "Remaining unique"
|
19
|
+
u.save.should be_true
|
20
|
+
|
21
|
+
u2 = Untranslated.new("name" => "I'm uniqué!")
|
22
|
+
u2.valid?.should be_false
|
23
|
+
u2.errors[:name].should == ["has already been taken"]
|
24
|
+
u2.save.should be_false
|
25
|
+
|
26
|
+
u2.name = "Now Im really also unique"
|
27
|
+
u2.save.should be_true
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should validate uniqueness" do
|
31
|
+
Topic.validates_globalized_uniqueness_of(:title)
|
32
|
+
title = "I'm uniqué!"
|
33
|
+
|
34
|
+
t = Topic.new("title" => title)
|
35
|
+
t.save.should be_true
|
36
|
+
|
37
|
+
t.content = "Remaining unique"
|
38
|
+
t.save.should be_true
|
39
|
+
|
40
|
+
t2 = Topic.new("title" => title)
|
41
|
+
t2.valid?.should be_false
|
42
|
+
t2.errors[:title].should == ["has already been taken"]
|
43
|
+
t2.save.should be_false
|
44
|
+
|
45
|
+
Globalize.with_locale(:de) do
|
46
|
+
t2.title = title
|
47
|
+
t2.valid?.should be_false
|
48
|
+
t2.errors[:title].should == ["has already been taken"]
|
49
|
+
t2.save.should be_false
|
50
|
+
end
|
51
|
+
|
52
|
+
t2.title = "Now Im really also unique"
|
53
|
+
t2.save.should be_true
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should validate uniqueness with :locale scope" do
|
57
|
+
Topic.validates_globalized_uniqueness_of(:title, :scope => :locale)
|
58
|
+
title = "I'm uniqué!"
|
59
|
+
|
60
|
+
t = Topic.new("title" => title)
|
61
|
+
t.save.should be_true
|
62
|
+
|
63
|
+
t.content = "Remaining unique"
|
64
|
+
t.save.should be_true
|
65
|
+
|
66
|
+
t2 = Topic.new("title" => title)
|
67
|
+
t2.valid?.should be_false
|
68
|
+
t2.errors[:title].should == ["has already been taken"]
|
69
|
+
t2.save.should be_false
|
70
|
+
|
71
|
+
Globalize.with_locale(:de) do
|
72
|
+
t2.title = title
|
73
|
+
t2.valid?.should be_true
|
74
|
+
t2.save.should be_true
|
75
|
+
|
76
|
+
t3 = Topic.new("title" => title)
|
77
|
+
t3.valid?.should be_false
|
78
|
+
t3.errors[:title].should == ["has already been taken"]
|
79
|
+
end
|
80
|
+
|
81
|
+
t2.title = "Now Im really also unique"
|
82
|
+
t2.save.should be_true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should validate uniqueness with validates" do
|
86
|
+
Topic.validates :title, :globalized_uniqueness => {:scope => :locale}
|
87
|
+
Topic.create!('title' => 'abc')
|
88
|
+
|
89
|
+
t2 = Topic.new('title' => 'abc')
|
90
|
+
t2.valid?.should be_false
|
91
|
+
t2.errors[:title].should be_true
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should validate uniqueness with multiple translated attributes in scope" do
|
95
|
+
Topic.validates_globalized_uniqueness_of(:content, :scope => [:locale, :title])
|
96
|
+
title = "I'm uniqué!"
|
97
|
+
content = "hello world"
|
98
|
+
|
99
|
+
t = Topic.new("title" => title, :content => content)
|
100
|
+
t.save.should be_true
|
101
|
+
|
102
|
+
t2 = Topic.new("title" => title, :content => content)
|
103
|
+
t2.valid?.should be_false
|
104
|
+
t2.errors[:content].should == ["has already been taken"]
|
105
|
+
t2.save.should be_false
|
106
|
+
|
107
|
+
Globalize.with_locale(:de) do
|
108
|
+
t2.title = title
|
109
|
+
t2.content = content
|
110
|
+
t2.valid?.should be_true
|
111
|
+
t2.save.should be_true
|
112
|
+
|
113
|
+
t3 = Topic.new("title" => title, "content" => content)
|
114
|
+
t3.valid?.should be_false
|
115
|
+
t3.errors[:content].should == ["has already been taken"]
|
116
|
+
end
|
117
|
+
|
118
|
+
t2.title = "Now Im really also unique"
|
119
|
+
t2.content = content
|
120
|
+
t2.save.should be_true
|
121
|
+
|
122
|
+
Topic.new("title" => title, :content => "foo").save.should be_true
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should validate uniqueness with both translated and untranslated attributes in scope" do
|
126
|
+
Reply.validates_globalized_uniqueness_of(:content, :scope => [:locale, "parent_id"])
|
127
|
+
content = "hello world"
|
128
|
+
|
129
|
+
t = Topic.create("title" => "I'm unique!")
|
130
|
+
|
131
|
+
r1 = t.replies.create "title" => "r1", "content" => content
|
132
|
+
r1.valid?.should be_true
|
133
|
+
|
134
|
+
r2 = t.replies.create "title" => "r2", "content" => content
|
135
|
+
r2.valid?.should be_false
|
136
|
+
|
137
|
+
Globalize.with_locale(:de) do
|
138
|
+
r2.content = content
|
139
|
+
r2.save.should be_true
|
140
|
+
end
|
141
|
+
|
142
|
+
r2.content = "something else"
|
143
|
+
r2.save.should be_true
|
144
|
+
|
145
|
+
t2 = Topic.create("title" => "I'm unique too!")
|
146
|
+
r3 = t2.replies.create "title" => "r3", "content" => content
|
147
|
+
r3.valid?.should be_true
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should validate case sensitive uniqueness" do
|
151
|
+
Topic.validates_globalized_uniqueness_of(:title, :case_sensitive => true, :allow_nil => true)
|
152
|
+
|
153
|
+
t = Topic.new("title" => "I'm unique!")
|
154
|
+
t.save.should be_true
|
155
|
+
|
156
|
+
t.content = "Remaining unique"
|
157
|
+
t.save.should be_true
|
158
|
+
|
159
|
+
t2 = Topic.new("title" => "I'M UNIQUE!")
|
160
|
+
t2.valid?.should be_true
|
161
|
+
t2.save.should be_true
|
162
|
+
|
163
|
+
t3 = Topic.new("title" => "I'M uNiQUe!")
|
164
|
+
t3.valid?.should be_true
|
165
|
+
t3.save.should be_true
|
166
|
+
|
167
|
+
t4 = Topic.new("title" => "I'M uNiQUe!")
|
168
|
+
t4.valid?.should be_false
|
169
|
+
t4.save.should be_false
|
170
|
+
t4.errors[:title].should == ["has already been taken"]
|
171
|
+
end
|
172
|
+
|
173
|
+
# FIXME this test fails on rails 3.0.x
|
174
|
+
it "should validate case insensitive uniqueness" do
|
175
|
+
Topic.validates_globalized_uniqueness_of(:title, :case_sensitive => false, :allow_nil => true)
|
176
|
+
|
177
|
+
t = Topic.new("title" => "I'm unique!")
|
178
|
+
t.save.should be_true
|
179
|
+
|
180
|
+
t2 = Topic.new("title" => "I'M UNIQUE!")
|
181
|
+
t2.valid?.should be_false
|
182
|
+
t2.save.should be_false
|
183
|
+
t2.errors[:title].should == ["has already been taken"]
|
184
|
+
end
|
185
|
+
|
186
|
+
it "should validate uniqueness with a translated serialized attribute"
|
187
|
+
|
188
|
+
end
|
metadata
ADDED
@@ -0,0 +1,180 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: globalize3-validations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Maximilian Herold
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-21 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '3.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '3.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: globalize3
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ~>
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.3'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ~>
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0.3'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: bundler
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ~>
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '1.3'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.3'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: appraisal
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ~>
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 0.5.1
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ~>
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: 0.5.1
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rspec-rails
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ~>
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: 2.8.0
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 2.8.0
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: database_cleaner
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.6'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ~>
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.6'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: sqlite3
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ! '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - ! '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: rake
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - ! '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
description: adds a uniqueness validator for globalize3 translated models
|
126
|
+
email:
|
127
|
+
- herold@emjot.de
|
128
|
+
executables: []
|
129
|
+
extensions: []
|
130
|
+
extra_rdoc_files: []
|
131
|
+
files:
|
132
|
+
- .gitignore
|
133
|
+
- .rspec
|
134
|
+
- .travis.yml
|
135
|
+
- Appraisals
|
136
|
+
- Gemfile
|
137
|
+
- LICENSE.txt
|
138
|
+
- README.md
|
139
|
+
- Rakefile
|
140
|
+
- gemfiles/rails_3.0.gemfile
|
141
|
+
- gemfiles/rails_3.1.gemfile
|
142
|
+
- gemfiles/rails_3.2.gemfile
|
143
|
+
- globalize3-validations.gemspec
|
144
|
+
- lib/active_record/compatibility.rb
|
145
|
+
- lib/active_record/validations/globalized_uniqueness.rb
|
146
|
+
- lib/globalize3-validations.rb
|
147
|
+
- lib/globalize3/validations/version.rb
|
148
|
+
- spec/data/models.rb
|
149
|
+
- spec/data/schema.rb
|
150
|
+
- spec/spec_helper.rb
|
151
|
+
- spec/validations/globalized_uniqueness_spec.rb
|
152
|
+
homepage: https://github.com/emjot/globalize3-validations
|
153
|
+
licenses:
|
154
|
+
- MIT
|
155
|
+
metadata: {}
|
156
|
+
post_install_message:
|
157
|
+
rdoc_options: []
|
158
|
+
require_paths:
|
159
|
+
- lib
|
160
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
161
|
+
requirements:
|
162
|
+
- - ! '>='
|
163
|
+
- !ruby/object:Gem::Version
|
164
|
+
version: '0'
|
165
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
166
|
+
requirements:
|
167
|
+
- - ! '>='
|
168
|
+
- !ruby/object:Gem::Version
|
169
|
+
version: '0'
|
170
|
+
requirements: []
|
171
|
+
rubyforge_project:
|
172
|
+
rubygems_version: 2.0.3
|
173
|
+
signing_key:
|
174
|
+
specification_version: 4
|
175
|
+
summary: uniqueness validation for globalize3 translated models
|
176
|
+
test_files:
|
177
|
+
- spec/data/models.rb
|
178
|
+
- spec/data/schema.rb
|
179
|
+
- spec/spec_helper.rb
|
180
|
+
- spec/validations/globalized_uniqueness_spec.rb
|