puret 1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
+
@@ -0,0 +1,3 @@
1
+ class <%= translation_model_class_name %> < ActiveRecord::Base
2
+ puret_for :<%= reference_name %>
3
+ end
@@ -0,0 +1,4 @@
1
+ require 'puret/active_record_extensions'
2
+
3
+ module Puret
4
+ end
@@ -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
@@ -0,0 +1,3 @@
1
+ module Puret
2
+ VERSION = "1.0".freeze
3
+ end
@@ -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
@@ -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