snusnu-dm-is-localizable 0.0.5
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/.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 }
|