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.
@@ -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