snusnu-dm-is-localizable 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,23 @@
1
+ test_log
2
+ pkg
3
+ pkg/*
4
+ */pkg/*
5
+ bundle
6
+ bundle/*
7
+ doc
8
+ *.log
9
+ log
10
+ !log*.rb
11
+ */log
12
+ log/*
13
+ */log/*
14
+ coverage
15
+ */coverage
16
+ lib/dm-more.rb
17
+ *.db
18
+ nbproject
19
+ .DS_Store
20
+ rspec_report.html
21
+ *.swp
22
+ _Yardoc
23
+ */ri
data/CHANGELOG ADDED
@@ -0,0 +1,183 @@
1
+ [e66cccc | Sat May 16 19:02:07 UTC 2009] snusnu <gamsnjaga@gmail.com>
2
+
3
+ * Version bump to 0.0.4
4
+
5
+ [963611b | Sat May 16 19:00:47 UTC 2009] snusnu <gamsnjaga@gmail.com>
6
+
7
+ * README corrections to reflect the recently added uniqueness constraints
8
+
9
+ [47c5e1e | Sat May 16 18:56:09 UTC 2009] snusnu <gamsnjaga@gmail.com>
10
+
11
+ * added passing specs for unique language translations
12
+
13
+ [fd37d64 | Sat May 16 18:55:09 UTC 2009] snusnu <gamsnjaga@gmail.com>
14
+
15
+ * more comprehensive unique constraint violation checks for language
16
+
17
+ [15a9626 | Sat May 16 18:34:13 UTC 2009] snusnu <gamsnjaga@gmail.com>
18
+
19
+ * added passing spec for unique language codes
20
+
21
+ [20242b3 | Sat May 16 18:17:19 UTC 2009] snusnu <gamsnjaga@gmail.com>
22
+
23
+ * split spec files into better manageable chunks
24
+
25
+ [f4b6d30 | Sat May 16 17:32:46 UTC 2009] snusnu <gamsnjaga@gmail.com>
26
+
27
+ * moved rspec_tmbundle_support from specs/shared to specs/lib
28
+
29
+ [12fe27a | Sat May 16 17:26:30 UTC 2009] snusnu <gamsnjaga@gmail.com>
30
+
31
+ * added TODO file
32
+
33
+ [c66d793 | Sat May 16 17:22:51 UTC 2009] snusnu <gamsnjaga@gmail.com>
34
+
35
+ * added more class and instance level API.
36
+
37
+ These API methods mainly target the support for a UI that allows
38
+ clients to enter localized versions of their textual data (and
39
+ find out if they already completed their translations for specific
40
+ resources).
41
+
42
+ added class level API
43
+
44
+ * available_languages
45
+ * nr_of_available_languages
46
+ * translations_complete?
47
+
48
+ instance level API
49
+
50
+ * available_languages
51
+ * nr_of_available_languages
52
+ * translations_complete?
53
+
54
+ [cb462f4 | Sat May 16 17:13:57 UTC 2009] snusnu <gamsnjaga@gmail.com>
55
+
56
+ * added support for nice html output in rspec tmbundle.
57
+
58
+ [f713c84 | Sat May 16 05:33:54 UTC 2009] snusnu <gamsnjaga@gmail.com>
59
+
60
+ * Version bump to 0.0.3
61
+
62
+ [aa6188c | Sat May 16 05:33:44 UTC 2009] snusnu <gamsnjaga@gmail.com>
63
+
64
+ * added available_languages instance level API
65
+
66
+ [d5f13e1 | Sat May 16 05:12:03 UTC 2009] snusnu <gamsnjaga@gmail.com>
67
+
68
+ * README correction (available_languages is a class method)
69
+
70
+ [92bf341 | Sat May 16 05:06:05 UTC 2009] snusnu <gamsnjaga@gmail.com>
71
+
72
+ * Version bump to 0.0.2
73
+
74
+ [8de6a08 | Sat May 16 05:00:33 UTC 2009] snusnu <gamsnjaga@gmail.com>
75
+
76
+ * added specs for the translations and languages association.
77
+
78
+ I wonder if available_languages should be implemeted on top of
79
+ has n, :languages, :through => :translations
80
+
81
+ [dd4830f | Sat May 16 04:51:23 UTC 2009] snusnu <gamsnjaga@gmail.com>
82
+
83
+ * added available_languages class method API
84
+
85
+ [3596021 | Sat May 16 04:37:45 UTC 2009] snusnu <gamsnjaga@gmail.com>
86
+
87
+ * use transactional fixtures and have the loggers handy
88
+
89
+ [fa00b88 | Sat May 16 04:07:10 UTC 2009] snusnu <gamsnjaga@gmail.com>
90
+
91
+ * enforce unique language codes (locale strings)
92
+
93
+ [b7b8221 | Sat May 16 04:01:59 UTC 2009] snusnu <gamsnjaga@gmail.com>
94
+
95
+ * added missing spec for translation_class reader
96
+
97
+ [b0cd326 | Sat May 16 03:43:33 UTC 2009] snusnu <gamsnjaga@gmail.com>
98
+
99
+ * translation_class reader instead of translation_class_name
100
+
101
+ [d34352b | Sat May 16 03:31:54 UTC 2009] snusnu <gamsnjaga@gmail.com>
102
+
103
+ * jeweler generated gemspec
104
+
105
+ [0ddc194 | Sat May 16 03:28:33 UTC 2009] snusnu <gamsnjaga@gmail.com>
106
+
107
+ * Version bump to 0.0.1
108
+
109
+ [5abddb9 | Sat May 16 03:28:06 UTC 2009] snusnu <gamsnjaga@gmail.com>
110
+
111
+ * Version bump to 0.0.0
112
+
113
+ [25ace0a | Sat May 16 03:26:39 UTC 2009] snusnu <gamsnjaga@gmail.com>
114
+
115
+ * updated README to reflect validations for Language
116
+
117
+ [8f78083 | Sat May 16 03:24:00 UTC 2009] snusnu <gamsnjaga@gmail.com>
118
+
119
+ * validations and specs for Language.
120
+
121
+ * Think about locale string validation.
122
+ * Currently this is only a regex
123
+ * Should THIS be checked against a full list of possible locales?
124
+
125
+ [d1c0a25 | Sat May 16 03:23:01 UTC 2009] snusnu <gamsnjaga@gmail.com>
126
+
127
+ * proper namespace for module Translation
128
+
129
+ [982e081 | Sat May 16 03:21:29 UTC 2009] snusnu <gamsnjaga@gmail.com>
130
+
131
+ * added gem dependencies to gem specification and spec_helper
132
+
133
+ [8cf2113 | Sat May 16 00:57:17 UTC 2009] snusnu <gamsnjaga@gmail.com>
134
+
135
+ * README cosmetics
136
+
137
+ [8507bab | Sat May 16 00:27:42 UTC 2009] snusnu <gamsnjaga@gmail.com>
138
+
139
+ * updated README with usage examples.
140
+
141
+ [d277892 | Fri May 15 23:49:27 UTC 2009] snusnu <gamsnjaga@gmail.com>
142
+
143
+ * updated LICENSE information
144
+
145
+ [f6655c8 | Fri May 15 21:42:44 UTC 2009] snusnu <gamsnjaga@gmail.com>
146
+
147
+ * basic storage layout functional and tested. now add useful API.
148
+
149
+ * one xxx_translations table for every translatable resource
150
+ * belongs_to the resource to translate
151
+ * belongs_to a language
152
+ * properties to be translated are defined in xxx_translations
153
+
154
+ Example
155
+
156
+ class Item
157
+ include DataMapper::Resource
158
+ property :id, Serial
159
+ is :localizable do
160
+ property :name, String, :nullable => false
161
+ property :desc, String, :nullable => false
162
+ end
163
+ end
164
+
165
+ Advantages
166
+
167
+ * Proper normalization and referential integrity
168
+ * Ease in adding a new language (add row to xxx_translations)
169
+ * Easy to query
170
+ * Columns keep there names
171
+
172
+ Disadvantages (not really if you think about it)
173
+
174
+ * one table for every tables resource that needs translations
175
+
176
+ Inspired by
177
+
178
+ option 4) at http://is.gd/Af0d
179
+
180
+ [bebfb8d | Fri May 15 13:57:49 UTC 2009] snusnu <gamsnjaga@gmail.com>
181
+
182
+ * Initial commit to dm-is-localizable.
183
+
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Martin Gamsjaeger (snusnu)
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,177 @@
1
+ h1. dm-is-localizable
2
+
3
+ Datamapper support for localization of (user entered) content in multilanguage applications
4
+
5
+ h3. Schema
6
+
7
+ * one xxx_translations table for every translatable resource
8
+ * xxx_translations belongs_to the resource to translate
9
+ * xxx_translations belongs_to a language
10
+ * properties to be translated are defined in xxx_translations
11
+
12
+
13
+ h4. Advantages
14
+
15
+ * Proper normalization and referential integrity
16
+ * Ease in adding a new language (add row to xxx_translations)
17
+ * Easy to query
18
+ * Columns keep their names
19
+
20
+
21
+ h4. Disadvantages (not really if you think about it)
22
+
23
+ * One table for every resource that needs translations
24
+
25
+
26
+ h3. Example definition of a localized model
27
+
28
+ Currently, you need to define a @Language@ model yourself, to get @dm-is-localizable@ started.
29
+
30
+ <pre>
31
+ <code>
32
+ class Language
33
+
34
+ include DataMapper::Resource
35
+
36
+ # properties
37
+
38
+ property :id, Serial
39
+
40
+ property :code, String, :nullable => false, :unique => true, :unique_index => true
41
+ property :name, String, :nullable => false
42
+
43
+ # locale string like 'en-US'
44
+ validates_format :code, :with => /^[a-z]{2}-[A-Z]{2}$/
45
+
46
+ end
47
+ </code>
48
+ </pre>
49
+
50
+ Once you have this model in place, you can start defining your _localized models_.
51
+
52
+ <pre>
53
+ <code>
54
+ class Item
55
+
56
+ include DataMapper::Resource
57
+
58
+ property :id, Serial
59
+
60
+ is :localizable do
61
+ property :name, String
62
+ property :desc, String
63
+ end
64
+
65
+ end
66
+ </code>
67
+ </pre>
68
+
69
+ The above @Item@ model will define and thus be able to @DataMapper.auto_migrate!@ the @ItemTranslation@ model. The _naming convention_ used here is @"#{ClassToBeLocalized.name}Translation"@.
70
+
71
+ Preliminary support for changing this is available by using the @:class_name@ option like so: @DataMapper::Model.is :localized, :class_name => 'ItemLocalization'@. However, this currently not yet specced.
72
+
73
+ <pre>
74
+ <code>
75
+ class ItemTranslation
76
+
77
+ include DataMapper::Resource
78
+
79
+ property :id, Serial
80
+
81
+ property :item_id, Integer, :nullable => false, :unique_index => :unique_languages
82
+ property :language_id, Integer, :nullable => false, :unique_index => :unique_languages
83
+
84
+ property :name, String
85
+ property :desc, String
86
+
87
+ belongs_to :item
88
+ belongs_to :language
89
+
90
+ end
91
+ </code>
92
+ </pre>
93
+
94
+ Furthermore, the following API gets defined on the @Item@ class:
95
+
96
+ <pre>
97
+ <code>
98
+ class Item
99
+
100
+ include DataMapper::Resource
101
+
102
+ property :id, Serial
103
+
104
+ is :localizable do
105
+ property :name, String
106
+ property :desc, String
107
+ end
108
+
109
+ # ----------------------------
110
+ # added by is :localizable
111
+ # ----------------------------
112
+
113
+ has n, :item_translations
114
+ has n, :languages, :through => :item_translations
115
+
116
+ # and a handy alias
117
+ alias :translations :item_translations
118
+
119
+ # helper to get at ItemTranslation
120
+ class_inheritable_reader :translation_class
121
+
122
+ # -------------------
123
+ # class level API
124
+ # -------------------
125
+
126
+ # list all available languages for Items
127
+ def self.available_languages
128
+ Language.all :id => translation_class.all.map { |t| t.language_id }.uniq
129
+ end
130
+
131
+ # the number of all available languages for the localizable model
132
+ def self.nr_of_available_languages
133
+ available_languages.size
134
+ end
135
+
136
+ # checks if all localizable resources are translated in all available languages
137
+ def self.translations_complete?
138
+ nr_of_available_languages * all.size == translation_class.all.size
139
+ end
140
+
141
+ # ----------------------
142
+ # instance level API
143
+ # ----------------------
144
+
145
+ # list all available languages for this instance
146
+ def available_languages
147
+ Language.all :id => translations.map { |t| t.language_id }.uniq
148
+ end
149
+
150
+ # the number of all available languages for this instance
151
+ def nr_of_available_languages
152
+ available_languages.size
153
+ end
154
+
155
+ # checks if this instance is translated into all available languages for this model
156
+ def translations_complete?
157
+ self.class.nr_of_available_languages == translations.size
158
+ end
159
+
160
+
161
+ # TODO
162
+ # more API to support common usecases (and i18n/l10n solutions)
163
+
164
+ end
165
+ </code>
166
+ </pre>
167
+
168
+ h3. Inspired by (thx guys!)
169
+
170
+ * Neil Barnwell's comment on the top voted answer to "Schema for a multilanguage database":http://stackoverflow.com/questions/316780/schema-for-a-multilanguage-database
171
+
172
+ * Gabi Solomon's option (4) at this blog post on "Multilanguage database design approach":http://www.gsdesign.ro/blog/multilanguage-database-design-approach/
173
+
174
+
175
+ h3. Copyright
176
+
177
+ Copyright (c) 2009 Martin Gamsjaeger (snusnu). See "LICENSE":http://github.com/snusnu/dm-is-localizable/tree/master/LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,77 @@
1
+ require 'pathname'
2
+ require 'rake'
3
+
4
+ ROOT = Pathname(__FILE__).dirname.expand_path
5
+ JRUBY = RUBY_PLATFORM =~ /java/
6
+
7
+ begin
8
+ require 'jeweler'
9
+ Jeweler::Tasks.new do |gem|
10
+ gem.name = "dm-is-localizable"
11
+ gem.summary = %Q{Datamapper support for localization of content in multilanguage applications}
12
+ gem.email = "gamsnjaga@gmail.com"
13
+ gem.homepage = "http://github.com/snusnu/dm-is-localizable"
14
+ gem.authors = ["Martin Gamsjaeger (snusnu)"]
15
+ gem.add_dependency('dm-core', '>= 0.9.11')
16
+ gem.add_dependency('dm-is-remixable', '>= 0.9.11')
17
+ gem.add_dependency('dm-validations', '>= 0.9.11')
18
+ end
19
+
20
+ rescue LoadError
21
+ puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
22
+ end
23
+
24
+ begin
25
+ gem 'rspec', '>=1.1.12'
26
+ require 'spec'
27
+ require 'spec/rake/spectask'
28
+
29
+ task :default => [ :spec ]
30
+
31
+ desc 'Run specifications'
32
+ Spec::Rake::SpecTask.new(:spec) do |t|
33
+ t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
34
+ t.spec_files = Pathname.glob((ROOT + 'spec/**/*_spec.rb').to_s).map { |f| f.to_s }
35
+
36
+ begin
37
+ gem 'rcov', '~>0.8'
38
+ t.rcov = JRUBY ? false : (ENV.has_key?('NO_RCOV') ? ENV['NO_RCOV'] != 'true' : true)
39
+ t.rcov_opts << '--exclude' << 'spec'
40
+ t.rcov_opts << '--text-summary'
41
+ t.rcov_opts << '--sort' << 'coverage' << '--sort-reverse'
42
+ rescue LoadError
43
+ # rcov not installed
44
+ end
45
+ end
46
+ rescue LoadError
47
+ # rspec not installed
48
+ end
49
+
50
+ begin
51
+ require 'cucumber/rake/task'
52
+ Cucumber::Rake::Task.new(:features)
53
+ rescue LoadError
54
+ task :features do
55
+ abort "Cucumber is not available. In order to run features, you must: sudo gem install cucumber"
56
+ end
57
+ end
58
+
59
+ task :default => :spec
60
+
61
+ require 'rake/rdoctask'
62
+ Rake::RDocTask.new do |rdoc|
63
+ if File.exist?('VERSION.yml')
64
+ config = YAML.load(File.read('VERSION.yml'))
65
+ version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
66
+ else
67
+ version = ""
68
+ end
69
+
70
+ rdoc.rdoc_dir = 'rdoc'
71
+ rdoc.title = "dm-is-localizable #{version}"
72
+ rdoc.rdoc_files.include('README*')
73
+ rdoc.rdoc_files.include('lib/**/*.rb')
74
+ end
75
+
76
+ # require all tasks below tasks
77
+ Pathname.glob(ROOT.join('tasks/**/*.rb').to_s).each { |f| require f }