mongoid-verbalize 0.0.1

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