puret 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +93 -0
- data/init.rb +1 -0
- data/lib/generators/puret.rb +63 -0
- data/lib/generators/puret/attribute/attribute_generator.rb +24 -0
- data/lib/generators/puret/attribute/templates/migration.rb +18 -0
- data/lib/generators/puret/model/model_generator.rb +29 -0
- data/lib/generators/puret/model/templates/migration.rb +20 -0
- data/lib/generators/puret/model/templates/model.rb +3 -0
- data/lib/puret.rb +4 -0
- data/lib/puret/active_record_extensions.rb +54 -0
- data/lib/puret/version.rb +3 -0
- data/test/puret_test.rb +60 -0
- data/test/test_helper.rb +37 -0
- metadata +66 -0
data/README.rdoc
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
== Puret
|
2
|
+
|
3
|
+
Puret is a minimal pure translation library for translating database values for Rails 3.
|
4
|
+
|
5
|
+
Puret is quiet like model_translations http://github.com/janne/model_translations (and borrowed much of its code), but comes with generators to help you get started and does not create the translation model dynamically but create it with generator.
|
6
|
+
|
7
|
+
|
8
|
+
== Installation
|
9
|
+
|
10
|
+
You need configure the puret gem inside your gemfile:
|
11
|
+
|
12
|
+
gem 'puret'
|
13
|
+
|
14
|
+
You can also use the latest edge version by specifying the git repository:
|
15
|
+
|
16
|
+
gem 'puret', :git => 'git://github.com/jo/puret.git'
|
17
|
+
|
18
|
+
Do not forget to run
|
19
|
+
|
20
|
+
bundle install
|
21
|
+
|
22
|
+
|
23
|
+
== Basic Usage
|
24
|
+
|
25
|
+
This is a walkthrough with all steps you need to setup puret translated attributes, including model and migration. You MUST also check out the *Generators* section below to help you start.
|
26
|
+
|
27
|
+
We're assuming here you want a Post model with some puret attributes, as outlined below:
|
28
|
+
|
29
|
+
class Post < ActiveRecord::Base
|
30
|
+
puret :title, :description
|
31
|
+
end
|
32
|
+
|
33
|
+
|
34
|
+
The pure translations are stored in a different translation model for every model you need translations for:
|
35
|
+
|
36
|
+
class PostTranslation < ActiveRecord::Base
|
37
|
+
puret_for :post
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
You now need to create a migration for the translations table:
|
42
|
+
|
43
|
+
create_table(:post_translations) do |t|
|
44
|
+
t.references :post
|
45
|
+
t.string :locale, :limit => 2
|
46
|
+
|
47
|
+
t.string :title
|
48
|
+
t.text :description
|
49
|
+
|
50
|
+
t.timestamps
|
51
|
+
end
|
52
|
+
add_index :post_translations, [:post_id, :locale], :unique => true
|
53
|
+
|
54
|
+
Thats it!
|
55
|
+
|
56
|
+
Now you are able to translate values for the attributes :title and :description per locale:
|
57
|
+
|
58
|
+
I18n.locale = :en
|
59
|
+
post.title = 'Puret really rocks!'
|
60
|
+
post.title #=> Puret really rocks!
|
61
|
+
I18n.locale = :de
|
62
|
+
post.title = 'Puret rockt wirklich!'
|
63
|
+
post.title #=> Puret rockt wirklich!
|
64
|
+
I18n.locale = :en
|
65
|
+
post.title #=> Puret really rocks!
|
66
|
+
|
67
|
+
|
68
|
+
== Generators
|
69
|
+
|
70
|
+
Puret comes with some generators to help you with your daily job:
|
71
|
+
|
72
|
+
rails generate puret:model Post title:string description:text
|
73
|
+
|
74
|
+
will setup all the code above and more, either you already have a Post model or not. In the latter case the Post model will be created for you.
|
75
|
+
|
76
|
+
In case you already have a translated model and want to add some more puret attributes, just run the puret:attribute generator:
|
77
|
+
|
78
|
+
rails generate puret:attribute Post body:text
|
79
|
+
|
80
|
+
This will create the appropriate migration and configure your Post model to translate the new attribute body.
|
81
|
+
|
82
|
+
Keep it simple! Relax.
|
83
|
+
|
84
|
+
|
85
|
+
== Bugs and Feedback
|
86
|
+
|
87
|
+
If you discover any bugs or want to drop a line, feel free to create an issue on
|
88
|
+
GitHub:
|
89
|
+
|
90
|
+
http://github.com/jo/puret/issues
|
91
|
+
|
92
|
+
|
93
|
+
Copyright (c) 2010 Johannes Jörg Schmidt, TF, released under the MIT license
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'puret'
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'rails/generators/named_base'
|
2
|
+
require 'rails/generators/active_model'
|
3
|
+
require 'rails/generators/migration'
|
4
|
+
|
5
|
+
module Puret
|
6
|
+
module Generators
|
7
|
+
class Base < Rails::Generators::NamedBase #:nodoc:
|
8
|
+
include Rails::Generators::Migration
|
9
|
+
|
10
|
+
def self.source_root
|
11
|
+
@_puret_source_root ||= begin
|
12
|
+
if base_name && generator_name
|
13
|
+
File.expand_path(File.join(base_name, generator_name, 'templates'), File.dirname(__FILE__))
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.next_migration_number(path)
|
19
|
+
Time.now.utc.strftime("%Y%m%d%H%M%S")
|
20
|
+
end
|
21
|
+
|
22
|
+
# create model unless exists
|
23
|
+
def invoke_model
|
24
|
+
invoke "model", [name] unless File.exists?(File.join(destination_root, model_path))
|
25
|
+
end
|
26
|
+
|
27
|
+
protected
|
28
|
+
|
29
|
+
# eg app/models/post.rb
|
30
|
+
def model_path
|
31
|
+
@model_path ||= File.join("app", "models", "#{file_path}.rb")
|
32
|
+
end
|
33
|
+
|
34
|
+
# eg PostTranslation
|
35
|
+
def translation_model_class_name
|
36
|
+
@translation_model_class_name ||= "#{class_name}Translation"
|
37
|
+
end
|
38
|
+
|
39
|
+
# eg app/models/post_translations.rb
|
40
|
+
def translation_model_path
|
41
|
+
@translation_model_path ||= File.join("app", "models", "#{file_path}_translation.rb")
|
42
|
+
end
|
43
|
+
|
44
|
+
def translation_model_exists?
|
45
|
+
File.exists?(File.join(destination_root, translation_model_path))
|
46
|
+
end
|
47
|
+
|
48
|
+
# eg post_translations
|
49
|
+
def translations_table_name
|
50
|
+
translation_model_class_name.tableize
|
51
|
+
end
|
52
|
+
|
53
|
+
def reference_name
|
54
|
+
name.underscore
|
55
|
+
end
|
56
|
+
|
57
|
+
def reference_id
|
58
|
+
"#{reference_name}_id"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'generators/puret'
|
2
|
+
|
3
|
+
module Puret
|
4
|
+
module Generators
|
5
|
+
class AttributeGenerator < Base
|
6
|
+
desc "Generates a translation attribute for the given model NAME (or create it if one does not exist) with Puret configuration plus a migration file."
|
7
|
+
|
8
|
+
argument :attributes, :type => :array, :banner => "field:type field:type"
|
9
|
+
|
10
|
+
# configure puret attributes in model
|
11
|
+
def inject_puret_config_into_model
|
12
|
+
inject_into_class model_path, class_name, <<-CONTENT
|
13
|
+
puret #{attributes.map { |a| ":%s" % a.name }.join(", ")}
|
14
|
+
CONTENT
|
15
|
+
end
|
16
|
+
|
17
|
+
# create migration
|
18
|
+
def copy_attribute_migration_template
|
19
|
+
migration_template "migration.rb", "db/migrate/add_#{translations_table_name}"
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
class Add<%= translations_table_name.camelize %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
change_table(:<%= translations_table_name %>) do |t|
|
4
|
+
<% attributes.each do |attribute| -%>
|
5
|
+
t.<%= attribute.type %> :<%= attribute.name %>
|
6
|
+
<% end -%>
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.down
|
11
|
+
change_table(:<%= translations_table_name %>) do |t|
|
12
|
+
<% attributes.each do |attribute| -%>
|
13
|
+
t.remove :<%= attribute.name %>
|
14
|
+
<% end -%>
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'generators/puret'
|
2
|
+
|
3
|
+
module Puret
|
4
|
+
module Generators
|
5
|
+
class ModelGenerator < Base
|
6
|
+
desc "Generates a translation model for the given model NAME (or create it if one does not exist) with Puret configuration plus a migration file."
|
7
|
+
|
8
|
+
argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
|
9
|
+
|
10
|
+
# create model
|
11
|
+
def copy_model_template
|
12
|
+
template "model.rb", translation_model_path
|
13
|
+
end
|
14
|
+
|
15
|
+
# configure puret attributes in model
|
16
|
+
def inject_puret_config_into_model
|
17
|
+
inject_into_class model_path, class_name, <<-CONTENT
|
18
|
+
puret #{attributes.map { |a| ":%s" % a.name }.join(", ")}
|
19
|
+
CONTENT
|
20
|
+
end
|
21
|
+
|
22
|
+
# create migration
|
23
|
+
def copy_migration_template
|
24
|
+
migration_template "migration.rb", "db/migrate/create_#{translations_table_name}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class Create<%= translations_table_name.camelize %> < ActiveRecord::Migration
|
2
|
+
def self.up
|
3
|
+
create_table(:<%= translations_table_name %>) do |t|
|
4
|
+
t.references :<%= reference_name %>
|
5
|
+
t.string :locale, :limit => 2
|
6
|
+
|
7
|
+
<% attributes.each do |attribute| -%>
|
8
|
+
t.<%= attribute.type %> :<%= attribute.name %>
|
9
|
+
<% end -%>
|
10
|
+
|
11
|
+
t.timestamps
|
12
|
+
end
|
13
|
+
add_index :<%= translations_table_name %>, [:<%= reference_id %>, :locale], :unique => true
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.down
|
17
|
+
drop_table :<%= translations_table_name %>
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
data/lib/puret.rb
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
module Puret
|
2
|
+
module ActiveRecordExtensions
|
3
|
+
module ClassMethods
|
4
|
+
def puret_for(model)
|
5
|
+
belongs_to model
|
6
|
+
end
|
7
|
+
|
8
|
+
def puret(*attributes)
|
9
|
+
make_it_puret! unless included_modules.include?(InstanceMethods)
|
10
|
+
|
11
|
+
attributes.each do |attribute|
|
12
|
+
define_method "#{attribute}=" do |value|
|
13
|
+
puret_attributes[attribute] = value
|
14
|
+
end
|
15
|
+
|
16
|
+
define_method attribute do
|
17
|
+
return puret_attributes[attribute] if puret_attributes[attribute]
|
18
|
+
return if new_record?
|
19
|
+
|
20
|
+
translation = translations.detect { |t| t.locale.to_sym == I18n.locale } ||
|
21
|
+
translations.detect { |t| t.locale.to_sym == I18n.default_locale } ||
|
22
|
+
translations.first
|
23
|
+
|
24
|
+
translation ? translation[attribute] : nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def make_it_puret!
|
32
|
+
include InstanceMethods
|
33
|
+
|
34
|
+
has_many :translations, :class_name => "#{self.to_s}Translation", :dependent => :destroy
|
35
|
+
after_save :update_translations!
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module InstanceMethods
|
40
|
+
def puret_attributes
|
41
|
+
@puret_attributes ||= {}
|
42
|
+
end
|
43
|
+
|
44
|
+
def update_translations!
|
45
|
+
return if puret_attributes.blank?
|
46
|
+
translation = translations.find_or_initialize_by_locale(I18n.locale.to_s)
|
47
|
+
translation.attributes = translation.attributes.merge(puret_attributes)
|
48
|
+
translation.save!
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
ActiveRecord::Base.extend Puret::ActiveRecordExtensions::ClassMethods
|
data/test/puret_test.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class PuretTest < ActiveSupport::TestCase
|
4
|
+
def setup
|
5
|
+
setup_db
|
6
|
+
I18n.locale = I18n.default_locale = :en
|
7
|
+
Post.create(:title => 'English title')
|
8
|
+
end
|
9
|
+
|
10
|
+
def teardown
|
11
|
+
teardown_db
|
12
|
+
end
|
13
|
+
|
14
|
+
test "database setup" do
|
15
|
+
assert Post.count == 1
|
16
|
+
end
|
17
|
+
|
18
|
+
test "allow translation" do
|
19
|
+
I18n.locale = :de
|
20
|
+
Post.first.update_attribute :title, 'Deutscher Titel'
|
21
|
+
assert_equal 'Deutscher Titel', Post.first.title
|
22
|
+
I18n.locale = :en
|
23
|
+
assert_equal 'English title', Post.first.title
|
24
|
+
end
|
25
|
+
|
26
|
+
test "assert fallback to default" do
|
27
|
+
assert Post.first.title == 'English title'
|
28
|
+
I18n.locale = :de
|
29
|
+
assert Post.first.title == 'English title'
|
30
|
+
end
|
31
|
+
|
32
|
+
test "post has_many translations" do
|
33
|
+
assert_equal PostTranslation, Post.first.translations.first.class
|
34
|
+
end
|
35
|
+
|
36
|
+
test "translations are deleted when parent is destroyed" do
|
37
|
+
I18n.locale = :de
|
38
|
+
Post.first.update_attribute :title, 'Deutscher Titel'
|
39
|
+
assert_equal 2, PostTranslation.count
|
40
|
+
|
41
|
+
Post.destroy_all
|
42
|
+
assert_equal 0, PostTranslation.count
|
43
|
+
end
|
44
|
+
|
45
|
+
test 'validates_presence_of should work' do
|
46
|
+
post = Post.new
|
47
|
+
assert_equal false, post.valid?
|
48
|
+
|
49
|
+
post.title = 'English title'
|
50
|
+
assert_equal true, post.valid?
|
51
|
+
end
|
52
|
+
|
53
|
+
test 'temporary locale switch should not clear changes' do
|
54
|
+
I18n.locale = :de
|
55
|
+
post = Post.first
|
56
|
+
post.text = 'Deutscher Text'
|
57
|
+
post.title.blank?
|
58
|
+
assert_equal 'Deutscher Text', post.text
|
59
|
+
end
|
60
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'active_support'
|
4
|
+
require "active_record"
|
5
|
+
require 'puret'
|
6
|
+
|
7
|
+
ActiveRecord::Base.establish_connection(:adapter => "sqlite3", :database => ":memory:")
|
8
|
+
|
9
|
+
def setup_db
|
10
|
+
ActiveRecord::Schema.define(:version => 1) do
|
11
|
+
create_table :posts do |t|
|
12
|
+
t.timestamps
|
13
|
+
end
|
14
|
+
|
15
|
+
create_table :post_translations do |t|
|
16
|
+
t.references :post
|
17
|
+
t.string :locale
|
18
|
+
t.string :title
|
19
|
+
t.timestamps
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def teardown_db
|
25
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
26
|
+
ActiveRecord::Base.connection.drop_table(table)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class Post < ActiveRecord::Base
|
31
|
+
puret :title, :text
|
32
|
+
validates_presence_of :title
|
33
|
+
end
|
34
|
+
|
35
|
+
class PostTranslation < ActiveRecord::Base
|
36
|
+
puret_for :post
|
37
|
+
end
|
metadata
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: puret
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "1.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Johannes J\xC3\xB6rg Schmidt"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-02-22 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Pure model translations
|
17
|
+
email: schmidt@netzmerk.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- init.rb
|
26
|
+
- lib/generators/puret.rb
|
27
|
+
- lib/generators/puret/attribute/attribute_generator.rb
|
28
|
+
- lib/generators/puret/attribute/templates/migration.rb
|
29
|
+
- lib/generators/puret/model/model_generator.rb
|
30
|
+
- lib/generators/puret/model/templates/migration.rb
|
31
|
+
- lib/generators/puret/model/templates/model.rb
|
32
|
+
- lib/puret.rb
|
33
|
+
- lib/puret/active_record_extensions.rb
|
34
|
+
- lib/puret/version.rb
|
35
|
+
- README.rdoc
|
36
|
+
has_rdoc: true
|
37
|
+
homepage: http://github.com/jo/puret
|
38
|
+
licenses: []
|
39
|
+
|
40
|
+
post_install_message:
|
41
|
+
rdoc_options:
|
42
|
+
- --charset=UTF-8
|
43
|
+
require_paths:
|
44
|
+
- lib
|
45
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
requirements: []
|
58
|
+
|
59
|
+
rubyforge_project:
|
60
|
+
rubygems_version: 1.3.5
|
61
|
+
signing_key:
|
62
|
+
specification_version: 3
|
63
|
+
summary: Pure model translations
|
64
|
+
test_files:
|
65
|
+
- test/puret_test.rb
|
66
|
+
- test/test_helper.rb
|