babylonia-rails 0.0.2 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 23b9f13165bbb20bfdb7df78223d70aeac3a2786
4
- data.tar.gz: 9852997ff2602bb4a6fdc3df158849ac70dfcba1
3
+ metadata.gz: 23a9d0abe039872a11f1cc2e55c0d4d1e50898f1
4
+ data.tar.gz: 256aedb91e172f3bdcc51d5e03de89dd6348408f
5
5
  SHA512:
6
- metadata.gz: 75121a38895577938c5c73c80118aa150621435dd096a28d4b720de9a7ccf0a509ac17f5b48ce0ce8edf3f532b0799eabafe5a3976a5dccd6720f0dbb6bd5812
7
- data.tar.gz: 8e1847e0126bd7f30da95912610795719f64e3411b1b69c6f7295bc103ac27f2f5c401becbfe8da221720ead3eb02a09a6c91b631fb182995eed8da2f55c5c7d
6
+ metadata.gz: 23c004c9cc8086efce000c3bfc5c37d33f660ce32aa818a30730c603076a81875b33178dafdeae4c191108c0e455fb91e33bba507145ffcd5003d1782b8cc443
7
+ data.tar.gz: 392d0bc2877b8ffd537acbec02d019a77d72d061025e982e98f7f92025c4c6969efc9e5629532de68608164fcb6b1a6c462d17111077f2f534d6950dd9e149da
data/README.rdoc CHANGED
@@ -7,28 +7,44 @@ In your gemfile
7
7
  Or via the gem command
8
8
  gem install babylonia-rails
9
9
 
10
- == How to ride the rails to babylonia
11
- Feels like babylonia, validates like rails
10
+ == How to Ride the Rails to Babylonia
11
+ Feels like babylonia, validates like rails: You can use all rails validators except confirmation. Just add the validation options under :locales in the validation as you would normally do.
12
12
 
13
13
  class BabylonianFields < ActiveRecord::Base
14
- build_babylonian_tower_on :grass
14
+ build_babylonian_tower_on :grass, :sand, :sea
15
15
 
16
- validate :grass, languages: {present: [:en, :de, :it], length: 2..1000}
16
+ validates :grass, locales: { presence: [:en, :de, :it], length: { in: 6..20 }}
17
+ validates :sand, locales: { absence: [:en, :it], format: { with: /some nifty regexp/ }}
17
18
  end
18
19
 
19
- I18n.locale #=> :en, so it will set en
20
+ You can add a :locales option to each validation to determine which locales get validated with the said validator
21
+
22
+ class BabylonianFields < ActiveRecord::Base
23
+ build_babylonian_tower_on :grass, :sand, :sea
24
+
25
+ validates :grass, locales: { presence: [:en, :de, :it], length: { in: 6..20, locales: [:it] }}
26
+ validates :sand, locales: { absence: [:en, :it], uniqueness: { scope: :some_scope, locales: [:de, :en] }}
27
+ end
20
28
 
21
- babylonian_field = BabylonianFields.new grass: 'Hello'
22
-
23
- babylonian_field.valid? #=> false
24
- babylonian_field.errors.full_messages #=> "Grass should also be translated in DE and IT"
25
- babylonian_field.grass = {de: 'DEUTSCH', it: 'I'}
26
- babylonian_field.valid? #=> false
27
- babylonian_field.errors.full_messages #=> "Grass should be between 2 an 1000 characters in IT"
28
- babylonian_field.grass = {it: 'IT'}
29
- babylonian_field.valid? #=> true
30
-
31
- == For Basic Usage
29
+ Yes, there's also uniqueness validation. It works with a LIKE / arel.matches query.
30
+
31
+ For all the details on rails validation visit {here}[http://edgeguides.rubyonrails.org/active_record_validations.html#validation-helpers].
32
+
33
+ Please be aware that validations will only validate available locales!
34
+
35
+ == What About Forms?
36
+
37
+ form_for(@babylonian_field, url: {action: 'create'}) do |f|
38
+ f.text_field :grass_en
39
+ f.text_field :grass_de
40
+ f.text_field :grass_it
41
+ f.submit "Create"
42
+ end
43
+
44
+ == What about dynamic Finders?
45
+ Uhhm, no. Arel itself is pretty nifty, so if you want to find something by SQL, you will.
46
+
47
+ == For Further Usage
32
48
  Visit {babylonia}[http://github.com/beatrichartz/babylonia]
33
49
 
34
50
  == Contributing to babylonia-rails
@@ -18,7 +18,7 @@ Gem::Specification.new do |s|
18
18
 
19
19
  active_record = ENV["ACTIVE_RECORD_VERSION"] || ">= 3.2.0"
20
20
 
21
- s.add_dependency "babylonia", ">= 0.0.3"
21
+ s.add_dependency "babylonia", ">= 0.1.1"
22
22
  s.add_dependency "activerecord", active_record
23
23
  s.add_development_dependency "bundler", ">= 1.0.0"
24
24
  end
@@ -1,4 +1,4 @@
1
1
  require 'babylonia'
2
2
  require "babylonia/rails/class_methods"
3
+ require "babylonia/rails/validators"
3
4
  require "babylonia/rails/integration"
4
- require "babylonia/rails/validator"
@@ -1,4 +1,5 @@
1
1
  require 'babylonia'
2
2
  require 'active_record'
3
3
 
4
- ActiveRecord::Base.send(:extend, Babylonia::ClassMethods)
4
+ ActiveRecord::Base.send(:extend, Babylonia::ClassMethods)
5
+ ActiveRecord::Base.send(:include, Babylonia::Rails::Validators)
@@ -0,0 +1,2 @@
1
+ require 'babylonia/rails/validators/locale_validator'
2
+ require 'babylonia/rails/validators/uniqueness_validator'
@@ -0,0 +1,62 @@
1
+ # Rails each validator for locales
2
+ # @params [Hash] options
3
+ # @option [Hash] locales The validations to do on the locales of an attribute
4
+ require 'active_model'
5
+
6
+ module Babylonia
7
+ module Rails
8
+ module Validators
9
+ class LocalesValidator < ::ActiveModel::EachValidator
10
+ attr_reader :validators
11
+
12
+ def validate_each(record, attribute, value)
13
+ @validators = {}
14
+ record.available_locales.each do |lang|
15
+ validations = ActiveRecord::VERSION::MAJOR < 4 ? [:acceptance, :exclusion, :format, :inclusion, :length, :numericality, :presence, :uniqueness] : [:absence, :acceptance, :exclusion, :format, :inclusion, :length, :numericality, :presence, :uniqueness]
16
+ validations.each do |validation|
17
+ add_validator validation, attribute, lang if should_validate?(validation, lang)
18
+ end
19
+ end
20
+
21
+ validators.each do |validator, attributes|
22
+ if validator == :uniqueness
23
+ Babylonia::Rails::Validators::LocaleUniquenessValidator.new(validator_attributes(validator, attributes)).validate(record)
24
+ else
25
+ "ActiveModel::Validations::#{validator.to_s.classify}Validator".constantize.new(validator_attributes(validator, attributes)).validate(record)
26
+ end
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def add_validator validator, attribute, lang
33
+ @validators ||= {}
34
+ if validator == :uniqueness
35
+ @validators[validator] ||= { attributes: {} }
36
+ @validators[validator][:attributes].merge! lang => attribute
37
+ else
38
+ @validators[validator] ||= { attributes: [] }
39
+ @validators[validator][:attributes] << :"#{attribute}_#{lang}"
40
+ end
41
+ end
42
+
43
+ def should_validate?(option, lang)
44
+ if [:presence, :absence, :uniqueness, :numericality].include?(option)
45
+ options[option] == true || (options[option].is_a?(Array) && options[option].include?(lang)) || (options[option].is_a?(Hash) && locale_included_in_validation_options?(option, lang))
46
+ else
47
+ options[option].is_a?(Hash) && locale_included_in_validation_options?(option, lang)
48
+ end
49
+ end
50
+
51
+ def locale_included_in_validation_options?(validation_name, lang)
52
+ options[validation_name][:locales].blank? || options[validation_name][:locales].include?(lang)
53
+ end
54
+
55
+ def validator_attributes(validator, attributes)
56
+ options[validator].is_a?(Hash) ? options[validator].delete_if{|k,v| k == :locales}.merge(attributes) : attributes
57
+ end
58
+
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,74 @@
1
+ require 'active_record'
2
+
3
+ module Babylonia
4
+ module Rails
5
+ module Validators
6
+ class LocaleUniquenessValidator < ::ActiveRecord::Validations::UniquenessValidator
7
+
8
+ def validate(record)
9
+ (ActiveRecord::VERSION::MAJOR > 3 ? attributes : attributes.first).each do |locale, attribute|
10
+ value = record.read_attribute_for_validation(:"#{attribute}_#{locale}")
11
+ next if (value.nil? && options[:allow_nil]) || (value.blank? && options[:allow_blank])
12
+ validate_each(record, attribute, locale, value)
13
+ end
14
+ end
15
+
16
+ def validate_each(record, attribute, locale, value)
17
+ finder_class = find_finder_class_for(record)
18
+ table = finder_class.arel_table
19
+
20
+ relation = build_relation(finder_class, table, attribute, locale, value)
21
+ relation = relation.and(table[finder_class.primary_key.to_sym].not_eq(record.id)) if record.persisted?
22
+ if ActiveRecord::VERSION::MAJOR > 3
23
+ relation = scope_relation(record, table, relation)
24
+ else
25
+ Array.wrap(options[:scope]).each do |scope_item|
26
+ scope_value = record.read_attribute(scope_item)
27
+ relation = relation.and(table[scope_item].eq(scope_value))
28
+ end
29
+ end
30
+ relation = finder_class.unscoped.where(relation)
31
+ relation = relation.merge(options[:conditions]) if options[:conditions]
32
+
33
+ if relation.exists?
34
+ error_options = options.except(:case_sensitive, :scope, :conditions)
35
+ error_options[:value] = value
36
+
37
+ record.errors.add(:"#{attribute}_#{locale}", :taken, error_options)
38
+ end
39
+ end
40
+
41
+ protected
42
+
43
+ # Copy from rails uniqueness validator
44
+ # The check for an existing value should be run from a class that
45
+ # isn't abstract. This means working down from the current class
46
+ # (self), to the first non-abstract class. Since classes don't know
47
+ # their subclasses, we have to build the hierarchy between self and
48
+ # the record's class.
49
+ def find_finder_class_for(record) #:nodoc:
50
+ class_hierarchy = [record.class]
51
+
52
+ while class_hierarchy.last != @klass && class_hierarchy.last.superclass
53
+ class_hierarchy.push(class_hierarchy.last.superclass)
54
+ end
55
+
56
+ class_hierarchy.detect { |klass| !klass.abstract_class? }
57
+ end
58
+
59
+ def build_relation(klass, table, attribute, locale, value) #:nodoc:
60
+ column = klass.columns_hash[attribute.to_s]
61
+ value = klass.connection.type_cast(value, column)
62
+ value = YAML.dump({locale => value}).gsub(/\A[^\n]+/, '')
63
+ value = value.to_s[0, column.limit] if value && column.limit && column.text?
64
+ if !options[:case_sensitive] && value && column.text?
65
+ table[attribute].lower.matches("%#{value.respond_to?(:expr) ? value.expr : value.downcase}%")
66
+ else
67
+ value = klass.connection.case_sensitive_modifier(value) unless value.nil?
68
+ table[attribute].matches("%#{value.expr}%")
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -1,5 +1,5 @@
1
1
  module Babylonia
2
2
  module Rails
3
- VERSION = '0.0.2'
3
+ VERSION = '0.1.2'
4
4
  end
5
5
  end
@@ -2,13 +2,13 @@ require 'spec_helper'
2
2
 
3
3
  describe "Integration" do
4
4
 
5
- class BabylonianField < ActiveRecord::Base
6
-
5
+ class BabylonianIntegratedField < ActiveRecord::Base
6
+ self.table_name = 'babylonian_fields'
7
7
  end
8
8
 
9
9
  describe "Babylonia" do
10
10
  it "should already have extended active record" do
11
- BabylonianField.should be_respond_to(:build_babylonian_tower_on)
11
+ BabylonianIntegratedField.should be_respond_to(:build_babylonian_tower_on)
12
12
  end
13
13
  end
14
14
 
@@ -1,11 +1,12 @@
1
1
  require 'spec_helper'
2
2
 
3
- describe LanguagesValidator do
3
+ describe Babylonia::Rails::Validators::LocalesValidator do
4
4
 
5
- class BabylonianField < ActiveRecord::Base
5
+ class BabylonianPersistentField < ActiveRecord::Base
6
+ self.table_name = 'babylonian_fields'
6
7
 
7
8
  build_babylonian_tower_on :marshes
8
- validates :marshes, languages: { present: [:de, :en, :it], length: 5..11 }
9
+ validates :marshes, locales: { presence: [:de, :en, :it], length: {in: 5..11} }
9
10
 
10
11
  end
11
12
 
@@ -13,7 +14,7 @@ describe LanguagesValidator do
13
14
  before(:each) do
14
15
  I18n.stub available_locales: [:de, :en, :it]
15
16
  end
16
- subject { BabylonianField.new(marshes: {en: 'Hello', de: 'Hello', it: 'Hello'})}
17
+ subject { BabylonianPersistentField.new(marshes: {en: 'Hello', de: 'Hello', it: 'Hello'})}
17
18
  it "should be possible to store the string value" do
18
19
  subject.save!
19
20
  subject.reload
@@ -0,0 +1,144 @@
1
+ require 'spec_helper'
2
+
3
+ describe Babylonia::Rails::Validators::LocalesValidator do
4
+ let(:format_validation_options) {
5
+ {
6
+ with: /\A\z/,
7
+ locales: [:de, :en, :it]
8
+ }
9
+ }
10
+ let(:numericality_validation_options) {
11
+ {
12
+ only_integer: true,
13
+ locales: [:de, :en, :it]
14
+ }
15
+ }
16
+ let(:inclusion_validation_options) {
17
+ {
18
+ in: %w(small medium large),
19
+ locales: [:de, :en, :it]
20
+ }
21
+ }
22
+ let(:exclusion_validation_options) {
23
+ {
24
+ in: %w(big huge),
25
+ locales: [:de, :en, :it]
26
+ }
27
+ }
28
+ let(:length_validation_options) {
29
+ {
30
+ in: 5..11,
31
+ locales: [:de, :en, :it]
32
+ }
33
+ }
34
+ let(:validation_options) {
35
+ {
36
+ presence: [:de, :en, :it],
37
+ absence: [:pi, :gb, :er],
38
+ format: format_validation_options,
39
+ numericality: numericality_validation_options,
40
+ inclusion: inclusion_validation_options,
41
+ exclusion: exclusion_validation_options,
42
+ length: length_validation_options
43
+ }
44
+ }
45
+
46
+ class BabylonianField < ActiveRecord::Base
47
+
48
+ build_babylonian_tower_on :marshes
49
+
50
+ class << self
51
+ attr_accessor :installed_validations
52
+ def install_validations(options)
53
+ self.installed_validations = true
54
+ validates :marshes, locales: options
55
+ end
56
+ end
57
+
58
+ end
59
+
60
+ class BabylonianSecondField < ActiveRecord::Base
61
+ self.table_name = 'babylonian_fields'
62
+
63
+ build_babylonian_tower_on :marshes
64
+
65
+ class << self
66
+ attr_accessor :installed_validations
67
+ def install_validations(options)
68
+ self.installed_validations = true
69
+ validates :marshes, locales: options
70
+ end
71
+ end
72
+ end
73
+
74
+ class BabylonianThirdField < ActiveRecord::Base
75
+ self.table_name = 'babylonian_fields'
76
+
77
+ build_babylonian_tower_on :marshes
78
+
79
+ class << self
80
+ attr_accessor :installed_validations
81
+ def install_validations(options)
82
+ self.installed_validations = true
83
+ validates :marshes, allow_nil: true, locales: options
84
+ end
85
+ end
86
+ end
87
+
88
+ before(:each) do
89
+ I18n.stub available_locales: [:de, :en, :it, :pi, :gb, :er]
90
+ # it is not allowed to access structures created by let in before(:all), but installing the validators on each run is also not
91
+ # the way to go.
92
+ BabylonianField.install_validations(validation_options) unless BabylonianField.installed_validations
93
+ BabylonianSecondField.install_validations(validation_options) unless BabylonianSecondField.installed_validations
94
+ BabylonianThirdField.install_validations(validation_options) unless BabylonianThirdField.installed_validations
95
+ end
96
+
97
+ describe "rails validations except confirmation, acceptance and uniqueness" do
98
+ subject { BabylonianField.new }
99
+ let(:format_validator) { double('format_validator') }
100
+ let(:inclusion_validator) { double('inclusion_validator') }
101
+ let(:exclusion_validator) { double('exlusion_validator') }
102
+ let(:length_validator) { double('length_validator') }
103
+ let(:presence_validator) { double('presence_validator') }
104
+ let(:absence_validator) { double('absence_validator') }
105
+ let(:numericality_validator) { double('absence_validator') }
106
+
107
+ it "should pass them on to the rails validators" do
108
+ [:format, :inclusion, :exclusion, :length, :numericality].each do |validator|
109
+ options = send(:"#{validator}_validation_options")
110
+ attributes = options[:locales].map{|l| :"marshes_#{l}"}
111
+ "ActiveModel::Validations::#{validator.to_s.classify}Validator".constantize.should_receive(:new).with(options.dup.delete_if{|k,v| k == :locales}.merge(attributes: attributes)).and_return(send(:"#{validator}_validator"))
112
+ send(:"#{validator}_validator").should_receive(:validate).with(subject)
113
+ end
114
+ ActiveModel::Validations::PresenceValidator.should_receive(:new).with(attributes: [:marshes_de,:marshes_en,:marshes_it]).and_return(presence_validator)
115
+ presence_validator.should_receive(:validate).with(subject)
116
+ unless ActiveRecord::VERSION::MAJOR < 4
117
+ ActiveModel::Validations::AbsenceValidator.should_receive(:new).with(attributes: [:marshes_pi,:marshes_gb,:marshes_er]).and_return(absence_validator)
118
+ absence_validator.should_receive(:validate).with(subject)
119
+ end
120
+ subject.valid? #=> this will be true since all calls are mocked
121
+ end
122
+ context "integration" do
123
+ context "with defaults" do
124
+ subject { BabylonianSecondField.new }
125
+ it "should work for all kinds of errors" do
126
+ subject.should_not be_valid
127
+ [:de, :en, :it].each do |lang|
128
+ subject.errors[:"marshes_#{lang}"].should == ["is not included in the list", "is too short (minimum is 5 characters)", "is not a number", "can't be blank"]
129
+ end
130
+ end
131
+ end
132
+ context "with allow_nil set to true" do
133
+ subject { BabylonianThirdField.new }
134
+ it "should allow blank" do
135
+ subject.should be_valid
136
+ subject.errors.should be_blank
137
+ end
138
+ end
139
+ end
140
+
141
+ end
142
+
143
+
144
+ end
@@ -0,0 +1,168 @@
1
+ require 'spec_helper'
2
+
3
+ describe Babylonia::Rails::Validators::LocaleUniquenessValidator do
4
+ let(:case_sensitive_validation_options) {
5
+ {
6
+ uniqueness: {
7
+ locales: [:de, :en, :fr]
8
+ }
9
+ }
10
+ }
11
+ let(:case_insensitive_validation_options) {
12
+ {
13
+ uniqueness: {
14
+ case_sensitive: false,
15
+ locales: [:de, :en, :fr]
16
+ }
17
+ }
18
+ }
19
+ let(:scoped_validation_options) {
20
+ {
21
+ uniqueness: {
22
+ scope: :some_value,
23
+ case_sensitive: false,
24
+ locales: [:de, :en, :fr]
25
+ }
26
+ }
27
+ }
28
+
29
+ class BabylonianUniqueField < ActiveRecord::Base
30
+ self.table_name = 'babylonian_fields'
31
+
32
+ build_babylonian_tower_on :marshes
33
+
34
+ class << self
35
+ attr_accessor :installed_validations
36
+ def install_validations(options)
37
+ self.installed_validations = true
38
+ validates :marshes, locales: options
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ class BabylonianUniqueSecondField < ActiveRecord::Base
45
+ self.table_name = 'babylonian_fields'
46
+
47
+ build_babylonian_tower_on :marshes
48
+
49
+ class << self
50
+ attr_accessor :installed_validations
51
+ def install_validations(options)
52
+ self.installed_validations = true
53
+ validates :marshes, locales: options
54
+ end
55
+ end
56
+ end
57
+
58
+ class BabylonianUniqueThirdField < ActiveRecord::Base
59
+ self.table_name = 'babylonian_fields'
60
+
61
+ build_babylonian_tower_on :marshes
62
+
63
+ class << self
64
+ attr_accessor :installed_validations
65
+ def install_validations(options)
66
+ self.installed_validations = true
67
+ validates :marshes, allow_nil: true, locales: options
68
+ end
69
+ end
70
+ end
71
+
72
+ before(:each) do
73
+ I18n.stub available_locales: [:de, :en, :it, :pi, :fr, :er]
74
+ # it is not allowed to access structures created by let in before(:all), but installing the validators on each run is also not
75
+ # the way to go.
76
+ BabylonianUniqueField.install_validations(case_insensitive_validation_options) unless BabylonianUniqueField.installed_validations
77
+ BabylonianUniqueSecondField.install_validations(case_sensitive_validation_options) unless BabylonianUniqueSecondField.installed_validations
78
+ BabylonianUniqueThirdField.install_validations(scoped_validation_options) unless BabylonianUniqueThirdField.installed_validations
79
+ end
80
+
81
+ describe "case insensitive uniqueness validation" do
82
+ let(:attributes) { { marshes: {en: "Hello, I'm a string!", de: "Hallo,\nIch bin ein Faden!", fr: "Bonjour: Je suis un fil" } } }
83
+ let(:duplicate_attributes) { { marshes: {en: "hello, I'm a string!", de: "Hallo,\nich bin ein faden!", fr: "Bonjour: je suis un fil" } } }
84
+ context "with an existing record" do
85
+ let!(:existing) { BabylonianUniqueField.create(attributes) }
86
+ subject { BabylonianUniqueField.new(duplicate_attributes) }
87
+ after(:each) do
88
+ existing.destroy
89
+ end
90
+ it "should validate the records uniqueness" do
91
+ subject.should_not be_valid
92
+ subject.errors.full_messages.should == ["Marshes de has already been taken", "Marshes en has already been taken", "Marshes fr has already been taken"]
93
+ end
94
+ end
95
+ context "with no existing record" do
96
+ subject { BabylonianUniqueField.new(duplicate_attributes) }
97
+ it "should validate the records uniqueness" do
98
+ subject.should be_valid
99
+ end
100
+ end
101
+ end
102
+
103
+ describe "case sensitive uniqueness validation" do
104
+ let(:attributes) { { marshes: {en: "Hello, I'm a string!", de: "Hallo,\nIch bin ein Faden!", fr: "Bonjour: Je suis un fil" } } }
105
+ let(:other_case_attributes) { { marshes: {en: "hello, I'm a string!", de: "Hallo,\nich bin ein faden!", fr: "Bonjour: je suis un fil" } } }
106
+ context "with an existing record" do
107
+ let!(:existing) { BabylonianUniqueSecondField.create(attributes) }
108
+ subject { BabylonianUniqueSecondField.new(attributes) }
109
+ after(:each) do
110
+ existing.destroy
111
+ end
112
+ it "should validate the records uniqueness" do
113
+ subject.should_not be_valid
114
+ subject.errors.full_messages.should == ["Marshes de has already been taken", "Marshes en has already been taken", "Marshes fr has already been taken"]
115
+ end
116
+ end
117
+ context "with an existing record with differently cased attributes" do
118
+ let!(:existing) { BabylonianUniqueSecondField.create(attributes) }
119
+ subject { BabylonianUniqueSecondField.new(other_case_attributes) }
120
+ after(:each) do
121
+ existing.destroy
122
+ end
123
+ it "should validate the records uniqueness" do
124
+ subject.should be_valid
125
+ end
126
+ end
127
+ context "with no existing record" do
128
+ subject { BabylonianUniqueSecondField.new(attributes) }
129
+ it "should validate the records uniqueness" do
130
+ subject.should be_valid
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "scoped uniqueness validation" do
136
+ let(:attributes) { { marshes: {en: "Hello, I'm a string!", de: "Hallo,\nIch bin ein Faden!", fr: "Bonjour: Je suis un fil" }, some_value: true } }
137
+ let(:same_scope_attributes) { { marshes: {en: "hello, I'm a string!", de: "Hallo,\nich bin ein faden!", fr: "Bonjour: je suis un fil" }, some_value: true } }
138
+ let(:other_scope_attributes) { { marshes: {en: "hello, I'm a string!", de: "Hallo,\nich bin ein faden!", fr: "Bonjour: je suis un fil" }, some_value: false } }
139
+ context "with an existing record" do
140
+ let!(:existing) { BabylonianUniqueThirdField.create(attributes) }
141
+ subject { BabylonianUniqueThirdField.new(same_scope_attributes) }
142
+ after(:each) do
143
+ existing.destroy
144
+ end
145
+ it "should validate the records uniqueness" do
146
+ subject.should_not be_valid
147
+ subject.errors.full_messages.should == ["Marshes de has already been taken", "Marshes en has already been taken", "Marshes fr has already been taken"]
148
+ end
149
+ end
150
+ context "with an existing record in another scope" do
151
+ let!(:existing) { BabylonianUniqueThirdField.create(attributes) }
152
+ subject { BabylonianUniqueThirdField.new(other_scope_attributes) }
153
+ after(:each) do
154
+ existing.destroy
155
+ end
156
+ it "should validate the records uniqueness" do
157
+ subject.should be_valid
158
+ end
159
+ end
160
+ context "with no existing record" do
161
+ subject { BabylonianUniqueThirdField.new(attributes) }
162
+ it "should validate the records uniqueness" do
163
+ subject.should be_valid
164
+ end
165
+ end
166
+ end
167
+
168
+ end
data/spec/spec_helper.rb CHANGED
@@ -18,12 +18,13 @@ module HelperMethods
18
18
 
19
19
  end
20
20
 
21
+ require File.expand_path('./support/tables.rb', File.dirname(__FILE__))
22
+
21
23
  RSpec.configure do |configuration|
22
24
  include HelperMethods
23
25
  configuration.before(:suite) do
24
26
  ActiveRecord::Base.configurations = YAML.load_file(File.expand_path('./support/database.yml', File.dirname(__FILE__)))
25
27
  ActiveRecord::Base.establish_connection(:test)
26
- require File.expand_path('./support/tables.rb', File.dirname(__FILE__))
27
28
 
28
29
  CreateTestTables.new.up
29
30
  end
@@ -1,7 +1,8 @@
1
1
  class CreateTestTables < ActiveRecord::Migration
2
2
  def up
3
- create_table :babylonian_fields do |t|
3
+ create_table :babylonian_fields, options: 'ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin' do |t|
4
4
  t.text :marshes
5
+ t.boolean :some_value
5
6
  t.timestamps
6
7
  end
7
8
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: babylonia-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Beat Richartz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-20 00:00:00.000000000 Z
11
+ date: 2013-11-21 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: babylonia
@@ -16,14 +16,14 @@ dependencies:
16
16
  requirements:
17
17
  - - '>='
18
18
  - !ruby/object:Gem::Version
19
- version: 0.0.3
19
+ version: 0.1.1
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
- version: 0.0.3
26
+ version: 0.1.1
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: activerecord
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -71,11 +71,14 @@ files:
71
71
  - lib/babylonia/rails.rb
72
72
  - lib/babylonia/rails/class_methods.rb
73
73
  - lib/babylonia/rails/integration.rb
74
- - lib/babylonia/rails/validator.rb
74
+ - lib/babylonia/rails/validators.rb
75
+ - lib/babylonia/rails/validators/locale_validator.rb
76
+ - lib/babylonia/rails/validators/uniqueness_validator.rb
75
77
  - lib/babylonia/rails/version.rb
76
78
  - spec/babylonia-rails/integration_spec.rb
77
79
  - spec/babylonia-rails/persistence_spec.rb
78
- - spec/babylonia-rails/validator_spec.rb
80
+ - spec/babylonia-rails/validators/locale_validator_spec.rb
81
+ - spec/babylonia-rails/validators/uniqueness_validator_spec.rb
79
82
  - spec/spec_helper.rb
80
83
  - spec/support/database.yml
81
84
  - spec/support/tables.rb
@@ -106,7 +109,8 @@ summary: Let there be languages for your rails users!
106
109
  test_files:
107
110
  - spec/babylonia-rails/integration_spec.rb
108
111
  - spec/babylonia-rails/persistence_spec.rb
109
- - spec/babylonia-rails/validator_spec.rb
112
+ - spec/babylonia-rails/validators/locale_validator_spec.rb
113
+ - spec/babylonia-rails/validators/uniqueness_validator_spec.rb
110
114
  - spec/spec_helper.rb
111
115
  - spec/support/database.yml
112
116
  - spec/support/tables.rb
@@ -1,42 +0,0 @@
1
- class LanguagesValidator < ActiveModel::EachValidator
2
- def validate_each(record, attribute, value)
3
- validate_languages_present(record, attribute, options[:present].sort) if options[:present]
4
- validate_length_in_range(record, attribute, options[:length]) if options[:length].is_a?(Range)
5
- end
6
-
7
- private
8
-
9
- def validate_languages_present(record, attribute, languages)
10
- is = record_attribute_languages(record, attribute)
11
- unless is == languages
12
- record.errors[attribute] << (options[:message] || "should be filled in #{(languages - is).map(&:upcase).to_sentence}")
13
- (languages - is).each do |lang|
14
- record.errors[:"#{attribute}_#{lang}"] << (options[:message] || "should be filled")
15
- end
16
- end
17
- end
18
-
19
- def validate_length_in_range(record, attribute, range)
20
- all = record_attribute_languages(record, attribute)
21
- in_range = keys_not_matching(record, attribute){|k,v| !range.include?(v.size) }
22
- unless all.empty? || all == in_range
23
- record.errors[attribute] << (options[:message] || "should be between #{range.first} and #{range.last} characters in #{(all - in_range).map(&:upcase).to_sentence}")
24
- (all - in_range).each do |lang|
25
- record.errors[:"#{attribute}_#{lang}"] << (options[:message] || "should be between #{range.first} and #{range.last} characters")
26
- end
27
- end
28
- end
29
-
30
- def keys_not_matching record, attribute, &block
31
- not_matching = record_attribute_hash(record, attribute).dup.delete_if &block
32
- not_matching.keys.sort
33
- end
34
-
35
- def record_attribute_hash record, attribute
36
- record.send(:"#{attribute}_hash")
37
- end
38
-
39
- def record_attribute_languages record, attribute
40
- record.send(:"#{attribute}_languages").sort
41
- end
42
- end
@@ -1,76 +0,0 @@
1
- require 'spec_helper'
2
-
3
- describe LanguagesValidator do
4
-
5
- class BabylonianField < ActiveRecord::Base
6
-
7
- build_babylonian_tower_on :marshes
8
- validates :marshes, languages: { present: [:de, :en, :it], length: 5..11 }
9
-
10
- end
11
-
12
- describe "validations" do
13
- subject { BabylonianField.new(marshes: "Hello") }
14
- before(:each) do
15
- I18n.stub locale: :en, available_locales: [:de, :en, :it]
16
- end
17
- context "presence validations" do
18
- context "when invalid" do
19
- it "should indicate which languages are not translated" do
20
- subject.should_not be_valid
21
- subject.errors[:marshes].first.should == "should be filled in DE and IT"
22
- subject.errors[:marshes_en].should be_empty
23
- subject.errors[:marshes_de].first.should == "should be filled"
24
- subject.errors[:marshes_it].first.should == "should be filled"
25
- end
26
- it "should indicate remaining incomplete languages if some are updated" do
27
- subject.marshes = {it: 'SOMETHING'}
28
- subject.should_not be_valid
29
- subject.errors[:marshes].first.should == "should be filled in DE"
30
- subject.errors[:marshes_en].should be_empty
31
- subject.errors[:marshes_it].should be_empty
32
- subject.errors[:marshes_de].first.should == "should be filled"
33
- end
34
- it "should work when attributes are deleted" do
35
- subject.marshes = ''
36
- subject.should_not be_valid
37
- subject.errors[:marshes].first.should == "should be filled in DE, EN, and IT"
38
- subject.errors[:marshes_en].first.should == "should be filled"
39
- subject.errors[:marshes_de].first.should == "should be filled"
40
- subject.errors[:marshes_it].first.should == "should be filled"
41
- end
42
- end
43
- context "when valid" do
44
- before(:each) do
45
- subject.marshes = {en: 'Hello', de: 'Hello', it: 'Hello'}
46
- end
47
- it "should be valid" do
48
- subject.should be_valid
49
- end
50
- end
51
- end
52
- context "length validations" do
53
- before(:each) do
54
- subject.marshes = {en: 'H', de: 'Hello', it: 'Hlo'}
55
- end
56
- context "when invalid" do
57
- it "should indicate that the languages that are not in length" do
58
- subject.should_not be_valid
59
- subject.errors[:marshes].first.should == "should be between 5 and 11 characters in EN and IT"
60
- subject.errors[:marshes_en].first.should == "should be between 5 and 11 characters"
61
- subject.errors[:marshes_it].first.should == "should be between 5 and 11 characters"
62
- end
63
- end
64
- context "when valid" do
65
- before(:each) do
66
- subject.marshes = {en: 'HelloHelloH', de: 'Hello', it: 'Hello'}
67
- end
68
- it "should be valid" do
69
- subject.should be_valid
70
- end
71
- end
72
- end
73
- end
74
-
75
-
76
- end