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 +5 -0
- data/.gitignore +23 -0
- data/CHANGELOG +183 -0
- data/LICENSE +20 -0
- data/README.textile +177 -0
- data/Rakefile +77 -0
- data/TODO +2 -0
- data/VERSION +1 -0
- data/dm-is-localizable.gemspec +82 -0
- data/features/dm-is-localizable.feature +9 -0
- data/features/step_definitions/dm-is-localizable_steps.rb +0 -0
- data/features/support/env.rb +4 -0
- data/lib/dm-is-localizable.rb +8 -0
- data/lib/dm-is-localizable/is/localizable.rb +89 -0
- data/lib/dm-is-localizable/storage/language.rb +15 -0
- data/lib/dm-is-localizable/storage/translation.rb +18 -0
- data/spec/fixtures/item.rb +12 -0
- data/spec/lib/rspec_tmbundle_support.rb +35 -0
- data/spec/shared/shared_examples_spec.rb +70 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +81 -0
- data/spec/unit/auto_migrate_spec.rb +13 -0
- data/spec/unit/class_level_api_spec.rb +159 -0
- data/spec/unit/instance_level_api_spec.rb +156 -0
- data/spec/unit/is_localizable_spec.rb +28 -0
- data/spec/unit/language_spec.rb +64 -0
- data/spec/unit/translation_spec.rb +16 -0
- data/tasks/changelog.rb +18 -0
- data/tasks/whitespace.rb +4 -0
- metadata +120 -0
data/.document
ADDED
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 }
|