lolita-translation 0.3.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (77) hide show
  1. data/.gitignore +25 -0
  2. data/Gemfile +2 -18
  3. data/README.md +1 -2
  4. data/Rakefile +1 -49
  5. data/app/assets/javascripts/lolita/translation/application.js +4 -3
  6. data/app/assets/stylesheets/lolita/translation/application.css +7 -1
  7. data/app/views/components/lolita/translation/_assets.html.haml +4 -0
  8. data/app/views/components/lolita/translation/_language_wrap.html.haml +2 -0
  9. data/app/views/components/lolita/translation/_switch.html.haml +4 -0
  10. data/lib/lolita-translation.rb +40 -33
  11. data/lib/lolita-translation/builder/abstract_builder.rb +103 -0
  12. data/lib/lolita-translation/builder/active_record_builder.rb +106 -0
  13. data/lib/lolita-translation/builder/mongoid_builder.rb +0 -0
  14. data/lib/lolita-translation/configuration.rb +41 -166
  15. data/lib/lolita-translation/errors.rb +15 -0
  16. data/lib/lolita-translation/locale.rb +32 -0
  17. data/lib/lolita-translation/locales.rb +62 -0
  18. data/lib/lolita-translation/lolita/component_hooks.rb +49 -0
  19. data/lib/lolita-translation/lolita/tab_extension.rb +113 -0
  20. data/lib/lolita-translation/migrator.rb +56 -0
  21. data/lib/lolita-translation/migrators/active_record_migrator.rb +93 -0
  22. data/lib/lolita-translation/migrators/mongoid_migrator.rb +81 -0
  23. data/lib/lolita-translation/orm/mixin.rb +57 -0
  24. data/lib/lolita-translation/rails.rb +6 -31
  25. data/lib/lolita-translation/record.rb +220 -0
  26. data/lib/lolita-translation/translated_string.rb +11 -0
  27. data/lib/lolita-translation/translation_class_builder.rb +59 -0
  28. data/lib/lolita-translation/utils.rb +13 -0
  29. data/lib/lolita-translation/version.rb +32 -0
  30. data/lib/tasks/lolita_translation.rake +14 -0
  31. data/lolita-translation.gemspec +24 -71
  32. data/spec/ar_schema.rb +90 -0
  33. data/spec/header.rb +14 -0
  34. data/spec/integrations/active_record_integration_spec.rb +218 -0
  35. data/spec/lolita-translation/builder/abstract_builder_spec.rb +67 -0
  36. data/spec/lolita-translation/builder/active_record_builder_spec.rb +40 -0
  37. data/spec/lolita-translation/configuration_spec.rb +72 -0
  38. data/spec/lolita-translation/locale_spec.rb +25 -0
  39. data/spec/lolita-translation/locales_spec.rb +31 -0
  40. data/spec/lolita-translation/lolita/tab_extension_spec.rb +61 -0
  41. data/spec/lolita-translation/migrator_spec.rb +42 -0
  42. data/spec/lolita-translation/migrators/active_record_migrator_spec.rb +50 -0
  43. data/spec/lolita-translation/orm/mixin_spec.rb +52 -0
  44. data/spec/lolita-translation/record_spec.rb +112 -0
  45. data/spec/lolita-translation/translation_class_builder_spec.rb +62 -0
  46. data/spec/lolita_translation_spec.rb +16 -0
  47. data/spec/rails_helper.rb +6 -0
  48. data/spec/requests/record_language_switch_spec.rb +16 -0
  49. data/spec/requests/record_saving_spec.rb +63 -0
  50. data/spec/spec_helper.rb +38 -90
  51. data/spec/tasks/lolita_translation_spec.rb +32 -0
  52. data/spec/test_app/app/controllers/application_controller.rb +3 -0
  53. data/spec/test_app/app/models/category.rb +6 -0
  54. data/spec/test_app/app/models/post.rb +8 -0
  55. data/spec/test_app/config/application.rb +24 -0
  56. data/spec/test_app/config/boot.rb +11 -0
  57. data/spec/test_app/config/database.yml +3 -0
  58. data/spec/test_app/config/enviroment.rb +5 -0
  59. data/spec/test_app/config/enviroments/development.rb +44 -0
  60. data/spec/test_app/config/initializers/lolita_translation.rb +4 -0
  61. data/spec/test_app/config/initializers/token.rb +7 -0
  62. data/spec/test_app/config/routes.rb +4 -0
  63. data/spec/test_app/db/.gitkeep +0 -0
  64. data/spec/test_app/log/.gitkeep +0 -0
  65. metadata +193 -46
  66. data/.document +0 -5
  67. data/VERSION +0 -1
  68. data/app/views/components/lolita/translation/_assets.html.erb +0 -7
  69. data/app/views/components/lolita/translation/_language_wrap.html.erb +0 -4
  70. data/app/views/components/lolita/translation/_switch.html.erb +0 -8
  71. data/lib/generators/lolita_translation/USAGE +0 -8
  72. data/lib/generators/lolita_translation/has_translations_generator.rb +0 -8
  73. data/lib/lolita-translation/model.rb +0 -100
  74. data/lib/lolita-translation/modules.rb +0 -130
  75. data/lib/lolita-translation/string.rb +0 -19
  76. data/lib/tasks/has_translations_tasks.rake +0 -4
  77. data/spec/has_translations_spec.rb +0 -43
@@ -0,0 +1,67 @@
1
+ require 'header'
2
+ require File.expand_path("lib/lolita-translation/builder/abstract_builder")
3
+
4
+ describe Lolita::Translation::Builder::AbstractBuilder do
5
+ let(:klass){ Lolita::Translation::Builder::AbstractBuilder }
6
+ let(:some_class){ Class.new }
7
+
8
+ def set_class_name(klass, name = "SomeClass")
9
+ klass.singleton_class.instance_eval do
10
+ define_method(:to_s) do
11
+ name
12
+ end
13
+ end
14
+ end
15
+
16
+ it "should have @base_klass attribute" do
17
+ set_class_name(some_class)
18
+ klass.new(some_class).base_klass.should eq(some_class)
19
+ end
20
+
21
+ it "should return new class name like <ClassName>Translation for class named 'ClassName' " do
22
+ set_class_name(some_class)
23
+ klass.new(some_class).class_name.should eq("SomeClassTranslation")
24
+ end
25
+
26
+ it "should return class name like <Scoped::ClassName>Translation for class name 'Scoped::ClassName'" do
27
+ begin
28
+ Object.const_set(:Scoped,Class.new)
29
+ set_class_name(some_class,"Scoped::ClassName")
30
+ klass.new(some_class).class_name.should eq("Scoped::ClassNameTranslation")
31
+ ensure
32
+ Object.send(:remove_const, :Scoped)
33
+ end
34
+ end
35
+
36
+ it "should create class with scoped name" do
37
+ begin
38
+ Object.const_set(:Scoped,Class.new)
39
+ set_class_name(some_class,"Scoped::OtherClassName")
40
+ klass.new(some_class).klass.name.should eq("Scoped::OtherClassNameTranslation")
41
+ ensure
42
+ Object.send(:remove_const, :Scoped)
43
+ end
44
+ end
45
+
46
+ it "should show warning when method that should be implemented in concrete builder is not implemented yet" do
47
+ set_class_name(some_class)
48
+ ab_builder = klass.new(some_class)
49
+ expect{
50
+ ab_builder.build
51
+ }.not_to raise_error
52
+ ab_builder.should_receive(:implementation_warn).once
53
+ ab_builder.build
54
+ end
55
+
56
+ it "should create new class on initialization" do
57
+ set_class_name(some_class)
58
+ klass.new(some_class).klass.to_s.should eq("SomeClassTranslation")
59
+ end
60
+
61
+ it "should accept superclass as third argument for new" do
62
+ set_class_name(some_class)
63
+ superclass = Class.new
64
+ builder = klass.new(some_class,nil,superclass)
65
+ builder.klass.superclass.should eq(superclass)
66
+ end
67
+ end
@@ -0,0 +1,40 @@
1
+ require 'header'
2
+ require 'ar_schema'
3
+ require File.expand_path("lib/lolita-translation/builder/active_record_builder")
4
+ require File.expand_path("lib/lolita-translation/configuration")
5
+ ARSchema.connect!
6
+
7
+ describe Lolita::Translation::Builder::ActiveRecordBuilder do
8
+ let(:klass) { Lolita::Translation::Builder::ActiveRecordBuilder }
9
+ let(:config){ Lolita::Translation::Configuration.new(Product) }
10
+ before(:each) do
11
+ Object.send(:remove_const, :Product) if Object.const_defined?(:Product)
12
+ a_klass = Class.new(ActiveRecord::Base)
13
+ Object.const_set(:Product, a_klass)
14
+ end
15
+
16
+ after(:each) do
17
+ Object.send(:remove_const, :Product) rescue nil
18
+ end
19
+
20
+
21
+ it "should build class with ActiveRecord::Base as superclass" do
22
+ obj = klass.new(Product)
23
+ obj.build
24
+ obj.klass.superclass.should eq(ActiveRecord::Base)
25
+ end
26
+
27
+ it "should call class methods on klass" do
28
+ obj = klass.new(Product, config)
29
+ obj.stub(:association_name).and_return(:product)
30
+ obj.build
31
+ obj.klass.reflections.keys.should include(:product)
32
+ end
33
+
34
+ it "should update base class" do
35
+ obj = klass.new(Product,config)
36
+ obj.stub(:translations_association_name).and_return(:translations)
37
+ obj.build
38
+ Product.reflections.keys.should include(:translations)
39
+ end
40
+ end
@@ -0,0 +1,72 @@
1
+ require 'header'
2
+ require File.expand_path("lib/lolita-translation/configuration")
3
+ require File.expand_path("lib/lolita-translation/translation_class_builder")
4
+ require File.expand_path("lib/lolita-translation/errors")
5
+
6
+ describe Lolita::Translation::Configuration do
7
+ let(:klass){Lolita::Translation::Configuration}
8
+ let(:some_class) do
9
+ Class.new do
10
+ class << self
11
+ def table_exists?
12
+ true
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ before(:each) do
19
+ Lolita::Translation::TranslationClassBuilder.any_instance.stub(:build_class).and_return("builder")
20
+ Lolita::Translation::TranslationClassBuilder.any_instance.stub(:override_attributes).and_return("attributes")
21
+ end
22
+
23
+ it "should have @klass attribute" do
24
+ klass.new(some_class).klass.should eq(some_class)
25
+ end
26
+
27
+ it "should have @attributes attribute" do
28
+ klass.new(some_class, :name, :body).attributes.sort.should eq([:name,:body].sort)
29
+ end
30
+
31
+ it "should have @translation_class attribute" do
32
+ Object.const_set(:Product,some_class)
33
+ klass.new(some_class).translation_class.should eq("builder")
34
+ end
35
+
36
+ describe "locales" do
37
+ before(:each) do
38
+ Object.const_set(:Product,some_class)
39
+ end
40
+
41
+ after(:each) do
42
+ Lolita::Translation.send(:remove_const,:Locales) rescue nil
43
+ end
44
+ it "can be received as option" do
45
+ Lolita::Translation.const_set(:Locales, Class.new)
46
+ Lolita::Translation::Locales.should_receive(:new).with([:en,:ru]).and_return([:en,:ru])
47
+ config = klass.new(some_class, :locales => [:en, :ru])
48
+ config.locales.should == [:en,:ru]
49
+ end
50
+
51
+ it "should fall back to Lolita::Translation.locales when no locales are passed" do
52
+ config = klass.new(some_class)
53
+ Lolita::Translation.stub(:locales).and_return([:lv,:ru])
54
+ config.locales.should eq(Lolita::Translation.locales)
55
+ end
56
+
57
+ it "can be as anonymous method" do
58
+ Lolita::Translation.const_set(:Locales, Class.new)
59
+ Lolita::Translation::Locales.should_receive(:new).and_return(Proc.new{ [:lv,:ru] })
60
+ config = klass.new(some_class, :locales => Proc.new{ [:lv,:ru] })
61
+ config.locales.should eq([:lv,:ru])
62
+ end
63
+ end
64
+
65
+ it "should yield block with self when called with block" do
66
+ block_called = nil
67
+ translation_conf = klass.new(some_class) do |conf|
68
+ block_called = conf
69
+ end
70
+ block_called.should eq(translation_conf)
71
+ end
72
+ end
@@ -0,0 +1,25 @@
1
+ require 'header'
2
+ require File.expand_path("lib/lolita-translation/locale")
3
+
4
+ describe Lolita::Translation::Locale do
5
+ let(:klass){ Lolita::Translation::Locale }
6
+
7
+ it "should create new with given locale name" do
8
+ obj = klass.new(:lv)
9
+ obj.name.should eq(:lv)
10
+ obj.short_name.should eq(:lv)
11
+ end
12
+
13
+ it "should have humanized name" do
14
+ obj = klass.new(:lv)
15
+ obj.humanized_short_name.should eq("Lv")
16
+ end
17
+
18
+ it "should detect locale is active" do
19
+ obj = klass.new(:lv)
20
+ I18n.locale = :en
21
+ obj.should_not be_active
22
+ I18n.locale = :lv
23
+ obj.should be_active
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'header'
2
+ require File.expand_path("lib/lolita-translation/locales")
3
+
4
+ describe Lolita::Translation::Locales do
5
+ let(:klass){ Lolita::Translation::Locales }
6
+
7
+ it "should create new with given locales names" do
8
+ obj = klass.new([:lv,:en,:ru])
9
+ obj.locale_names.should eq([:en,:lv,:ru])
10
+ end
11
+
12
+ it "should implement Enumerable and each element should be Locale" do
13
+ obj = klass.new([:lv,:en,:ru])
14
+ obj.first.should be_kind_of(Lolita::Translation::Locale)
15
+ end
16
+
17
+ it "should return locales in order where first is locale that belongs to record" do
18
+ obj = klass.new([:lv,:en,:ru])
19
+ resource = double("resource")
20
+ transl_rec = double("transl-rec")
21
+ resource.stub(:translation_record).and_return(transl_rec)
22
+ transl_rec.stub(:default_locale).and_return(:ru)
23
+ obj.by_resource_locale(resource).map{|r| r.name}.should eq([:ru,:en,:lv])
24
+ end
25
+
26
+ it "should return active locale" do
27
+ obj = klass.new([:lv,:en,:ru])
28
+ I18n.locale = :ru
29
+ obj.active.name.should eq(:ru)
30
+ end
31
+ end
@@ -0,0 +1,61 @@
1
+ require 'spec_helper'
2
+ ARSchema.connect!
3
+
4
+ describe "Lolita tab extension" do
5
+ let(:some_class) do
6
+ Class.new do
7
+ class << self
8
+ def table_exists?
9
+ true
10
+ end
11
+ end
12
+ end
13
+ end
14
+
15
+ let(:dbi) do
16
+ tdbi = double("dbi")
17
+ tdbi.stub(:klass).and_return(some_class)
18
+ tdbi
19
+ end
20
+
21
+ it "should provide tab with #translatable? method" do
22
+ Lolita::Translation::Configuration.any_instance.stub(:build_translation_class).and_return(true)
23
+ tab = Lolita::Configuration::Tab::Base.new(dbi, :default)
24
+ tab.send(:translation_tab_extension).stub(:collect_possibly_translateble_fields).and_return([:name, :body])
25
+ tab.should_not be_translatable
26
+ some_class.class_eval do
27
+ include Lolita::Translation
28
+ translate :name, :body
29
+ end
30
+ tab.should be_translatable
31
+ end
32
+
33
+ it "should provide tab with #build_translations_nested_form with resource" do
34
+ c_class = Class.new(ActiveRecord::Base)
35
+ Object.const_set(:Product,c_class)
36
+ c_class.class_eval do
37
+ include Lolita::Configuration
38
+ include Lolita::Translation
39
+ translate :name
40
+ lolita
41
+ end
42
+ tab = Lolita::Configuration::Tab::Base.new(Lolita::DBI::Base.create(Product), :default)
43
+ category = Product.new
44
+ form = tab.build_translations_nested_form(category)
45
+ category.translations.should have(I18n.available_locales.size - 1).items
46
+ form.fields.should have(1).item
47
+ end
48
+
49
+ it "should add #original_locale field to original tab" do
50
+ c_class = Class.new(ActiveRecord::Base)
51
+ Object.const_set(:Product,c_class)
52
+ c_class.class_eval do
53
+ include Lolita::Configuration
54
+ include Lolita::Translation
55
+ translate :name
56
+ lolita
57
+ end
58
+ tab = c_class.lolita.tabs.first
59
+ tab.fields.detect{|f| f.name == :original_locale}.should_not be_nil
60
+ end
61
+ end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Lolita::Translation::Migrator do
4
+ let(:klass) { Lolita::Translation::Migrator }
5
+
6
+ before(:each) do
7
+ Object.send(:remove_const,:Comment) rescue nil
8
+ c_class = Class.new(ActiveRecord::Base)
9
+ Object.const_set(:Comment,c_class)
10
+ c_class.class_eval do
11
+ include Lolita::Translation
12
+ translate :body
13
+ end
14
+ end
15
+
16
+ after(:each) do
17
+ Object.send(:remove_const,:Comment) rescue nil
18
+ end
19
+
20
+ describe "Instance methods" do
21
+
22
+ it "should have klass and config attributes" do
23
+ migrator = klass.new(Comment)
24
+ migrator.klass.should eq(Comment)
25
+ migrator.config.should eq(Comment.translations_configuration)
26
+ end
27
+
28
+ it "should raise error when #migrate called" do
29
+ migrator = klass.new(Comment)
30
+ expect{
31
+ migrator.migrate
32
+ }.to raise_error(StandardError)
33
+ end
34
+ end
35
+
36
+ describe "Class methods" do
37
+ it "should create concrete migrator for AR" do
38
+ klass.create(Comment).should be_kind_of(Lolita::Translation::Migrators::ActiveRecordMigrator)
39
+ end
40
+ end
41
+
42
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+ require 'ar_schema'
3
+ ARSchema.connect!
4
+
5
+ describe Lolita::Translation::Migrators::ActiveRecordMigrator do
6
+ let(:klass){ Lolita::Translation::Migrators::ActiveRecordMigrator }
7
+
8
+ before(:each) do
9
+ Object.send(:remove_const,:Comment) rescue nil
10
+ c_class = Class.new(ActiveRecord::Base)
11
+ Object.const_set(:Comment,c_class)
12
+ c_class.class_eval do
13
+ include Lolita::Translation
14
+ translate :body
15
+ end
16
+ ActiveRecord::Base.connection.execute("DROP TABLE comments_translations") rescue nil
17
+ end
18
+
19
+ after(:each) do
20
+ Object.send(:remove_const,:Comment) rescue nil
21
+ end
22
+
23
+ it "should create new table for class" do
24
+ migrator = klass.new(Comment)
25
+ ActiveRecord::Base.connection.tables.should_not include("comments_translations")
26
+ migrator.migrate
27
+ ActiveRecord::Base.connection.tables.should include("comments_translations")
28
+ CommentTranslation.column_names.sort.should eq(%w(body locale comment_id id).sort)
29
+ end
30
+
31
+ it "should add new column to table when one is added to configuration" do
32
+ migrator = klass.new(Comment)
33
+ ActiveRecord::Base.connection.tables.should_not include("comments_translations")
34
+ migrator.migrate
35
+ CommentTranslation.column_names.sort.should eq(%w(body locale comment_id id).sort)
36
+ Comment.translations_configuration.attributes << :commenter
37
+ migrator.migrate
38
+ CommentTranslation.column_names.sort.should eq(%w(body locale comment_id id commenter).sort)
39
+ end
40
+
41
+ it "should remove column from table if it is removed from configuration" do
42
+ migrator = klass.new(Comment)
43
+ Comment.translations_configuration.attributes << :commenter
44
+ migrator.migrate
45
+ CommentTranslation.column_names.sort.should eq(%w(body locale comment_id id commenter).sort)
46
+ Comment.translations_configuration.attributes.pop
47
+ migrator.migrate
48
+ CommentTranslation.column_names.sort.should eq(%w(body locale comment_id id).sort)
49
+ end
50
+ end
@@ -0,0 +1,52 @@
1
+ require 'header'
2
+ require File.expand_path("lib/lolita-translation/orm/mixin")
3
+ require File.expand_path("lib/lolita-translation/errors")
4
+
5
+ describe Lolita::Translation::ORM do
6
+ let(:klass){ Class.new }
7
+
8
+ before(:each) do
9
+ klass.extend(Lolita::Translation::ORM::ClassMethods)
10
+ klass.class_eval do
11
+ include Lolita::Translation::ORM::InstanceMethods
12
+ end
13
+ end
14
+
15
+ context "ClassMethods" do
16
+
17
+ it "should provide #translate method to class" do
18
+ klass.should respond_to(:translate)
19
+ end
20
+
21
+ it "should provide #translations_configuration for class" do
22
+ klass.should respond_to(:translations_configuration)
23
+ end
24
+
25
+ it "should raise error when configuration is requested before its initialization" do
26
+ expect{
27
+ klass.translations_configuration
28
+ }.to raise_error(Lolita::Translation::ConfigurationNotInitializedError)
29
+ end
30
+
31
+ it "should provide #sync_translation_table!" do
32
+ klass.should respond_to(:sync_translation_table!)
33
+ end
34
+ end
35
+
36
+ context "InstanceMethods" do
37
+ it "should provide #translation_record to all instances of class" do
38
+ obj = klass.new
39
+ obj.should respond_to(:translation_record)
40
+ end
41
+
42
+ it "should provide #original_locale" do
43
+ obj = klass.new
44
+ obj.should respond_to(:original_locale)
45
+ end
46
+
47
+ it "should provide #original_locale=" do
48
+ obj = klass.new
49
+ obj.should respond_to(:original_locale=)
50
+ end
51
+ end
52
+ end