mongoid-verbalize 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --color
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source :rubygems
2
+
3
+ gemspec
data/LICENSE ADDED
File without changes
data/README.md ADDED
File without changes
data/Rakefile ADDED
@@ -0,0 +1,25 @@
1
+ require 'rspec/core/rake_task'
2
+ require 'yard'
3
+
4
+ spec = Gem::Specification.load("mongoid-verbalize.gemspec")
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ task :default => :spec
9
+
10
+ YARD::Rake::YardocTask.new(:doc)
11
+
12
+ desc "Build the .gem file"
13
+ task :build do
14
+ system "gem build #{spec.name}.gemspec"
15
+ end
16
+
17
+ desc "Push the .gem file to rubygems.org"
18
+ task :release => :build do
19
+ system "gem push #{spec.name}-#{spec.version}.gem"
20
+ end
21
+
22
+ desc "Open an irb session"
23
+ task :console do
24
+ sh "irb -rubygems -I lib -r ./spec/spec_helper.rb"
25
+ end
@@ -0,0 +1,36 @@
1
+ require 'mongoid/verbalize/fields'
2
+ require 'mongoid/verbalize/translated_string'
3
+ require 'mongoid/verbalize/selector'
4
+ require 'mongoid/verbalize/verbalized_validator'
5
+ require 'mongoid/verbalize/verbalized_version'
6
+ require 'mongoid/verbalize/versioning'
7
+
8
+ module Mongoid
9
+ module Verbalize
10
+ extend ActiveSupport::Concern
11
+
12
+ included do
13
+ include Mongoid::Verbalize::Fields
14
+
15
+ Mongoid::Fields.option :use_default_if_empty do |model, field, value| end
16
+ end
17
+
18
+ module ClassMethods
19
+ def verbalized_field(name, options = {})
20
+ field(name, options.merge(:type => TranslatedString, :default => {}))
21
+ end
22
+
23
+ def validates_default_locale(names, options = {})
24
+ validates_with VerbalizedValidator, options.merge(:mode => :only_default, :attributes => names)
25
+ end
26
+
27
+ def validates_one_locale(names, options = {})
28
+ validates_with VerbalizedValidator, options.merge(:mode => :one_locale, :attributes => names)
29
+ end
30
+
31
+ def validates_all_locales(names, options = {})
32
+ validates_with VerbalizedValidator, options.merge(:mode => :all_locales, :attributes => names)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,75 @@
1
+ module Mongoid
2
+ module Verbalize
3
+ module Fields
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+
8
+ protected
9
+
10
+ # Monkey patch for Mongoid method
11
+ def create_accessors(name, meth, options = {})
12
+ # Let Mongoid do its thing
13
+ super
14
+
15
+ return unless options[:type] == Mongoid::Verbalize::TranslatedString
16
+
17
+ field = fields[name]
18
+
19
+ create_verbalized_field_getter(name, meth, field)
20
+ create_verbalized_field_setter(name, meth, field)
21
+
22
+ create_verbalized_translations_getter(name, meth, field)
23
+
24
+ create_verbalized_translations_raw_getter(name, meth)
25
+ create_verbalized_translations_raw_setter(name, meth)
26
+ end
27
+
28
+ def create_verbalized_field_getter(name, meth, field)
29
+ generated_methods.module_eval do
30
+ define_method("#{meth}") do
31
+ raw = read_attribute(name)
32
+ field_value = field.demongoize(raw)
33
+ field_value.current_locale_value(field.options[:use_default_if_empty])
34
+ end
35
+ end
36
+ end
37
+
38
+ def create_verbalized_field_setter(name, meth, field)
39
+ generated_methods.module_eval do
40
+ define_method("#{meth}=") do |value|
41
+ raw = read_attribute(name)
42
+ field_value = field.demongoize(raw)
43
+ field_value.current_locale_value = value
44
+ write_attribute(name, field_value)
45
+ end
46
+ end
47
+ end
48
+
49
+ def create_verbalized_translations_getter(name, meth, field)
50
+ generated_methods.module_eval do
51
+ define_method("#{meth}_translations") do
52
+ field.demongoize(read_attribute(name))
53
+ end
54
+ end
55
+ end
56
+
57
+ def create_verbalized_translations_raw_getter(name, meth)
58
+ generated_methods.module_eval do
59
+ define_method("#{meth}_translations_raw") do
60
+ read_attribute(name)
61
+ end
62
+ end
63
+ end
64
+
65
+ def create_verbalized_translations_raw_setter(name, meth)
66
+ generated_methods.module_eval do
67
+ define_method("#{meth}_translations_raw=") do |values|
68
+ write_attribute(name, values)
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,14 @@
1
+ module Origin
2
+ class Selector
3
+
4
+ private
5
+
6
+ def normalized_key(key, serializer)
7
+ if serializer.type == Mongoid::Verbalize::TranslatedString
8
+ "#{key}.#{::I18n.locale}.value"
9
+ else
10
+ super
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,137 @@
1
+ module Mongoid
2
+ module Verbalize
3
+ # Strongly-typed accessor for this structure:
4
+ # {
5
+ # 'en' => { "value" => "Title", "versions" => [ { "version" => 0, "value" => 'Title' } ] },
6
+ # 'es' => { "value" => "Título", "versions" => [ { "version" => 0, "value" => 'Título' } ] },
7
+ # }
8
+ class TranslatedString
9
+ class LocalizedValue
10
+ attr_accessor :current_value, :versions
11
+
12
+ def initialize(current_value=nil, versions=[])
13
+ @current_value = current_value
14
+ @versions = versions
15
+ end
16
+
17
+ def changed?
18
+ previous_version = versions.last
19
+ previous_version.nil? || previous_version.value != current_value
20
+ end
21
+
22
+ def add_version(new_version_number)
23
+ versions.push(LocalizedVersion.new(new_version_number, current_value))
24
+ end
25
+
26
+ def find_version(version)
27
+ versions.find_all { |v| v.version <= version }.last
28
+ end
29
+ end
30
+
31
+ LocalizedVersion = Struct.new(:version, :value)
32
+
33
+ attr_reader :localized_values
34
+
35
+ def initialize(localized_values)
36
+ @localized_values = localized_values
37
+ end
38
+
39
+ def find_version(language, version)
40
+ localized_value = @localized_values[language]
41
+ return unless localized_value.present?
42
+
43
+ localized_value.find_version(version)
44
+ end
45
+
46
+ # Return translated value of field, accoring to current locale.
47
+ # If :use_default_if_empty is set, then in case when there no
48
+ # translation available for current locale, if will try to
49
+ # get translation for defalt_locale.
50
+ def current_locale_value(use_default_if_empty)
51
+ lookups = [self.class.current_locale]
52
+
53
+ # TODO: Add I18n.fallbacks support instead of :use_default_if_empty
54
+ if use_default_if_empty
55
+ lookups.push(::I18n.default_locale)
56
+ end
57
+
58
+ # Find first localized value in lookup path
59
+ localized_value = @localized_values[lookups.find { |l| @localized_values[l] }]
60
+ return nil if localized_value.nil?
61
+
62
+ localized_value.current_value
63
+ end
64
+ def current_locale_value=(value)
65
+ current_value.current_value = value
66
+ end
67
+
68
+ # Called when determining whether to create a new version.
69
+ def changed?
70
+ @localized_values.values.any?(&:changed?)
71
+ end
72
+
73
+ # Creates a new version for changed values
74
+ def prepare_for_save(new_version_number)
75
+ @localized_values.values.select(&:changed?).each do |v|
76
+ v.add_version(new_version_number)
77
+ end
78
+ end
79
+
80
+ # Converts an object of this instance into a database friendly value.
81
+ def mongoize
82
+ @localized_values.each_with_object({}) do |(key, value), h|
83
+ h[key.to_s] = {
84
+ 'value' => value.current_value,
85
+ 'versions' => value.versions.map do |v|
86
+ { 'version' => v.version, 'value' => v.value }
87
+ end
88
+ }
89
+ end
90
+ end
91
+
92
+ class << self
93
+ # Get the object as it was stored in the database, and instantiate
94
+ # this custom class from it.
95
+ def demongoize(object)
96
+ localized_values = object.each_with_object({}) do |(key, value), h|
97
+ versions = (value['versions'] || []).map do |v|
98
+ LocalizedVersion.new(v['version'], v['value'])
99
+ end
100
+ h[key.to_sym] = LocalizedValue.new(value['value'], versions)
101
+ end
102
+ TranslatedString.new(localized_values)
103
+ end
104
+
105
+ # Takes any possible object and converts it to how it would be
106
+ # stored in the database.
107
+ def mongoize(object)
108
+ case object
109
+ when TranslatedString then object.mongoize
110
+ else object
111
+ end
112
+ end
113
+
114
+ # Converts the object that was supplied to a criteria and converts it
115
+ # into a database friendly form.
116
+ def evolve(object)
117
+ case object
118
+ when TranslatedString then object.mongoize.current_locale_value
119
+ else object
120
+ end
121
+ end
122
+
123
+ # Return current locale as string
124
+ def current_locale
125
+ ::I18n.locale
126
+ end
127
+ end
128
+
129
+ private
130
+
131
+ # TODO: Rename this method.
132
+ def current_value
133
+ @localized_values[self.class.current_locale] ||= LocalizedValue.new
134
+ end
135
+ end
136
+ end
137
+ end
@@ -0,0 +1,27 @@
1
+ module Mongoid
2
+ module Verbalize
3
+ class VerbalizedValidator < ActiveModel::EachValidator
4
+ def validate_each record, attribute, value
5
+ if options[:mode] == :only_default
6
+ if record.send("#{attribute}_translations_raw")[::I18n.default_locale.to_s].blank?
7
+ record.errors.add(attribute, :locale_blank, options.except(:mode).merge(
8
+ :cur_locale => ::I18n.t(:"locales.#{::I18n.default_locale}", :default => ::I18n.default_locale.to_s)
9
+ ))
10
+ end
11
+ elsif options[:mode] == :one_locale
12
+ record.errors.add(attribute, :all_locales_blank, options.except(:mode)) if record.send("#{attribute}_translations_raw").empty?
13
+ elsif options[:mode] == :all_locales
14
+ #difference between available locales and stored locales
15
+ diffloc = ::I18n.available_locales - record.send("#{attribute}_translations_raw").keys.collect { |key| key.to_sym }
16
+
17
+ #print an error for each missing locale
18
+ diffloc.each do |locale|
19
+ record.errors.add(attribute, :locale_blank, options.except(:mode).merge(
20
+ :cur_locale => ::I18n.t(:"locales.#{locale}", :default => locale.to_s)
21
+ ))
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,7 @@
1
+ module Mongoid::Verbalize
2
+ class VerbalizedVersion
3
+ include Mongoid::Document
4
+ embedded_in :versionable, :polymorphic => true
5
+ field :version, :type => Integer
6
+ end
7
+ end
@@ -0,0 +1,85 @@
1
+ module Mongoid
2
+ module Verbalize
3
+ module Versioning
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include ActiveSupport::Callbacks
8
+
9
+ define_callbacks :create_version
10
+ embeds_many :verbalized_versions, :as => :versionable,
11
+ :class_name => 'Mongoid::Verbalize::VerbalizedVersion'
12
+ before_save :create_new_version
13
+ end
14
+
15
+ module ClassMethods
16
+ def verbalized_fields(document)
17
+ document.class.fields.reject { |name, field| field.options[:type] != TranslatedString }
18
+ end
19
+
20
+ def verbalized_children(document)
21
+ document._children.reject { |child| !child.class.include?(Mongoid::Verbalize) }
22
+ end
23
+
24
+ def verbalized_field_values(document)
25
+ verbalized_fields(document).map do |name, field|
26
+ document.send("#{field.name}_translations")
27
+ end
28
+ end
29
+
30
+ def iterate_verbalized_fields(document, &block)
31
+ verbalized_fields(document).each do |name, field|
32
+ yield document, field
33
+ end
34
+ end
35
+ end
36
+
37
+ private
38
+
39
+ def create_new_version
40
+ return if embedded? # Only do this on the root document
41
+
42
+ # Do a first pass to see if any verbalized field has changed
43
+ any_field_changed = false
44
+ iterate_all_verbalized_fields do |document, field|
45
+ any_field_changed = true if field.demongoize(document.read_attribute(field.name)).changed?
46
+ end
47
+ return unless any_field_changed
48
+
49
+ run_callbacks :create_version do
50
+ # Calculate new version number
51
+ previous_version = self.verbalized_versions.last.version if self.verbalized_versions.last.present?
52
+ next_version_number = previous_version.present? ? previous_version + 1 : 0
53
+ self.verbalized_versions.build(:version => next_version_number)
54
+
55
+ # Apply this new version number to verbalized fields
56
+ iterate_all_verbalized_fields do |document, field|
57
+ field_value = field.demongoize(document.read_attribute(field.name))
58
+ field_value.prepare_for_save(next_version_number)
59
+ document.write_attribute(field.name, field_value)
60
+ end
61
+ end
62
+
63
+ # Reset _children so that Mongoid persists verbalized_versions correctly
64
+ @_children = nil
65
+ end
66
+
67
+ def all_verbalized_field_values
68
+ [self.class.verbalized_field_values(self) + self.class.verbalized_children(self).map do |child|
69
+ self.class.verbalized_field_values(child)
70
+ end].flatten
71
+ end
72
+
73
+ def iterate_all_verbalized_fields(&block)
74
+ self.class.iterate_verbalized_fields(self, &block)
75
+ self.class.verbalized_children(self).each do |child|
76
+ self.class.iterate_verbalized_fields(child, &block)
77
+ end
78
+ end
79
+
80
+ def current_version
81
+ self.verbalized_versions.last.version
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,600 @@
1
+ # encoding: utf-8
2
+ require 'spec_helper'
3
+
4
+ class Entry
5
+ include Mongoid::Document
6
+ include Mongoid::Verbalize
7
+ include Mongoid::Verbalize::Versioning
8
+
9
+ field :weight, :type => Integer, :default => 60
10
+
11
+ verbalized_field :title
12
+ verbalized_field :title_with_default, :use_default_if_empty => true
13
+ end
14
+
15
+ class EntryWithValidations
16
+ include Mongoid::Document
17
+ include Mongoid::Verbalize
18
+ include Mongoid::Verbalize::Versioning
19
+
20
+ verbalized_field :title_validated_with_default_locale
21
+ verbalized_field :title_validated_with_one_locale
22
+ verbalized_field :title_validated_with_all_locales
23
+
24
+ validates_default_locale :title_validated_with_default_locale
25
+ validates_one_locale :title_validated_with_one_locale
26
+ validates_all_locales :title_validated_with_all_locales
27
+ end
28
+
29
+ describe Mongoid::Verbalize, "verbalized_field" do
30
+ before do
31
+ I18n.locale = :en
32
+ end
33
+
34
+ describe "without an assigned value" do
35
+ before do
36
+ @entry = Entry.new
37
+ end
38
+
39
+ it "should return blank" do
40
+ @entry.title.should be_blank
41
+ end
42
+ end
43
+
44
+ describe "with an assigned value" do
45
+ before do
46
+ @entry = Entry.new(:title => 'Title')
47
+ end
48
+
49
+ it "should return that value" do
50
+ @entry.title.should == 'Title'
51
+ end
52
+
53
+ describe "and persisted" do
54
+ before do
55
+ @entry.save
56
+ end
57
+
58
+ describe "find by id" do
59
+ it "should find the document" do
60
+ Entry.find(@entry.id).should == @entry
61
+ end
62
+ end
63
+
64
+ describe "where() criteria" do
65
+ it "should use the current locale value" do
66
+ query = Entry.where(:title => 'Title')
67
+ Entry.where(:title => 'Title').first.should == @entry
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "when the locale is changed" do
73
+ before do
74
+ I18n.locale = :es
75
+ end
76
+
77
+ it "should return a blank value" do
78
+ @entry.title.should be_blank
79
+ end
80
+
81
+ describe "a new value is assigned" do
82
+ before do
83
+ @entry.title = 'Título'
84
+ end
85
+
86
+ it "should return the new value" do
87
+ @entry.title.should == 'Título'
88
+ end
89
+
90
+ describe "persisted and retrieved from db" do
91
+ before do
92
+ @entry.save
93
+ @entry.reload
94
+ end
95
+
96
+ it "the localized field value should be correct" do
97
+ @entry.title.should == 'Título'
98
+ I18n.locale = :en
99
+ @entry.title.should == 'Title'
100
+ @entry.title_translations_raw.should == {
101
+ 'en' => { "value" => "Title", "versions" => [ { "version" => 0, "value" => 'Title' } ] },
102
+ 'es' => { "value" => "Título", "versions" => [ { "version" => 0, "value" => 'Título' } ] },
103
+ }
104
+ @entry.verbalized_versions.should have(1).item
105
+ end
106
+ end
107
+
108
+ describe "field translations hash" do
109
+ context "before saving" do
110
+ it "should return all translations without versions" do
111
+ @entry.title_translations_raw.should == {
112
+ 'en' => { "value" => "Title", "versions" => [] },
113
+ 'es' => { "value" => "Título", "versions" => [] },
114
+ }
115
+ end
116
+ end
117
+
118
+ context "after saving" do
119
+ before do
120
+ @entry.save
121
+ @entry.reload
122
+ end
123
+
124
+ it "should return all translations with versions" do
125
+ @entry.title_translations_raw.should == {
126
+ 'en' => { "value" => "Title", "versions" => [ { "version" => 0, "value" => 'Title' } ] },
127
+ 'es' => { "value" => "Título", "versions" => [ { "version" => 0, "value" => 'Título' } ] },
128
+ }
129
+ end
130
+ end
131
+ end
132
+
133
+ describe "field translations" do
134
+ context "before saving" do
135
+ it "should return all translations without versions" do
136
+ @entry.title_translations.should be_instance_of(
137
+ Mongoid::Verbalize::TranslatedString)
138
+ @entry.title_translations.localized_values[:en].should be_instance_of(
139
+ Mongoid::Verbalize::TranslatedString::LocalizedValue)
140
+ @entry.title_translations.localized_values[:en].current_value.should == 'Title'
141
+ @entry.title_translations.localized_values[:en].versions.should == []
142
+ @entry.title_translations.localized_values[:es].should be_instance_of(
143
+ Mongoid::Verbalize::TranslatedString::LocalizedValue)
144
+ @entry.title_translations.localized_values[:es].current_value.should == 'Título'
145
+ @entry.title_translations.localized_values[:es].versions.should == []
146
+ end
147
+ end
148
+
149
+ context "after saving" do
150
+ before do
151
+ @entry.save
152
+ @entry.reload
153
+ end
154
+
155
+ it "should return all translations with versions" do
156
+ @entry.title_translations.localized_values[:en].should be_instance_of(
157
+ Mongoid::Verbalize::TranslatedString::LocalizedValue)
158
+ @entry.title_translations.localized_values[:en].current_value.should == 'Title'
159
+ @entry.title_translations.localized_values[:en].versions.should have(1).item
160
+ @entry.title_translations.localized_values[:en].versions[0].should be_instance_of(
161
+ Mongoid::Verbalize::TranslatedString::LocalizedVersion)
162
+ @entry.title_translations.localized_values[:en].versions[0].version.should == 0
163
+ @entry.title_translations.localized_values[:en].versions[0].value.should == 'Title'
164
+ @entry.title_translations.localized_values[:es].should be_instance_of(
165
+ Mongoid::Verbalize::TranslatedString::LocalizedValue)
166
+ @entry.title_translations.localized_values[:es].current_value.should == 'Título'
167
+ @entry.title_translations.localized_values[:es].versions.should have(1).item
168
+ @entry.title_translations.localized_values[:es].versions[0].should be_instance_of(
169
+ Mongoid::Verbalize::TranslatedString::LocalizedVersion)
170
+ @entry.title_translations.localized_values[:es].versions[0].version.should == 0
171
+ @entry.title_translations.localized_values[:es].versions[0].value.should == 'Título'
172
+ end
173
+ end
174
+ end
175
+
176
+ describe "with mass-assigned translations" do
177
+ before do
178
+ @entry.title_translations_raw = {
179
+ 'en' => { "value" => "Title", "versions" => [ { "version" => 0, "value" => 'Title' } ] },
180
+ 'es' => { "value" => "Nuevo título", "versions" => [ { "version" => 0, "value" => 'Nuevo título' } ] },
181
+ }
182
+ end
183
+
184
+ it "should set all translations" do
185
+ @entry.title_translations_raw.should == {
186
+ 'en' => { "value" => "Title", "versions" => [ { "version" => 0, "value" => 'Title' } ] },
187
+ 'es' => { "value" => "Nuevo título", "versions" => [ { "version" => 0, "value" => 'Nuevo título' } ] },
188
+ }
189
+ end
190
+
191
+ it "the getter should return the new translation" do
192
+ @entry.title.should == 'Nuevo título'
193
+ end
194
+ end
195
+
196
+ describe "if we go back to the original locale" do
197
+ before do
198
+ I18n.locale = :en
199
+ end
200
+
201
+ it "should return the original value" do
202
+ @entry.title.should == 'Title'
203
+ end
204
+ end
205
+ end
206
+ end
207
+ end
208
+ end
209
+
210
+ describe Mongoid::Verbalize do
211
+ before do
212
+ I18n.locale = :en
213
+ end
214
+
215
+ describe "versions" do
216
+ before { @entry = Entry.new }
217
+ subject { @entry }
218
+
219
+ describe "document versions" do
220
+ before do
221
+ @entry.title = 'foo'
222
+ @entry.save
223
+ end
224
+
225
+ its(:verbalized_versions) { should have(1).item }
226
+ end
227
+
228
+ describe "when field is updated twice without saving" do
229
+ before do
230
+ @entry.title = 'foo'
231
+ @entry.title = 'bar'
232
+ @expected_translations = {
233
+ 'en' => { "value" => 'bar', 'versions' => [] }
234
+ }
235
+ end
236
+
237
+ its(:title_translations_raw) { should == @expected_translations }
238
+ end
239
+
240
+ describe "when field is not updated" do
241
+ before do
242
+ @entry.title = 'foo'
243
+ @entry.title_with_default = 'bar'
244
+ I18n.locale = :es
245
+ @entry.title = 'spanishfoo'
246
+ I18n.locale = :en
247
+ @entry.save
248
+ @entry.title = 'baz'
249
+ @entry.save
250
+ @expected_title_translations = {
251
+ 'en' => {
252
+ "value" => "baz",
253
+ "versions" => [
254
+ { "version" => 0, "value" => 'foo' },
255
+ { "version" => 1, "value" => 'baz' }
256
+ ]
257
+ },
258
+ 'es' => {
259
+ "value" => "spanishfoo",
260
+ "versions" => [
261
+ { "version" => 0, "value" => 'spanishfoo' }
262
+ ]
263
+ }
264
+ }
265
+ @expected_title_with_default_translations = {
266
+ 'en' => {
267
+ "value" => "bar",
268
+ "versions" => [
269
+ { "version" => 0, "value" => 'bar' }
270
+ ]
271
+ }
272
+ }
273
+ end
274
+
275
+ its(:title_translations_raw) { should == @expected_title_translations }
276
+ its(:title_with_default_translations_raw) { should == @expected_title_with_default_translations }
277
+ end
278
+
279
+ describe "when field is updated, saved, updated, and then saved" do
280
+ before do
281
+ @entry.title = 'foo'
282
+ @entry.save
283
+ @entry.title = 'bar'
284
+ @entry.save
285
+ @expected_translations = {
286
+ 'en' => {
287
+ "value" => "bar",
288
+ "versions" => [
289
+ { "version" => 0, "value" => 'foo' },
290
+ { "version" => 1, "value" => 'bar' }
291
+ ]
292
+ }
293
+ }
294
+ end
295
+
296
+ its(:title_translations_raw) { should == @expected_translations }
297
+ end
298
+
299
+ describe "when field is updated with the same value" do
300
+ before do
301
+ @entry.title = 'foo'
302
+ @entry.save
303
+ @entry.title = 'foo'
304
+ @entry.save
305
+ @expected_translations = {
306
+ 'en' => {
307
+ "value" => "foo",
308
+ "versions" => [
309
+ { "version" => 0, "value" => 'foo' }
310
+ ]
311
+ }
312
+ }
313
+ end
314
+
315
+ its(:title_translations_raw) { should == @expected_translations }
316
+ its(:verbalized_versions) { should have(1).item }
317
+ end
318
+
319
+ describe "embedded document" do
320
+ before do
321
+ class Entry
322
+ include Mongoid::Document
323
+ include Mongoid::Verbalize
324
+ include Mongoid::Verbalize::Versioning
325
+
326
+ verbalized_field :title
327
+ embeds_many :sub_entries
328
+ end
329
+
330
+ class SubEntry
331
+ include Mongoid::Document
332
+ include Mongoid::Verbalize
333
+ verbalized_field :subtitle
334
+ embedded_in :entry
335
+ end
336
+ @entry = Entry.new(:title => 'Selfridges')
337
+ @sub_entry = @entry.sub_entries.build(:subtitle => 'Oxford Street')
338
+ @entry.save
339
+ @entry.save
340
+ @entry.reload
341
+ @sub_entry = @entry.sub_entries.first
342
+ @expected_title_translations = {
343
+ 'en' => {
344
+ "value" => "Selfridges",
345
+ "versions" => [
346
+ { "version" => 0, "value" => 'Selfridges' }
347
+ ]
348
+ }
349
+ }
350
+ @expected_subtitle_translations = {
351
+ 'en' => {
352
+ "value" => "Oxford Street",
353
+ "versions" => [
354
+ { "version" => 0, "value" => 'Oxford Street' }
355
+ ]
356
+ }
357
+ }
358
+ end
359
+
360
+ describe "entry" do
361
+ subject { @entry }
362
+ its(:verbalized_versions) { should have(1).item }
363
+ its(:title_translations_raw) { should == @expected_title_translations }
364
+ end
365
+
366
+ describe "subentry" do
367
+ subject { @sub_entry }
368
+ it { should_not respond_to(:verbalized_versions) }
369
+ its(:subtitle_translations_raw) { should == @expected_subtitle_translations }
370
+ end
371
+
372
+ describe "when subentry is updated" do
373
+ before do
374
+ @sub_entry.subtitle = 'Regent Street'
375
+ @entry.save
376
+ #@entry.reload
377
+ end
378
+
379
+ describe "entry" do
380
+ subject { @entry }
381
+ its(:verbalized_versions) { should have(2).items }
382
+ its("verbalized_versions.first.version") { should == 0 }
383
+ its("verbalized_versions.second.version") { should == 1 }
384
+ end
385
+ end
386
+ end
387
+ end
388
+ end
389
+
390
+ describe Mongoid::Verbalize, 'localized field in embedded association' do
391
+ before do
392
+ class Entry
393
+ embeds_many :sub_entries
394
+ end
395
+
396
+ class SubEntry
397
+ include Mongoid::Document
398
+ include Mongoid::Verbalize
399
+ verbalized_field :title
400
+ embedded_in :entry, :inverse_of => :sub_entries
401
+ end
402
+ @entry = Entry.new
403
+ @sub_entries = (0..2).map { @entry.sub_entries.build }
404
+ end
405
+
406
+ it "should contain the embedded documents" do
407
+ @entry.sub_entries.criteria.instance_variable_get("@documents").should == @sub_entries
408
+ end
409
+ end
410
+
411
+ describe Mongoid::Verbalize, 'localized field in embedded document' do
412
+ before do
413
+ class Entry
414
+ embeds_one :sub_entry
415
+ end
416
+
417
+ class SubEntry
418
+ include Mongoid::Document
419
+ include Mongoid::Verbalize
420
+ verbalized_field :subtitle
421
+ embedded_in :entry, :inverse_of => :sub_entries
422
+ end
423
+ @entry = Entry.new
424
+ @entry.create_sub_entry(:subtitle => 'Oxford Street')
425
+ end
426
+
427
+ it "should store the title in the right locale" do
428
+ @entry.reload.sub_entry.subtitle.should == 'Oxford Street'
429
+ end
430
+ end
431
+
432
+ describe Mongoid::Verbalize, "verbalized_field with :use_default_if_empty => true" do
433
+ before do
434
+ I18n.default_locale = :en
435
+ I18n.locale = :en
436
+ end
437
+
438
+ describe "without an assigned value" do
439
+ before do
440
+ @entry = Entry.new
441
+ end
442
+
443
+ it "should return blank" do
444
+ @entry.title_with_default.should be_blank
445
+ end
446
+ end
447
+
448
+ describe "with an assigned value in the default locale" do
449
+ before do
450
+ @entry = Entry.new(:title_with_default => 'Title with default')
451
+ end
452
+
453
+ it "should return that value with the default locale" do
454
+ @entry.title_with_default.should == 'Title with default'
455
+ end
456
+
457
+ describe "when the locale is changed" do
458
+ before do
459
+ I18n.locale = :it
460
+ end
461
+
462
+ it "should return the value of the default locale" do
463
+ @entry.title_with_default.should == 'Title with default'
464
+ end
465
+
466
+ describe "when a new value is assigned" do
467
+ before do
468
+ @entry.title_with_default = 'Titolo con default'
469
+ end
470
+
471
+ it "should return the new value" do
472
+ @entry.title_with_default.should == 'Titolo con default'
473
+ end
474
+
475
+ describe "if we go back to the original locale" do
476
+ before do
477
+ I18n.locale = :en
478
+ end
479
+
480
+ it "should return the original value" do
481
+ @entry.title_with_default.should == 'Title with default'
482
+ end
483
+ end
484
+ end
485
+ end
486
+ end
487
+ end
488
+
489
+ describe Mongoid::Verbalize, "verbalized_field with validation 'validates_default_locale'" do
490
+ before do
491
+ I18n.default_locale = :en
492
+ I18n.locale = :it
493
+ @entry = EntryWithValidations.new
494
+ end
495
+
496
+ describe "when run entry validations and default locale translation wasn't set" do
497
+ before do
498
+ @entry.title_validated_with_default_locale = "Titolo"
499
+ @entry.valid?
500
+ end
501
+
502
+ it "is added a 'locale_blank' error for that field to entry errors list" do
503
+ @entry.errors.include?(:title_validated_with_default_locale).should be_true
504
+ @entry.errors[:title_validated_with_default_locale][0].split('.').last.should == 'locale_blank'
505
+ end
506
+ end
507
+
508
+ describe "when run entry validations and default locale translation was set" do
509
+ before do
510
+ @entry.title_validated_with_default_locale_translations_raw={'en'=>'Title'}
511
+ @entry.valid?
512
+ end
513
+
514
+ it "no error for that field is added to entry errors list" do
515
+ @entry.errors.include?(:title_validated_with_default_locale).should be_false
516
+ end
517
+ end
518
+ end
519
+
520
+ describe Mongoid::Verbalize, "verbalized_field with validation 'validates_one_locale'" do
521
+ before do
522
+ I18n.default_locale = :en
523
+ I18n.locale = :it
524
+ @entry = EntryWithValidations.new
525
+ end
526
+
527
+ describe "when run entry validations and no translation was set" do
528
+ before do
529
+ @entry.valid?
530
+ end
531
+
532
+ it "is added a 'locale_blank' error for that field to entry errors list" do
533
+ @entry.errors.include?(:title_validated_with_one_locale).should be_true
534
+ @entry.errors[:title_validated_with_one_locale][0].split('.').last.should == 'all_locales_blank'
535
+ end
536
+ end
537
+
538
+ describe "when run entry validations and a locale translation was set" do
539
+ before do
540
+ @entry.title_validated_with_one_locale_translations_raw={'it'=>'Titolo'}
541
+ @entry.valid?
542
+ end
543
+
544
+ it "no error for that field is added to entry errors list" do
545
+ @entry.errors.include?(:title_validated_with_one_locale).should be_false
546
+ end
547
+ end
548
+ end
549
+
550
+ describe Mongoid::Verbalize, "verbalized_field with validation 'validates_all_locales'" do
551
+ before do
552
+ I18n.default_locale = :en
553
+ I18n.available_locales = [:en, :it, :de, :fr]
554
+ I18n.locale = :it
555
+ @entry = EntryWithValidations.new
556
+ end
557
+
558
+ describe "when run entry validations and not all translations were set" do
559
+ before do
560
+ @entry.title_validated_with_all_locales_translations_raw={'it'=>'Titolo', 'en'=>'Title'}
561
+ @entry.valid?
562
+ end
563
+
564
+ it "is added a 'locale_blank' error for that field for each missing locale" do
565
+ @entry.errors.include?(:title_validated_with_all_locales).should be_true
566
+ @entry.errors[:title_validated_with_all_locales].count.should == 2
567
+ @entry.errors[:title_validated_with_all_locales][0].split('.').last.should == 'locale_blank'
568
+ @entry.errors[:title_validated_with_all_locales][1].split('.').last.should == 'locale_blank'
569
+ end
570
+ end
571
+
572
+ describe "when run entry validations and all available locales translation were set" do
573
+ before do
574
+ @entry.title_validated_with_all_locales_translations_raw={'it'=>'Titolo', 'en'=>'Title', 'fr'=>'Titre', 'de'=>'Titel'}
575
+ @entry.valid?
576
+ end
577
+
578
+ it "no error for that field is added to entry errors list" do
579
+ @entry.errors.include?(:title_validated_with_all_locales).should be_false
580
+ end
581
+ end
582
+ end
583
+
584
+ describe Mongoid::Verbalize, "create_accessors" do
585
+ before do
586
+ I18n.locale = :en
587
+ @entry = Entry.new
588
+ end
589
+
590
+ it "should not affect other fields accessors" do
591
+ @entry.weight.should == 60
592
+
593
+ @entry.weight = 70
594
+ @entry.weight.should == 70
595
+ end
596
+
597
+ it "should not define own methods on for fields" do
598
+ @entry.should_not respond_to :weight_translations
599
+ end
600
+ end
@@ -0,0 +1,22 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'mongoid'
5
+ require 'mongoid/verbalize'
6
+
7
+ require 'rspec'
8
+
9
+ Mongoid.configure do |config|
10
+ config.connect_to('mongoid_verbalize_test')
11
+ end
12
+ # Mongoid.logger = Logger.new($stdout)
13
+ # Mongoid.logger.level = Logger::DEBUG
14
+ # Moped.logger.level = Logger::DEBUG
15
+ # Moped.logger = Logger.new($stdout)
16
+
17
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each { |f| require f }
18
+
19
+ RSpec.configure do |config|
20
+ config.mock_with :rspec
21
+ config.after(:each) { Mongoid.purge! }
22
+ end
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mongoid-verbalize
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Tim Jones
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-16 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: mongoid
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - <=
20
+ - !ruby/object:Gem::Version
21
+ version: '4.0'
22
+ - - ! '>='
23
+ - !ruby/object:Gem::Version
24
+ version: '3.0'
25
+ type: :runtime
26
+ prerelease: false
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - <=
31
+ - !ruby/object:Gem::Version
32
+ version: '4.0'
33
+ - - ! '>='
34
+ - !ruby/object:Gem::Version
35
+ version: '3.0'
36
+ - !ruby/object:Gem::Dependency
37
+ name: rake
38
+ requirement: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.2
44
+ type: :development
45
+ prerelease: false
46
+ version_requirements: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ! '>='
50
+ - !ruby/object:Gem::Version
51
+ version: 0.9.2
52
+ - !ruby/object:Gem::Dependency
53
+ name: rspec
54
+ requirement: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ~>
58
+ - !ruby/object:Gem::Version
59
+ version: 2.12.0
60
+ type: :development
61
+ prerelease: false
62
+ version_requirements: !ruby/object:Gem::Requirement
63
+ none: false
64
+ requirements:
65
+ - - ~>
66
+ - !ruby/object:Gem::Version
67
+ version: 2.12.0
68
+ - !ruby/object:Gem::Dependency
69
+ name: yard
70
+ requirement: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ~>
74
+ - !ruby/object:Gem::Version
75
+ version: '0.8'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ none: false
80
+ requirements:
81
+ - - ~>
82
+ - !ruby/object:Gem::Version
83
+ version: '0.8'
84
+ - !ruby/object:Gem::Dependency
85
+ name: debugger
86
+ requirement: !ruby/object:Gem::Requirement
87
+ none: false
88
+ requirements:
89
+ - - ! '>='
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ type: :development
93
+ prerelease: false
94
+ version_requirements: !ruby/object:Gem::Requirement
95
+ none: false
96
+ requirements:
97
+ - - ! '>='
98
+ - !ruby/object:Gem::Version
99
+ version: '0'
100
+ description: Fine-grained versioning and localization for Mongoid documents
101
+ email:
102
+ - tim@timjones.tw
103
+ executables: []
104
+ extensions: []
105
+ extra_rdoc_files: []
106
+ files:
107
+ - lib/mongoid/verbalize/fields.rb
108
+ - lib/mongoid/verbalize/selector.rb
109
+ - lib/mongoid/verbalize/translated_string.rb
110
+ - lib/mongoid/verbalize/verbalized_validator.rb
111
+ - lib/mongoid/verbalize/verbalized_version.rb
112
+ - lib/mongoid/verbalize/versioning.rb
113
+ - lib/mongoid/verbalize.rb
114
+ - spec/mongoid/verbalize_spec.rb
115
+ - spec/spec_helper.rb
116
+ - LICENSE
117
+ - README.md
118
+ - Rakefile
119
+ - Gemfile
120
+ - .rspec
121
+ homepage: https://github.com/tgjones/mongoid-verbalize
122
+ licenses:
123
+ - MIT
124
+ post_install_message:
125
+ rdoc_options: []
126
+ require_paths:
127
+ - lib
128
+ required_ruby_version: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ segments:
135
+ - 0
136
+ hash: 458357717130051605
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ segments:
144
+ - 0
145
+ hash: 458357717130051605
146
+ requirements: []
147
+ rubyforge_project:
148
+ rubygems_version: 1.8.23
149
+ signing_key:
150
+ specification_version: 3
151
+ summary: Fine-grained versioning and localization for Mongoid documents
152
+ test_files: []
153
+ has_rdoc: