puret 1.0
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.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
|