serial_translator 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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 597ebe3f2c16e2d5c2253570aa6e2bc3fb2743f4
4
+ data.tar.gz: 4f54a88c540d09761c79663f7ba20bdca089fad7
5
+ SHA512:
6
+ metadata.gz: 971fdbcbf302aa42934d7b859c1d42ea5abe848597891979249f298f51fd97e0c07ed434dba5326cfb1874b6ca1001b01b9f7de2714fdaff0b8bd68e51b96733
7
+ data.tar.gz: aea5ed5d8467ad104d25f0fcc0492724936e15c322dff460f63488bb160d4720f9fb90495461216e36dbbdb2ec1c7a1aa46ccc61b8a561ea0bb1f79ac7b9a5d2
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.ruby-gemset ADDED
@@ -0,0 +1 @@
1
+ serial_translator
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.0.0
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ rvm:
2
+ - 2.0.0
3
+
4
+ script: "bundle exec rspec"
5
+
6
+ notifications:
7
+ disabled: false
8
+ recipients:
9
+ - developers@betterplace.org
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in serial_translator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,13 @@
1
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2
+ Version 2, December 2004
3
+
4
+ Copyright (C) 2013 betterplace development team <developers@betterplace.org>
5
+
6
+ Everyone is permitted to copy and distribute verbatim or modified
7
+ copies of this license document, and changing it is allowed as long
8
+ as the name is changed.
9
+
10
+ DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11
+ TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12
+
13
+ 0. You just DO WHAT THE FUCK YOU WANT TO.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # SerialTranslator
2
+
3
+ Translate active record object attributes without the use of additional models.
4
+
5
+ ## Build status
6
+
7
+ [![Build Status](https://travis-ci.org/betterplace/serial_translator.png)](https://travis-ci.org/betterplace/serial_translator)
8
+
9
+ ## Installation
10
+
11
+ Add this line to your application's Gemfile:
12
+
13
+ gem 'serial_translator'
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ ## Usage
20
+
21
+ To be written.
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,58 @@
1
+ require 'active_support'
2
+ require 'active_model'
3
+ require 'serial_translator/version'
4
+ require 'serial_translator/serial_translator_validator'
5
+
6
+ module SerialTranslator
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ attr_writer :current_translation_locale, :translation_fallback
11
+ end
12
+
13
+ module ClassMethods
14
+ attr_reader :serial_translator_attributes
15
+
16
+ def serial_translator_for(*attributes)
17
+ @serial_translator_attributes = attributes
18
+
19
+ attributes.each do |attribute|
20
+ serialize :"#{attribute}_translations", Hash
21
+
22
+ define_method attribute do |locale = current_translation_locale|
23
+ translations = translations_for(attribute)
24
+ result = translations[locale] if translations[locale].present?
25
+ result ||= translations.values.detect(&:present?) if translation_fallback?
26
+ result
27
+ end
28
+
29
+ define_method "#{attribute}=" do |value|
30
+ translations = translations_for(attribute)
31
+ translations[current_translation_locale] = value
32
+ __send__(:"#{attribute}_translations=", translations)
33
+ end
34
+ end
35
+ end
36
+ end
37
+
38
+ def current_translation_locale
39
+ (@current_translation_locale || I18n.locale).to_sym
40
+ end
41
+
42
+ def translation_fallback?
43
+ @translation_fallback != false
44
+ end
45
+
46
+ def translated_locales
47
+ result = []
48
+ self.class.serial_translator_attributes.each do |attribute|
49
+ translations = translations_for(attribute).select { |_, value| value.present? }
50
+ result += translations.keys
51
+ end
52
+ result.uniq
53
+ end
54
+
55
+ def translations_for(attribute)
56
+ __send__(:"#{attribute}_translations") || {}
57
+ end
58
+ end
@@ -0,0 +1,48 @@
1
+ class SerialTranslator::SerialTranslatorValidator < ActiveModel::EachValidator
2
+
3
+ # SimpleForm inspects validators to find out how long
4
+ # a field may be.
5
+ def kind
6
+ options[:length].present? ? :length : :presence
7
+ end
8
+
9
+ # SimpleForm expects the options :maximum and :minimum to be present
10
+ # for length validations. This is why we inspect our range and set them
11
+ # if possible.
12
+ def initialize(options)
13
+ if length = options[:length]
14
+ length.is_a?(Range) or raise "Only range values are allowed for length"
15
+ options[:maximum] = length.max
16
+ options[:minimum] = length.min
17
+ end
18
+ super options
19
+ end
20
+
21
+ def validate_each(record, attribute, value)
22
+ options[:presence] and validate_presence(record, attribute)
23
+ options[:length] and validate_translation_lengths(record, attribute)
24
+ end
25
+
26
+ def validate_presence(record, attribute)
27
+ translations = record.__send__(:"#{attribute}_translations") || {}
28
+ translations.values.any?(&:present?) or record.errors.add_on_blank(attribute, options)
29
+ end
30
+
31
+ def validate_translation_lengths(record, attribute)
32
+ translations = record.__send__(:"#{attribute}_translations") || {}
33
+
34
+ translations.each do |locale, value|
35
+ validate_length(record, attribute, value)
36
+ end
37
+ end
38
+
39
+ def validate_length(record, attribute, value)
40
+ return if value.blank?
41
+ length = options[:length]
42
+
43
+ value_length = value.respond_to?(:length) ? value.length : value.to_s.length
44
+
45
+ value_length < length.min and record.errors.add(attribute, :too_short, count: length.min)
46
+ value_length > length.max and record.errors.add(attribute, :too_long, count: length.max)
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module SerialTranslator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'serial_translator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "serial_translator"
8
+ spec.version = SerialTranslator::VERSION
9
+ spec.authors = ["betterplace development team"]
10
+ spec.email = ["developers@betterplace.org"]
11
+ spec.description = %q{Translate attribute values without additional models}
12
+ spec.summary = %q{Translate attribute values without additional models}
13
+ spec.homepage = "http://www.betterplace.org"
14
+ spec.license = "WTFPL"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activemodel', '>= 3.0.0'
22
+ spec.add_dependency 'activeresource', '>= 3.0.0'
23
+
24
+ spec.add_development_dependency 'bundler', '~> 1.3'
25
+ spec.add_development_dependency 'rake'
26
+ spec.add_development_dependency 'rspec'
27
+ spec.add_development_dependency 'simplecov'
28
+ end
@@ -0,0 +1,86 @@
1
+ require 'spec_helper'
2
+
3
+ describe SerialTranslator::SerialTranslatorValidator do
4
+ let(:example) { FakeObject.new }
5
+
6
+ describe 'presence validation' do
7
+ it 'validates presence correctly if attribute is nil' do
8
+ example.valid?
9
+ example.should have(1).error_on(:description)
10
+ end
11
+
12
+ it 'validates presence correctly if attribute is empty' do
13
+ example.description = ''
14
+ example.should have(1).error_on(:description)
15
+ end
16
+
17
+ it 'has no error if everything is fine' do
18
+ example.description = 'This is a nice foo thing'
19
+ example.should have(0).errors_on(:description)
20
+ end
21
+
22
+ it 'is valid if any language has a value' do
23
+ example.description_translations = { en: '', de: '' }
24
+ example.should have(1).errors_on(:description)
25
+ example.description_translations = { en: '', de: 'foobar' }
26
+ example.should have(0).errors_on(:description)
27
+ example.description_translations = { en: 'foobar', de: nil }
28
+ example.should have(0).errors_on(:description)
29
+ end
30
+ end
31
+
32
+ describe 'length validation' do
33
+ it 'validates min length correctly' do
34
+ example.title = '123'
35
+ example.should have(1).error_on(:title)
36
+ end
37
+
38
+ it 'validates max length correctly' do
39
+ example.title = 'f' * 26
40
+ example.should have(1).error_on(:title)
41
+ end
42
+
43
+ it 'ignores blank fields' do
44
+ example.title = ''
45
+ example.should have(0).errors_on(:title)
46
+ end
47
+
48
+ it 'validates all translation values' do
49
+ example.title_translations = { en: '123', de: '123' }
50
+ example.should have(2).errors_on(:title)
51
+
52
+ example.title_translations = { en: '123456', de: '123' }
53
+ example.should have(1).errors_on(:title)
54
+
55
+ example.title_translations = { en: '123', de: '123456' }
56
+ example.should have(1).errors_on(:title)
57
+
58
+ example.title_translations = { en: '123456', de: '123456' }
59
+ example.should have(0).errors_on(:title)
60
+ end
61
+
62
+ it 'validates correctly on language change as well' do
63
+ I18n.locale = :de
64
+ example.description = :something_to_make_it_pass_valid
65
+ example.title = 'Valid foo'
66
+ example.should be_valid
67
+
68
+ I18n.locale = :en
69
+ example.title = ''
70
+ example.should be_valid
71
+ end
72
+ end
73
+
74
+ describe '#kind' do
75
+ it 'returns length if length validation is set' do
76
+ validator = SerialTranslator::SerialTranslatorValidator.new(attributes: :foo, length: 1..2)
77
+ validator.kind.should eq :length
78
+ end
79
+
80
+ it 'returns presence if it is no length validation' do
81
+ validator = SerialTranslator::SerialTranslatorValidator.new(attributes: :foo)
82
+ validator.kind.should eq :presence
83
+ end
84
+ end
85
+ end
86
+
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe SerialTranslator do
4
+
5
+ let(:object) { FakeObject.new }
6
+
7
+ describe 'getting a translated value' do
8
+ it 'returns the translated value in the requested locale' do
9
+ object.title_translations = { de: 'German translation', en: 'English translation' }
10
+ object.title(:de).should eq 'German translation'
11
+ end
12
+
13
+ it 'returns the origin locale value if no translation is present' do
14
+ object.title_translations = { en: 'English translation' }
15
+ object.title(:de).should eq 'English translation'
16
+ end
17
+
18
+ it 'returns the origin locale value if the translation is empty' do
19
+ object.title_translations = { de: '', en: 'English translation' }
20
+ object.title(:de).should eq 'English translation'
21
+ end
22
+
23
+ it 'returns nil if no translations are there at all' do
24
+ object.title_translations = {}
25
+ object.title(:de).should be_nil
26
+ end
27
+
28
+ it 'uses the given text locale' do
29
+ object.title_translations = { de: 'Deutsch', en: 'English' }
30
+ object.current_translation_locale = :en
31
+ object.title.should eq 'English'
32
+ end
33
+
34
+ it 'does not fail even if translations were not initialized before' do
35
+ object.title.should be_blank
36
+ end
37
+ end
38
+
39
+ describe 'writing an attribute' do
40
+ it 'sets the value for the current text locale' do
41
+ object.current_translation_locale = :de
42
+ object.title = 'Deutsch'
43
+ object.title_translations.should eq({ de: 'Deutsch' })
44
+ end
45
+ end
46
+
47
+ describe '#current_translation_locale' do
48
+ it 'returns the value that was given' do
49
+ object.current_translation_locale = 'de'
50
+ object.current_translation_locale.should eq :de
51
+ end
52
+
53
+ it 'must not return nil' do
54
+ object.current_translation_locale = nil
55
+ object.current_translation_locale.should eq I18n.locale.to_sym
56
+ end
57
+ end
58
+
59
+ describe '#translated_locales' do
60
+ it 'returns an empty array if nothing was translated' do
61
+ object.translated_locales.should eq []
62
+ end
63
+
64
+ it 'includes all locales where something is present' do
65
+ object.title_translations = { en: 'Foo', de: 'Foo' }
66
+ object.summary_translations = { nb: 'Foo' }
67
+ object.translated_locales.should eq [:en, :de, :nb]
68
+ end
69
+
70
+ it 'does not include locales where all translations are blank' do
71
+ object.title_translations = { en: 'Foo', de: '' }
72
+ object.translated_locales.should eq [:en]
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,17 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.adapters.define 'gem' do
4
+ add_filter '/spec/'
5
+ add_group 'Libraries', '/lib/'
6
+ end
7
+ SimpleCov.start 'gem'
8
+
9
+ require 'serial_translator'
10
+ require 'active_model'
11
+ require 'active_resource'
12
+ require 'support/validations_matcher'
13
+ require 'support/fake_object'
14
+
15
+ RSpec.configure do |config|
16
+ config.color_enabled = true
17
+ end
@@ -0,0 +1,12 @@
1
+ # This class is only used for testing purposes.
2
+ class FakeObject
3
+ include ActiveModel::Validations
4
+ def self.serialize(*) ; end
5
+ def self.before_save(*) ; end
6
+ attr_accessor :title_translations, :description_translations, :summary_translations
7
+
8
+ include SerialTranslator
9
+ serial_translator_for :title, :description, :summary
10
+ validates :title, serial_translator: { length: 5..25 }
11
+ validates :description, serial_translator: { presence: true }
12
+ end
@@ -0,0 +1,11 @@
1
+ # Monkey patch errors_on matcher
2
+ module ::ActiveModel::Validations
3
+ def errors_on(attribute, options = {})
4
+ valid_args = [options[:context]].compact
5
+ self.valid?(*valid_args)
6
+
7
+ [self.errors[attribute]].flatten.compact
8
+ end
9
+
10
+ alias :error_on :errors_on
11
+ end
metadata ADDED
@@ -0,0 +1,150 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: serial_translator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - betterplace development team
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2013-10-06 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 3.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '>='
25
+ - !ruby/object:Gem::Version
26
+ version: 3.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activeresource
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: 3.0.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: 3.0.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '1.3'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '1.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: simplecov
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - '>='
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - '>='
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ description: Translate attribute values without additional models
98
+ email:
99
+ - developers@betterplace.org
100
+ executables: []
101
+ extensions: []
102
+ extra_rdoc_files: []
103
+ files:
104
+ - .gitignore
105
+ - .ruby-gemset
106
+ - .ruby-version
107
+ - .travis.yml
108
+ - Gemfile
109
+ - LICENSE.txt
110
+ - README.md
111
+ - Rakefile
112
+ - lib/serial_translator.rb
113
+ - lib/serial_translator/serial_translator_validator.rb
114
+ - lib/serial_translator/version.rb
115
+ - serial_translator.gemspec
116
+ - spec/lib/serial_translator/serial_translator_validator_spec.rb
117
+ - spec/lib/serial_translator_spec.rb
118
+ - spec/spec_helper.rb
119
+ - spec/support/fake_object.rb
120
+ - spec/support/validations_matcher.rb
121
+ homepage: http://www.betterplace.org
122
+ licenses:
123
+ - WTFPL
124
+ metadata: {}
125
+ post_install_message:
126
+ rdoc_options: []
127
+ require_paths:
128
+ - lib
129
+ required_ruby_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ required_rubygems_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 2.0.3
142
+ signing_key:
143
+ specification_version: 4
144
+ summary: Translate attribute values without additional models
145
+ test_files:
146
+ - spec/lib/serial_translator/serial_translator_validator_spec.rb
147
+ - spec/lib/serial_translator_spec.rb
148
+ - spec/spec_helper.rb
149
+ - spec/support/fake_object.rb
150
+ - spec/support/validations_matcher.rb