traco 0.3.1 → 0.4.2

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/README.markdown CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  [![Build Status](https://secure.travis-ci.org/barsoom/traco.png)](http://travis-ci.org/barsoom/traco)
4
4
 
5
- Translatable columns for Rails 3, stored in the model table itself.
5
+ Translatable attributes for Rails 3, stored in the model table itself.
6
6
 
7
7
  Inspired by Iain Hecker's [translatable_columns](https://github.com/iain/translatable_columns/).
8
8
 
@@ -26,9 +26,9 @@ Write a migration to get database columns with locale suffixes, e.g. `title_sv`
26
26
  end
27
27
  end
28
28
 
29
- Don't create a column named `title` without a suffix, since Traco will define a method with that name.
29
+ Don't create a database column named `title` without a suffix, since Traco will define a method with that name.
30
30
 
31
- Declare these columns in the model:
31
+ Declare the attributes in the model:
32
32
 
33
33
  class Post < ActiveRecord::Base
34
34
  translates :title, :body
@@ -42,17 +42,26 @@ You can still use your accessors like `title_sv` and `title_sv=` in forms, valid
42
42
 
43
43
  `.human_attribute_name(:title_sv)`: Extends this standard method to return "Title (Swedish)" if you have a translation key `i18n.languages.sv = "Swedish"` and "Title (SV)" otherwise. Rails uses this method to build validation error messages and form labels.
44
44
 
45
- `.translatable_columns`: Returns an array like `[:title, :body]`.
45
+ `.translatable_attributes`: Returns an array like `[:title, :body]`.
46
46
 
47
- `.locales_for_column(:title)`: Returns an array like `[:sv, :en]` sorted with default locale first and then alphabetically. Suitable for looping in forms:
47
+ `.locale_columns(:title)`: Returns an array like `[:title_sv, :title_en]` sorted with default locale first and then alphabetically. Suitable for looping in forms:
48
48
 
49
- <% Post.locales_for_column(:title).each do |locale| %>
49
+ <% Post.locale_columns(:title).each do |column| %>
50
50
  <p>
51
- <%= form.label "title_#{locale}" %>
52
- <%= form.text_field "title_#{locale}" %>
51
+ <%= form.label column %>
52
+ <%= form.text_field column %>
53
53
  </p>
54
54
  <% end %>
55
55
 
56
+ Or perhaps for things like:
57
+
58
+ attr_accessible *locale_columns(:name)
59
+
60
+ validates *locale_columns(:name),
61
+ :uniqueness => true
62
+
63
+ `.locales_for_attribute(:title)`: Returns an array like `[:sv, :en]` sorted with default locale first and then alphabetically.
64
+
56
65
  And the equivalent methods for `body`, of course.
57
66
 
58
67
 
@@ -68,15 +77,24 @@ if you specify
68
77
  then `#title` will return `nil` if there is no translation in the current locale, instead of falling back to the default locale.
69
78
 
70
79
 
71
- ## Installation
80
+ ### Overriding methods
72
81
 
73
- Add this to your `Gemfile` if you use Bundler 1.1+:
82
+ Methods are defined in an included module, so you can just override them and call Traco's implementation with `super`:
83
+
84
+ class Post < ActiveRecord::Base
85
+ translates :title
86
+
87
+ def title
88
+ super.reverse
89
+ end
90
+ end
74
91
 
75
- gem 'traco', github: 'barsoom/traco'
76
92
 
77
- Or with an earlier version of Bundler:
93
+ ## Installation
94
+
95
+ Add this to your `Gemfile` if you use Bundler 1.1+:
78
96
 
79
- gem 'traco', git: 'git://github.com/barsoom/traco.git'
97
+ gem 'traco'
80
98
 
81
99
  Then run
82
100
 
@@ -1,9 +1,17 @@
1
1
  module Traco
2
2
  module ClassMethods
3
- def locales_for_column(column)
4
- column_names.grep(/\A#{column}_([a-z]{2})\z/) {
5
- $1.to_sym
6
- }.sort_by { |locale| locale_sort_value(locale) }
3
+ def locales_for_attribute(attribute)
4
+ re = /\A#{attribute}_([a-z]{2})\z/
5
+
6
+ column_names.
7
+ grep(re) { $1.to_sym }.
8
+ sort_by(&locale_sort_value)
9
+ end
10
+
11
+ def locale_columns(attribute)
12
+ locales_for_attribute(attribute).map { |locale|
13
+ :"#{attribute}_#{locale}"
14
+ }
7
15
  end
8
16
 
9
17
  def human_attribute_name(attribute, options = {})
@@ -19,18 +27,20 @@ module Traco
19
27
 
20
28
  private
21
29
 
22
- def locale_sort_value(locale)
23
- if locale == I18n.default_locale
24
- # Sort the default locale first.
25
- "0"
26
- else
27
- # Sort the rest alphabetically.
28
- locale.to_s
29
- end
30
+ def locale_sort_value
31
+ lambda { |locale|
32
+ if locale == I18n.default_locale
33
+ # Sort the default locale first.
34
+ "0"
35
+ else
36
+ # Sort the rest alphabetically.
37
+ locale.to_s
38
+ end
39
+ }
30
40
  end
31
41
 
32
- def translates?(column)
33
- translatable_columns.include?(column.to_sym)
42
+ def translates?(attribute)
43
+ translatable_attributes.include?(attribute.to_sym)
34
44
  end
35
45
 
36
46
  def locale_name(locale)
@@ -1,14 +1,14 @@
1
1
  module Traco
2
2
  class LocalizedReader
3
- def initialize(record, column, options)
3
+ def initialize(record, attribute, options)
4
4
  @record = record
5
- @column = column
5
+ @attribute = attribute
6
6
  @fallback = options[:fallback]
7
7
  end
8
8
 
9
9
  def value
10
10
  locales_to_try.each do |locale|
11
- value = @record.send("#{@column}_#{locale}")
11
+ value = @record.send("#{@attribute}_#{locale}")
12
12
  return value if value.present?
13
13
  end
14
14
 
@@ -18,7 +18,7 @@ module Traco
18
18
  private
19
19
 
20
20
  def locales_to_try
21
- @locales_to_try ||= locale_chain & locales_for_column
21
+ @locales_to_try ||= locale_chain & locales_for_attribute
22
22
  end
23
23
 
24
24
  def locale_chain
@@ -28,8 +28,8 @@ module Traco
28
28
  chain
29
29
  end
30
30
 
31
- def locales_for_column
32
- @record.class.locales_for_column(@column)
31
+ def locales_for_attribute
32
+ @record.class.locales_for_attribute(@attribute)
33
33
  end
34
34
  end
35
35
  end
@@ -1,52 +1,61 @@
1
1
  module Traco
2
2
  module Translates
3
- def translates(*columns)
4
- options = columns.extract_options!
3
+ def translates(*attributes)
4
+ options = attributes.extract_options!
5
5
  set_up_once
6
- store_as_translatable_columns columns.map(&:to_sym), options
6
+ store_as_translatable_attributes attributes.map(&:to_sym), options
7
7
  end
8
8
 
9
9
  private
10
10
 
11
- # Don't overwrite values if running multiple times in the same
12
- # class or in different classes of an inheritance chain.
11
+ # Only called once per class or inheritance chain (e.g. once
12
+ # for the superclass, not at all for subclasses). The separation
13
+ # is important if we don't want to overwrite values if running
14
+ # multiple times in the same class or in different classes of
15
+ # an inheritance chain.
13
16
  def set_up_once
14
- return if respond_to?(:translatable_columns)
17
+ return if respond_to?(:translatable_attributes)
15
18
 
16
- extend Traco::ClassMethods
19
+ class_attribute :traco_instance_methods
20
+ class_attribute :translatable_attributes
17
21
 
18
- class_attribute :translatable_columns
19
- self.translatable_columns = []
22
+ self.translatable_attributes = []
23
+ extend Traco::ClassMethods
20
24
  end
21
25
 
22
- def store_as_translatable_columns(columns, options)
26
+ def store_as_translatable_attributes(attributes, options)
23
27
  fallback = options.fetch(:fallback, true)
24
28
 
25
- self.translatable_columns |= columns
29
+ self.translatable_attributes |= attributes
30
+
31
+ # Instance methods are defined on an included module, so your class
32
+ # can just redefine them and call `super`, if you need to.
33
+ self.traco_instance_methods = Module.new
34
+ include traco_instance_methods
26
35
 
27
- columns.each do |column|
28
- define_localized_reader column, :fallback => fallback
29
- define_localized_writer column
36
+ attributes.each do |attribute|
37
+ define_localized_reader attribute, :fallback => fallback
38
+ define_localized_writer attribute
30
39
  end
31
40
  end
32
41
 
33
- def define_localized_reader(column, options)
42
+ def define_localized_reader(attribute, options)
34
43
  fallback = options[:fallback]
35
44
 
36
- define_method(column) do
37
- @localized_readers ||= {}
38
- @localized_readers[column] ||= Traco::LocalizedReader.new(
39
- self,
40
- column,
41
- :fallback => fallback
42
- )
43
- @localized_readers[column].value
45
+ traco_instance_methods.module_eval do
46
+ define_method(attribute) do
47
+ @localized_readers ||= {}
48
+ @localized_readers[attribute] ||= Traco::LocalizedReader.new(self, attribute, :fallback => fallback)
49
+ @localized_readers[attribute].value
50
+ end
44
51
  end
45
52
  end
46
53
 
47
- def define_localized_writer(column)
48
- define_method("#{column}=") do |value|
49
- send("#{column}_#{I18n.locale}=", value)
54
+ def define_localized_writer(attribute)
55
+ traco_instance_methods.module_eval do
56
+ define_method("#{attribute}=") do |value|
57
+ send("#{attribute}_#{I18n.locale}=", value)
58
+ end
50
59
  end
51
60
  end
52
61
  end
data/lib/traco/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Traco
2
- VERSION = "0.3.1"
2
+ VERSION = "0.4.2"
3
3
  end
data/spec/traco_spec.rb CHANGED
@@ -28,29 +28,42 @@ describe ActiveRecord::Base, ".translates" do
28
28
  end
29
29
  end
30
30
 
31
- describe Post, ".translatable_columns" do
31
+ describe Post, ".translatable_attributes" do
32
32
  before do
33
33
  Post.translates :title
34
34
  end
35
35
 
36
- it "should list the translatable columns" do
37
- Post.translatable_columns.should == [ :title ]
36
+ it "should list the translatable attributes" do
37
+ Post.translatable_attributes.should == [ :title ]
38
38
  end
39
39
  end
40
40
 
41
- describe Post, ".locales_for_column" do
41
+ describe Post, ".locales_for_attribute" do
42
42
  before do
43
43
  Post.translates :title
44
44
  end
45
45
 
46
46
  it "should list the locales, default first and then alphabetically" do
47
47
  I18n.default_locale = :fi
48
- Post.locales_for_column(:title).should == [
48
+ Post.locales_for_attribute(:title).should == [
49
49
  :fi, :en, :sv
50
50
  ]
51
51
  end
52
52
  end
53
53
 
54
+ describe Post, ".locale_columns" do
55
+ before do
56
+ Post.translates :title
57
+ end
58
+
59
+ it "should list the columns-with-locale for that attribute, default locale first and then alphabetically" do
60
+ I18n.default_locale = :fi
61
+ Post.locale_columns(:title).should == [
62
+ :title_fi, :title_en, :title_sv
63
+ ]
64
+ end
65
+ end
66
+
54
67
  describe Post, "#title" do
55
68
  let(:post) {
56
69
  Post.new(:title_sv => "Hej", :title_en => "Halloa", :title_fi => "Moi moi")
@@ -89,6 +102,16 @@ describe Post, "#title" do
89
102
  post.title.should be_nil
90
103
  end
91
104
 
105
+ it "should be overridable" do
106
+ class Post
107
+ def title
108
+ super.reverse
109
+ end
110
+ end
111
+
112
+ post.title.should == "jeH"
113
+ end
114
+
92
115
  # Had a regression.
93
116
  it "handles multiple columns" do
94
117
  Post.translates :title, :body
@@ -104,7 +127,7 @@ describe Post, "#title" do
104
127
  }
105
128
 
106
129
  before do
107
- Post.translates :title, fallback: false
130
+ Post.translates :title, :fallback => false
108
131
  I18n.default_locale = :en
109
132
  end
110
133
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: traco
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-29 00:00:00.000000000 Z
12
+ date: 2012-12-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
16
- requirement: &70244124230460 !ruby/object:Gem::Requirement
16
+ requirement: &70327471440700 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '3.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70244124230460
24
+ version_requirements: *70327471440700
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: sqlite3
27
- requirement: &70244124230040 !ruby/object:Gem::Requirement
27
+ requirement: &70327471440280 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: '0'
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70244124230040
35
+ version_requirements: *70327471440280
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: rake
38
- requirement: &70244124213140 !ruby/object:Gem::Requirement
38
+ requirement: &70327471423380 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: '0'
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *70244124213140
46
+ version_requirements: *70327471423380
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rspec
49
- requirement: &70244124212720 !ruby/object:Gem::Requirement
49
+ requirement: &70327471422960 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70244124212720
57
+ version_requirements: *70327471422960
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: guard
60
- requirement: &70244124212300 !ruby/object:Gem::Requirement
60
+ requirement: &70327471422540 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *70244124212300
68
+ version_requirements: *70327471422540
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: guard-rspec
71
- requirement: &70244124211880 !ruby/object:Gem::Requirement
71
+ requirement: &70327471422120 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,7 +76,7 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *70244124211880
79
+ version_requirements: *70327471422120
80
80
  description:
81
81
  email:
82
82
  - henrik@barsoom.se
@@ -115,7 +115,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
115
115
  version: '0'
116
116
  segments:
117
117
  - 0
118
- hash: 2678658822643103053
118
+ hash: -237714647746195328
119
119
  required_rubygems_version: !ruby/object:Gem::Requirement
120
120
  none: false
121
121
  requirements:
@@ -124,7 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
124
  version: '0'
125
125
  segments:
126
126
  - 0
127
- hash: 2678658822643103053
127
+ hash: -237714647746195328
128
128
  requirements: []
129
129
  rubyforge_project:
130
130
  rubygems_version: 1.8.5