arcopy 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/COPYING +18 -0
- data/HACKING +61 -0
- data/README.md +285 -0
- data/Rakefile +16 -0
- data/bin/replicate +100 -0
- data/lib/replicate.rb +24 -0
- data/lib/replicate/active_record.rb +347 -0
- data/lib/replicate/dumper.rb +142 -0
- data/lib/replicate/emitter.rb +54 -0
- data/lib/replicate/loader.rb +157 -0
- data/lib/replicate/object.rb +57 -0
- data/lib/replicate/status.rb +54 -0
- data/test/active_record_test.rb +589 -0
- data/test/dumper_test.rb +108 -0
- data/test/dumpscript.rb +1 -0
- data/test/linked_dumpscript.rb +1 -0
- data/test/loader_test.rb +93 -0
- data/test/replicate_test.rb +10 -0
- metadata +135 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ba25896eb10e1027cf4021ea88ba0fb5517c630907d442bfc5b27ed861f41677
|
4
|
+
data.tar.gz: d035b464d641f563962a1b3040e9fe894c848a0625e458f0e6385c92ffa78a4d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 7f784501bd721c2be2ccfe16c740f84da92a43edd2b0a02ffe356de940e8db4a0906d701dafc643e1ee5e9d571e10916f46e3f5e2bedb595df4e63e44acfa695
|
7
|
+
data.tar.gz: d856708d946dacd3366015d7d4d11bd3e7aa2e87562b97367b2fd67627e5ccb551b17c719e4b3a526cc8f2f646d0d9c9f6699c547d31eaec2f94438c4f778e25
|
data/COPYING
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2011 Ryan Tomayko <http://tomayko.com/about>
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to
|
5
|
+
deal in the Software without restriction, including without limitation the
|
6
|
+
rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
|
7
|
+
sell copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in
|
11
|
+
all copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
+
THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/HACKING
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
Grab a local clone of rtomayko/replicate:
|
2
|
+
|
3
|
+
git clone git://github.com/rtomayko/replicate.git
|
4
|
+
cd replicate
|
5
|
+
|
6
|
+
The default rake task installs the latest supported activerecord
|
7
|
+
version to a project local GEM_HOME=./vendor and runs the unit
|
8
|
+
tests:
|
9
|
+
|
10
|
+
$ rake
|
11
|
+
installing activerecord-3.1.0 to ./vendor/1.8
|
12
|
+
installing sqlite3 to ./vendor/1.8
|
13
|
+
Using activerecord 3.1.0
|
14
|
+
Loaded suite ...
|
15
|
+
Started
|
16
|
+
....................
|
17
|
+
Finished in 0.150186 seconds.
|
18
|
+
|
19
|
+
20 tests, 106 assertions, 0 failures, 0 errors
|
20
|
+
|
21
|
+
Use `rake test:all' to run tests under all activerecord versions:
|
22
|
+
|
23
|
+
$ rake test:all
|
24
|
+
installing activerecord ~> 2.2.3 to ./vendor
|
25
|
+
installing activerecord ~> 2.3.14 to ./vendor
|
26
|
+
installing activerecord ~> 3.0.10 to ./vendor
|
27
|
+
installing activerecord ~> 3.1.0 to ./vendor
|
28
|
+
==> testing activerecord ~> 2.2.3
|
29
|
+
Started
|
30
|
+
....................
|
31
|
+
Finished in 0.119517 seconds.
|
32
|
+
|
33
|
+
20 tests, 106 assertions, 0 failures, 0 errors
|
34
|
+
==> testing activerecord ~> 2.3.14
|
35
|
+
Started
|
36
|
+
....................
|
37
|
+
Finished in 0.119517 seconds.
|
38
|
+
|
39
|
+
20 tests, 106 assertions, 0 failures, 0 errors
|
40
|
+
<snip>
|
41
|
+
|
42
|
+
rake test:all should always be passing under latest stable MRI
|
43
|
+
1.8.7 and MRI 1.9.x.
|
44
|
+
|
45
|
+
Running individual test files directly requires setting the
|
46
|
+
GEM_HOME environment variable and ensuring ./lib is on the load
|
47
|
+
path:
|
48
|
+
|
49
|
+
export GEM_HOME=vendor/1.9 # or 1.8.7
|
50
|
+
ruby -Ilib test/active_record_test.rb
|
51
|
+
|
52
|
+
You can also control which activerecord version is used in the
|
53
|
+
test with the AR_VERSION environment variable:
|
54
|
+
|
55
|
+
rake setup:all
|
56
|
+
export GEM_HOME=vendor/1.8.7
|
57
|
+
AR_VERSION=3.1.0 ruby -rubygems -Ilib test/active_record_test.rb
|
58
|
+
|
59
|
+
If you have something worth sharing, please send a pull request:
|
60
|
+
|
61
|
+
https://github.com/rtomayko/replicate
|
data/README.md
ADDED
@@ -0,0 +1,285 @@
|
|
1
|
+
Dump and load relational objects between Ruby environments.
|
2
|
+
===========================================================
|
3
|
+
|
4
|
+
*This repository is archived and no longer actively maintained by @rtomayko as of 2017-11-08. Issues and PRs documenting current issues have been intentionally left open for informational purposes.*
|
5
|
+
|
6
|
+
The project started at GitHub to simplify the process of getting real production
|
7
|
+
data into development and staging environments. We use it to replicate entire
|
8
|
+
repository data (including associated issue, pull request, commit comment, etc.
|
9
|
+
records) from production to our development environments with a single command.
|
10
|
+
It's excessively useful for troubleshooting issues, support requests, and
|
11
|
+
exception reports as well as for establishing real data for evaluating design
|
12
|
+
concepts.
|
13
|
+
|
14
|
+
Synopsis
|
15
|
+
--------
|
16
|
+
|
17
|
+
### Installing
|
18
|
+
|
19
|
+
$ gem install replicate
|
20
|
+
|
21
|
+
### Dumping objects
|
22
|
+
|
23
|
+
Evaluate a Ruby expression, dumping all resulting objects to standard output:
|
24
|
+
|
25
|
+
$ replicate -r ./config/environment -d "User.find(1)" > user.dump
|
26
|
+
==> dumped 4 total objects:
|
27
|
+
Profile 1
|
28
|
+
User 1
|
29
|
+
UserEmail 2
|
30
|
+
|
31
|
+
The `-r ./config/environment` option is used to require environment setup and
|
32
|
+
model instantiation code needed by the ruby expression.
|
33
|
+
|
34
|
+
### Dumping many objects with a dump script
|
35
|
+
|
36
|
+
Dump scripts are normal ruby source files evaluated in the context of the
|
37
|
+
dumper. The `dump(object)` method is used to put objects into the dump stream.
|
38
|
+
|
39
|
+
```ruby
|
40
|
+
# config/replicate/dump-stuff.rb
|
41
|
+
require 'config/environment'
|
42
|
+
|
43
|
+
%w[rtomayko/tilt rtomayko/bcat].each do |repo_name|
|
44
|
+
repo = Repository.find_by_name_with_owner(repo_name)
|
45
|
+
dump repo
|
46
|
+
dump repo.commit_comments
|
47
|
+
dump repo.issues
|
48
|
+
end
|
49
|
+
```
|
50
|
+
|
51
|
+
Run the dump script:
|
52
|
+
|
53
|
+
$ replicate -d config/replicate/dump-stuff.rb > repos.dump
|
54
|
+
==> dumped 1479 total objects:
|
55
|
+
AR::Habtm 101
|
56
|
+
CommitComment 95
|
57
|
+
Issue 101
|
58
|
+
IssueComment 427
|
59
|
+
IssueEvent 308
|
60
|
+
Label 5
|
61
|
+
Language 19
|
62
|
+
LanguageName 1
|
63
|
+
Milestone 3
|
64
|
+
Organization 4
|
65
|
+
Profile 82
|
66
|
+
PullRequest 44
|
67
|
+
PullRequestReviewComment 8
|
68
|
+
Repository 20
|
69
|
+
Team 4
|
70
|
+
TeamMember 6
|
71
|
+
User 89
|
72
|
+
UserEmail 162
|
73
|
+
|
74
|
+
### Loading many objects:
|
75
|
+
|
76
|
+
$ replicate -r ./config/environment -l < repos.dump
|
77
|
+
==> loaded 1479 total objects:
|
78
|
+
AR::Habtm 101
|
79
|
+
CommitComment 95
|
80
|
+
Issue 101
|
81
|
+
IssueComment 427
|
82
|
+
IssueEvent 308
|
83
|
+
Label 5
|
84
|
+
Language 19
|
85
|
+
LanguageName 1
|
86
|
+
Milestone 3
|
87
|
+
Organization 4
|
88
|
+
Profile 82
|
89
|
+
PullRequest 44
|
90
|
+
PullRequestReviewComment 8
|
91
|
+
Repository 20
|
92
|
+
Team 4
|
93
|
+
TeamMember 6
|
94
|
+
User 89
|
95
|
+
UserEmail 162
|
96
|
+
|
97
|
+
### Dumping and loading over ssh
|
98
|
+
|
99
|
+
$ remote_command="replicate -r /app/config/environment -d 'User.find(1234)'"
|
100
|
+
$ ssh example.org "$remote_command" |replicate -r ./config/environment -l
|
101
|
+
|
102
|
+
ActiveRecord
|
103
|
+
------------
|
104
|
+
|
105
|
+
Basic support for dumping and loading ActiveRecord objects is included. The
|
106
|
+
tests pass under ActiveRecord versions 2.2.3, 2.3.5, 2.3.14, 3.0.10, 3.1.0, and 3.2.0 under
|
107
|
+
MRI 1.8.7 as well as under MRI 1.9.2.
|
108
|
+
|
109
|
+
To use customization macros in your models, require the replicate library after
|
110
|
+
ActiveRecord (in e.g., `config/initializers/libraries.rb`):
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
require 'active_record'
|
114
|
+
require 'replicate'
|
115
|
+
```
|
116
|
+
|
117
|
+
ActiveRecord support works sensibly without customization so this isn't strictly
|
118
|
+
necessary to use the `replicate` command. The following sections document the
|
119
|
+
available customization macros.
|
120
|
+
|
121
|
+
### Association Dumping
|
122
|
+
|
123
|
+
The baked in support adds some more or less sensible default behavior for all
|
124
|
+
subclasses of `ActiveRecord::Base` such that dumping an object will bring in
|
125
|
+
objects related via `belongs_to` and `has_one` associations.
|
126
|
+
|
127
|
+
Unlike 1:1 associations, `has_many` and `has_and_belongs_to_many` associations
|
128
|
+
are not automatically included. Doing so would quickly lead to the entire
|
129
|
+
database being sucked in. It can be useful to mark specific associations for
|
130
|
+
automatic inclusion using the `replicate_associations` macro. For instance,
|
131
|
+
to always include `EmailAddress` records belonging to a `User`:
|
132
|
+
|
133
|
+
```ruby
|
134
|
+
class User < ActiveRecord::Base
|
135
|
+
belongs_to :profile
|
136
|
+
has_many :email_addresses
|
137
|
+
|
138
|
+
replicate_associations :email_addresses
|
139
|
+
end
|
140
|
+
```
|
141
|
+
|
142
|
+
You may also do this by passing an option in your dump script:
|
143
|
+
|
144
|
+
```ruby
|
145
|
+
dump User.all, :associations => [:email_addresses]
|
146
|
+
```
|
147
|
+
|
148
|
+
### Natural Keys
|
149
|
+
|
150
|
+
By default, the loader attempts to create a new record with a new primary key id
|
151
|
+
for all objects. This can lead to unique constraint errors when a record already
|
152
|
+
exists with matching attributes. To update existing records instead of
|
153
|
+
creating new ones, define a natural key for the model using the `replicate_natural_key`
|
154
|
+
macro:
|
155
|
+
|
156
|
+
```ruby
|
157
|
+
class User < ActiveRecord::Base
|
158
|
+
belongs_to :profile
|
159
|
+
has_many :email_addresses
|
160
|
+
|
161
|
+
replicate_natural_key :login
|
162
|
+
replicate_associations :email_addresses
|
163
|
+
end
|
164
|
+
|
165
|
+
class EmailAddress < ActiveRecord::Base
|
166
|
+
belongs_to :user
|
167
|
+
replicate_natural_key :user_id, :email
|
168
|
+
end
|
169
|
+
```
|
170
|
+
|
171
|
+
Multiple attribute names may be specified to define a compound key. Foreign key
|
172
|
+
column attributes (`user_id`) are often included in natural keys.
|
173
|
+
|
174
|
+
### Omission of attributes and associations
|
175
|
+
|
176
|
+
You might want to exclude some attributes or associations from being dumped. For
|
177
|
+
this, use the replicate_omit_attributes macro:
|
178
|
+
|
179
|
+
```ruby
|
180
|
+
class User < ActiveRecord::Base
|
181
|
+
has_one :profile
|
182
|
+
|
183
|
+
replicate_omit_attributes :created_at, :profile
|
184
|
+
end
|
185
|
+
```
|
186
|
+
|
187
|
+
You can omit belongs_to associations by omitting the foreign key column.
|
188
|
+
|
189
|
+
You may also do this by passing an option in your dump script:
|
190
|
+
|
191
|
+
```ruby
|
192
|
+
dump User.all, :omit => [:profile]
|
193
|
+
```
|
194
|
+
|
195
|
+
### Validations and Callbacks
|
196
|
+
|
197
|
+
__IMPORTANT:__ All ActiveRecord validations and callbacks are disabled on the
|
198
|
+
loading side. While replicate piggybacks on AR for relationship information and
|
199
|
+
uses `ActiveRecord::Base#save` to write objects to the database, it's designed
|
200
|
+
to act as a simple dump / load tool.
|
201
|
+
|
202
|
+
It's sometimes useful to run certain types of callbacks on replicate. For
|
203
|
+
instance, you might want to create files on disk or load information into a
|
204
|
+
separate data store any time an object enters the database. The best way to go
|
205
|
+
about this currently is to override the model's `load_replicant` class method:
|
206
|
+
|
207
|
+
```ruby
|
208
|
+
class User < ActiveRecord::Base
|
209
|
+
def self.load_replicant(type, id, attrs)
|
210
|
+
id, object = super
|
211
|
+
object.register_in_redis
|
212
|
+
object.some_other_callback
|
213
|
+
[id, object]
|
214
|
+
end
|
215
|
+
end
|
216
|
+
```
|
217
|
+
|
218
|
+
This interface will be improved in future versions.
|
219
|
+
|
220
|
+
Custom Objects
|
221
|
+
--------------
|
222
|
+
|
223
|
+
Other object types may be included in the dump stream so long as they implement
|
224
|
+
the `dump_replicant` and `load_replicant` methods.
|
225
|
+
|
226
|
+
### dump_replicant
|
227
|
+
|
228
|
+
The dump side calls `#dump_replicant(dumper, opts={})` on each object. The method must
|
229
|
+
call `dumper.write()` with the class name, id, and hash of primitively typed
|
230
|
+
attributes for the object:
|
231
|
+
|
232
|
+
```ruby
|
233
|
+
class User
|
234
|
+
attr_reader :id
|
235
|
+
attr_accessor :name, :email
|
236
|
+
|
237
|
+
def dump_replicant(dumper, opts={})
|
238
|
+
attributes = { 'name' => name, 'email' => email }
|
239
|
+
dumper.write self.class, id, attributes, self
|
240
|
+
end
|
241
|
+
end
|
242
|
+
```
|
243
|
+
|
244
|
+
### load_replicant
|
245
|
+
|
246
|
+
The load side calls `::load_replicant(type, id, attributes)` on the class to
|
247
|
+
load each object into the current environment. The method must return an
|
248
|
+
`[id, object]` tuple:
|
249
|
+
|
250
|
+
```ruby
|
251
|
+
class User
|
252
|
+
def self.load_replicant(type, id, attributes)
|
253
|
+
user = User.new
|
254
|
+
user.name = attributes['name']
|
255
|
+
user.email = attributes['email']
|
256
|
+
user.save!
|
257
|
+
[user.id, user]
|
258
|
+
end
|
259
|
+
end
|
260
|
+
```
|
261
|
+
|
262
|
+
How it works
|
263
|
+
------------
|
264
|
+
|
265
|
+
The dump format is designed for streaming relational data. Each object is
|
266
|
+
encoded as a `[type, id, attributes]` tuple and marshalled directly onto the
|
267
|
+
stream. The `type` (class name string) and `id` must form a distinct key when
|
268
|
+
combined, `attributes` must consist of only string keys and simply typed values.
|
269
|
+
|
270
|
+
Relationships between objects in the stream are managed as follows:
|
271
|
+
|
272
|
+
- An object's attributes may encode references to objects that precede it
|
273
|
+
in the stream using a simple tuple format: `[:id, 'User', 1234]`.
|
274
|
+
|
275
|
+
- The dump side ensures that objects are written to the dump stream in
|
276
|
+
"reference order" such that when an object A includes a reference attribute
|
277
|
+
to an object B, B is guaranteed to arrive before A.
|
278
|
+
|
279
|
+
- The load side maintains a mapping of ids from the dumping system to the newly
|
280
|
+
replicated objects on the loading system. When the loader encounters a
|
281
|
+
reference value `[:id, 'User', 1234]` in an object's attributes, it converts it
|
282
|
+
to the load side id value.
|
283
|
+
|
284
|
+
Dumping and loading happens in a streaming fashion. There is no limit on the
|
285
|
+
number of objects included in the stream.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'rake/clean'
|
2
|
+
require 'rake/testtask'
|
3
|
+
|
4
|
+
task :default => :test
|
5
|
+
|
6
|
+
desc "Run tests"
|
7
|
+
Rake::TestTask.new do |t|
|
8
|
+
t.test_files = FileList['test/*_test.rb']
|
9
|
+
end
|
10
|
+
|
11
|
+
CLEAN.include 'test/db'
|
12
|
+
|
13
|
+
desc "Build gem"
|
14
|
+
task :build do
|
15
|
+
sh "gem build replicate.gemspec"
|
16
|
+
end
|
data/bin/replicate
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#/ Usage: replicate --dump dumpscript.rb > objects.dump
|
3
|
+
#/ replicate [-r <lib>] --dump "<ruby>" [-- <argv>...] > objects.dump
|
4
|
+
#/ replicate [-r <lib>] --load < objects.dump
|
5
|
+
#/ Dump and load objects between environments.
|
6
|
+
#
|
7
|
+
#/ The --dump form writes to stdout the objects dumped by the script or
|
8
|
+
#/ ruby expression(s) given. Dump scripts are normal Ruby source files but
|
9
|
+
#/ must call dump(object) on one or more objects. When a Ruby expression is
|
10
|
+
#/ given, all resulting objects are dumped automatically.
|
11
|
+
#/
|
12
|
+
#/ Dump scripts have access to any additional <argv> provided via the normal
|
13
|
+
#/ system ARGV array. This can be used to pass arguments to dump scripts.
|
14
|
+
#/
|
15
|
+
#/ The --load form reads dump data from stdin and creates objects under the
|
16
|
+
#/ current environment.
|
17
|
+
#/
|
18
|
+
#/ Mode selection:
|
19
|
+
#/ -d, --dump Dump the repository and all related objects to stdout.
|
20
|
+
#/ -l, --load Load dump file data from stdin.
|
21
|
+
#/
|
22
|
+
#/ Options:
|
23
|
+
#/ -r, --require Require the library. Often used with 'config/environment'.
|
24
|
+
#/ -i, --keep-id Use replicated ids when loading dump file.
|
25
|
+
#/ -f, --force Allow loading in production environments.
|
26
|
+
#/ -v, --verbose Write more status output.
|
27
|
+
#/ -q, --quiet Write less status output.
|
28
|
+
$stderr.sync = true
|
29
|
+
$stdout = $stderr
|
30
|
+
|
31
|
+
require 'optparse'
|
32
|
+
|
33
|
+
# default options
|
34
|
+
mode = nil
|
35
|
+
verbose = false
|
36
|
+
quiet = false
|
37
|
+
keep_id = false
|
38
|
+
out = STDOUT
|
39
|
+
force = false
|
40
|
+
|
41
|
+
# parse arguments
|
42
|
+
file = __FILE__
|
43
|
+
usage = lambda { exec "grep ^#/<'#{file}'|cut -c4-" }
|
44
|
+
original_argv = ARGV.dup
|
45
|
+
ARGV.options do |opts|
|
46
|
+
opts.on("-d", "--dump") { mode = :dump }
|
47
|
+
opts.on("-l", "--load") { mode = :load }
|
48
|
+
opts.on("-r", "--require=f") { |file| require file }
|
49
|
+
opts.on("-v", "--verbose") { verbose = true }
|
50
|
+
opts.on("-q", "--quiet") { quiet = true }
|
51
|
+
opts.on("-i", "--keep-id") { keep_id = true }
|
52
|
+
opts.on("--force") { force = true }
|
53
|
+
opts.on_tail("-h", "--help", &usage)
|
54
|
+
opts.parse!
|
55
|
+
end
|
56
|
+
|
57
|
+
# load replicate lib and setup AR
|
58
|
+
require 'replicate'
|
59
|
+
if defined?(ActiveRecord::Base)
|
60
|
+
require 'replicate/active_record'
|
61
|
+
ActiveRecord::Base.replicate_id = keep_id
|
62
|
+
ActiveRecord::Base.connection.enable_query_cache!
|
63
|
+
end
|
64
|
+
|
65
|
+
# dump mode means we're reading records from the database here and writing to
|
66
|
+
# stdout. the database should not be modified at all by this operation.
|
67
|
+
if mode == :dump
|
68
|
+
script = ARGV.shift
|
69
|
+
usage.call if script.empty?
|
70
|
+
Replicate::Dumper.new do |dumper|
|
71
|
+
dumper.marshal_to out
|
72
|
+
dumper.log_to $stderr, verbose, quiet
|
73
|
+
if script == '-'
|
74
|
+
code = $stdin.read
|
75
|
+
objects = dumper.instance_eval(code, '<stdin>', 0)
|
76
|
+
elsif File.exist?(script)
|
77
|
+
dumper.load_script script
|
78
|
+
else
|
79
|
+
objects = dumper.instance_eval(script, '<argv>', 0)
|
80
|
+
dumper.dump objects
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# load mode means we're reading objects from stdin and creating them under
|
85
|
+
# the current environment.
|
86
|
+
elsif mode == :load
|
87
|
+
if Replicate.production_environment? && !force
|
88
|
+
abort "error: refusing to load in production environment\n" +
|
89
|
+
" manual override: #{File.basename($0)} --force #{original_argv.join(' ')}"
|
90
|
+
else
|
91
|
+
Replicate::Loader.new do |loader|
|
92
|
+
loader.log_to $stderr, verbose, quiet
|
93
|
+
loader.read $stdin
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# mode not set means no -l or -d arg was given. show usage and bail.
|
98
|
+
else
|
99
|
+
usage.call
|
100
|
+
end
|