mruby_tools 0.0.3.1 → 0.0.4.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9d06a51f1ed9667b7b39c12bcb3173f4b064d6ad
4
- data.tar.gz: e63a21a002b71f1aef930fb5f91950e90757e7c3
3
+ metadata.gz: 74d761a22e0d9e61ab9da0ea9204690c938b45f4
4
+ data.tar.gz: 6f4a0f440ce7638f058b4b7631b22c6d8ceb714e
5
5
  SHA512:
6
- metadata.gz: 566ad106b83ae4838f08d0233f485b910e9f86d484e819c72034b4b9fb36422db9e490bcfc9f0d24add9e0f021d244400b4704644afc38bf508c90f7dff4408f
7
- data.tar.gz: 32b9ae7a7685e73e17b23fba501c8036ec04065c9ee46afc97f855fbd6fc2b6cfcf2da4c29df9e685760e27cb0024d08cee470dee46be9aaf099949e1a2a4ad5
6
+ metadata.gz: 4a90438bfd7b6edd9a91eb87e02be7a6206cd7476a4343de5998a8896578e0fafff3f8f5a5a97efdbf8ff1d8e98c5f4b23da77f5cc78318ca32737c17c93b288
7
+ data.tar.gz: 8b0e1c387bf140520b1919361aa2d6a753f2e21f4b09188c951d40186bd525a767603ae0c37d649b6e5e7243ae9c40276e53aba2c74e34d682cf6feeb3e4683b
data/README.md CHANGED
@@ -13,6 +13,15 @@ with **mruby**.
13
13
  executable using mruby. The .rb files must be
14
14
  [mruby-compatible](https://github.com/mruby/mruby/blob/master/doc/limitations.md) (roughly equivalent to MRI v1.9).
15
15
 
16
+ Two primary modes of operation are supported:
17
+
18
+ 1. ruby code is injected into a C wrapper which is then compiled. The ruby
19
+ code is **interpreted** every time the resulting binary is executed.
20
+
21
+ 2. ruby code is first interpreted into **bytecode**, and the bytecode
22
+ is injected into a C wrapper which is then compiled. This means a faster
23
+ runtime as the code interpretation is not performed at runtime.
24
+
16
25
  ## Install
17
26
 
18
27
  ### `git clone`
@@ -67,16 +76,24 @@ Prerequisites:
67
76
  gem install mruby_tools
68
77
  ```
69
78
 
70
- Now, `mrbt` may be used. It will need to know where to find the mruby_dir,
71
- so specify it with the `-m` flag or `MRUBY_DIR` environment variable.
79
+ Now, `mrbt` may be used. We set the `MRUBY_DIR` environment variable:
72
80
 
73
81
  ```shell
74
- export MRUBY_DIR=~/src/mruby-1.3.0 # or wherever
82
+ export MRUBY_DIR=~/src/mruby-1.3.0 # or wherever it lives
75
83
  mrbt file1.rb file2.rb # etc.
76
84
  ```
77
85
 
78
86
  ## Usage
79
87
 
88
+ ```
89
+ USAGE: mrbt file1.rb file2.rb ...
90
+ OPTIONS: -o outfile (provide a name for the standalone executable)
91
+ -c generated.c (leave the specified C file on the filesystem)
92
+ -m mruby_dir (provide the dir for mruby src)
93
+ -b (pregenerate ruby bytecode for faster execution)
94
+ -v (verbose)
95
+ ```
96
+
80
97
  With no additional options, `mrbt` will inject the contents of file1.rb and
81
98
  file2.rb into C strings, to be loaded with mrb_load_nstring(), written to a
82
99
  Tempfile. `mrbt` then compiles the generated .c file using GCC and writes
@@ -97,25 +114,12 @@ rake mrbt -- examples/hello_world.rb examples/goodbye_world.rb -o adios
97
114
 
98
115
  Other useful rake tasks:
99
116
 
100
- * hello world - compiles and runs `examples/hello_world.rb`
101
- * timed_simplex - compiles and runs several files that work together
117
+ * hello world - compiles and runs `examples/hello_world.rb`
118
+ * timed_simplex - compiles and runs several files that work together
102
119
  * raise_exception - compiles and runs `examples/raise.rb`
103
120
  * examples - runs hello_world and timed_simplex
104
- * verbose - add verbose output, e.g. `rake verbose hello_world`
105
-
106
- ### With *rubygems*
107
-
108
- ```shell
109
- mrbt -h
110
- ```
111
-
112
- ```
113
- USAGE: mrbt file1.rb file2.rb ...
114
- OPTIONS: -o outfile (provide a name for the standalone executable)
115
- -c generated.c (leave the specified C file on the filesystem)
116
- -m mruby_dir (provide the dir for mruby src)
117
- -v (verbose)
118
- ```
121
+ * verbose - add verbose output, e.g. `rake verbose hello_world`
122
+ * bytecode - enable bytecode generation at compile time
119
123
 
120
124
  ## Examples
121
125
 
@@ -162,7 +166,7 @@ rake verbose hello_world
162
166
  ```
163
167
 
164
168
  ```
165
- /home/vagrant/.rubies/ruby-2.4.0/bin/ruby -Ilib bin/mrbt examples/hello_world.rb -o examples/hello_world -v
169
+ /home/vagrant/.rubies/ruby-2.4.0/bin/ruby -Ilib bin/mrbt -v examples/hello_world.rb -o examples/hello_world
166
170
  #include <stdlib.h>
167
171
  #include <mruby.h>
168
172
  #include <mruby/compile.h>
@@ -198,21 +202,21 @@ main(void)
198
202
  return 0;
199
203
  }
200
204
 
201
- generated /tmp/mrbt-20171117-24568-no7ee3.c
205
+ generated /tmp/mrbt-20171122-20650-4pi6tt.c
202
206
  compiling...
203
207
  created binary executable: examples/hello_world
204
208
 
205
209
  file examples/hello_world
206
- examples/hello_world: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=2114ab60983156c92a5a74b88895f9c43a4bb086, not stripped
210
+ examples/hello_world: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=e9fb81e70686e761cf49202efc4f3e733d7aab17, not stripped
207
211
 
208
212
  stat examples/hello_world
209
213
  File: ‘examples/hello_world’
210
- Size: 1559056 Blocks: 3048 IO Block: 4096 regular file
211
- Device: 801h/2049d Inode: 388049 Links: 1
214
+ Size: 1631784 Blocks: 3192 IO Block: 4096 regular file
215
+ Device: 801h/2049d Inode: 388542 Links: 1
212
216
  Access: (0755/-rwxr-xr-x) Uid: ( 1000/ vagrant) Gid: ( 1000/ vagrant)
213
- Access: 2017-11-17 18:48:25.713629203 +0000
214
- Modify: 2017-11-17 18:48:25.709629203 +0000
215
- Change: 2017-11-17 18:48:25.709629203 +0000
217
+ Access: 2017-11-22 23:42:12.202875568 +0000
218
+ Modify: 2017-11-22 23:42:12.190881566 +0000
219
+ Change: 2017-11-22 23:42:12.190881566 +0000
216
220
  Birth: -
217
221
 
218
222
  examples/hello_world
@@ -224,3 +228,82 @@ This proceeds exactly as before but with additional output:
224
228
  * Show the generated C code
225
229
  * Note the temporary file containing the C code
226
230
  * Call `file` and `stat` on the output executable
231
+
232
+ ### Verbose Bytecode Hello World
233
+
234
+ ```shell
235
+ mrbt -v examples/hello_world.rb -b
236
+
237
+ # or
238
+ rake mrbt -- -b examples/hello_world.rb -v
239
+
240
+ # or even
241
+ rake verbose bytecode hello_world
242
+ ```
243
+
244
+ ```
245
+ /home/vagrant/.rubies/ruby-2.4.0/bin/ruby -Ilib bin/mrbt -b -v examples/hello_world.rb -o examples/hello_world
246
+ creating bytecode.mrb...
247
+ #include <stdlib.h>
248
+ #include <stdint.h>
249
+ #include <mruby.h>
250
+ #include <mruby/string.h>
251
+ #include <mruby/irep.h>
252
+
253
+ const uint8_t
254
+ #if defined __GNUC__
255
+ __attribute__((aligned(4)))
256
+ #elif defined _MSC_VER
257
+ __declspec(align(4))
258
+ #endif
259
+ /* bytecode.mrb */
260
+ test_symbol[] = {
261
+ 0x52,0x49,0x54,0x45,0x30,0x30,0x30,0x34,0x22,0x80,0x00,0x00,0x00,0x65,0x4d,0x41,0x54,0x5a,0x30,0x30,0x30,0x30,0x49,0x52,0x45,0x50,0x00,0x00,0x00,0x47,0x30,0x30,0x30,0x30,0x00,0x00,0x00,0x3f,0x00,0x01,0x00,0x04,0x00,0x00,0x00,0x00,0x00,0x04,0x00,0x80,0x00,0x06,0x01,0x00,0x00,0x84,0x00,0x80,0x00,0xa0,0x00,0x00,0x00,0x4a,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x02,0x00,0x04,0x70,0x75,0x74,0x73,0x00,0x00,0x0b,0x68,0x65,0x6c,0x6c,0x6f,0x5f,0x77,0x6f,0x72,0x6c,0x64,0x00,0x45,0x4e,0x44,0x00,0x00,0x00,0x00,0x08,
262
+ };
263
+
264
+ void check_exc(mrb_state *mrb) {
265
+ if (mrb->exc) {
266
+ mrb_value exc = mrb_obj_value(mrb->exc);
267
+ mrb_value exc_msg = mrb_funcall(mrb, exc, "to_s", 0);
268
+ fprintf(stderr, "ERROR %s: %s\n",
269
+ mrb_obj_classname(mrb, exc),
270
+ mrb_str_to_cstr(mrb, exc_msg));
271
+ /* mrb_print_backtrace(mrb); # empty */
272
+ exit(1);
273
+ }
274
+ }
275
+
276
+ int
277
+ main(void)
278
+ {
279
+ mrb_state *mrb = mrb_open();
280
+ if (!mrb) {
281
+ printf("mrb problem");
282
+ exit(1);
283
+ }
284
+ mrb_load_irep(mrb, test_symbol);
285
+ check_exc(mrb);
286
+ mrb_close(mrb);
287
+ return 0;
288
+ }
289
+
290
+ generated /tmp/mrbt-20171122-25456-azsw3n.c
291
+ compiling...
292
+ created binary executable: examples/hello_world
293
+
294
+ file examples/hello_world
295
+ examples/hello_world: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=61e903e56a9e5d1159cb5a5ea4a669b05b493f44, not stripped
296
+
297
+ stat examples/hello_world
298
+ File: ‘examples/hello_world’
299
+ Size: 1324616 Blocks: 2592 IO Block: 4096 regular file
300
+ Device: 801h/2049d Inode: 388542 Links: 1
301
+ Access: (0755/-rwxr-xr-x) Uid: ( 1000/ vagrant) Gid: ( 1000/ vagrant)
302
+ Access: 2017-11-22 23:46:09.540147567 +0000
303
+ Modify: 2017-11-22 23:46:09.536149567 +0000
304
+ Change: 2017-11-22 23:46:09.536149567 +0000
305
+ Birth: -
306
+
307
+ examples/hello_world
308
+ hello_world
309
+ ```
data/Rakefile CHANGED
@@ -37,8 +37,15 @@ task :verbose do
37
37
  @verbose = true
38
38
  end
39
39
 
40
+ @bytecode = false
41
+
42
+ task :bytecode do
43
+ @bytecode = true
44
+ end
45
+
40
46
  def mrbt *args
41
47
  args.unshift('-v') if @verbose and !args.include?('-v')
48
+ args.unshift('-b') if @bytecode and !args.include?('-b')
42
49
  ruby '-Ilib', 'bin/mrbt', *args
43
50
  end
44
51
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3.1
1
+ 0.0.4.1
data/bin/mrbt CHANGED
@@ -1,6 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
- require 'tempfile'
4
3
  require 'mruby_tools'
5
4
 
6
5
  # args like: file1.rb file2.rb -o outfile
@@ -9,18 +8,35 @@ require 'mruby_tools'
9
8
  opts = MRubyTools::CLI.args(ARGV)
10
9
  MRubyTools::CLI.usage if opts[:help]
11
10
 
11
+ FAIL_MRUBY_NOT_FOUND = 1
12
+ FAIL_MRBC_BYTECODE = 1
13
+ FAIL_GCC = 1
14
+
15
+
12
16
  mrb = MRubyTools.new(opts[:mruby_dir]) # nil is fine
13
17
  begin
14
18
  mrb.validate!
15
19
  rescue MRubyTools::MRubyNotFound => e
16
20
  warn "#{e.class}: can't find #{e}"
17
- exit 1
21
+ exit FAIL_MRUBY_NOT_FOUND
18
22
  end
19
23
 
20
24
  rb_files = opts.fetch(:rb_files)
21
25
  MRubyTools::CLI.usage("no .rb files provided") if rb_files.empty?
22
26
 
23
- c_code = MRubyTools::C.wrapper(rb_files)
27
+ # inject a string vs create and inject bytecode
28
+ if opts[:bytecode]
29
+ bc_file = 'bytecode.mrb'
30
+ puts "creating #{bc_file}..."
31
+ if mrb.mrbc rb_files, bc_file
32
+ c_code = MRubyTools::C.bytecode_wrapper(bc_file)
33
+ else
34
+ warn "FATAL: mrbc bytecode generation failed"
35
+ exit FAIL_MRBC_BYTECODE
36
+ end
37
+ else
38
+ c_code = MRubyTools::C.wrapper(rb_files)
39
+ end
24
40
  puts c_code + "\n" if opts[:verbose]
25
41
 
26
42
  c_file = opts.fetch(:c_file)
@@ -34,5 +50,5 @@ if mrb.compile(c_file.path, out_file)
34
50
  puts "created binary executable: #{out_file}"
35
51
  else
36
52
  warn "FATAL: compile failed"
37
- exit 1
53
+ exit FAIL_GCC
38
54
  end
@@ -9,12 +9,13 @@ class MRubyTools
9
9
  ENV['MRUBY_DIR'] || MRUBY_DIR
10
10
  end
11
11
 
12
- attr_accessor :mruby_dir, :inc_path, :ar_path
12
+ attr_accessor :mruby_dir, :inc_path, :ar_path, :bin_path
13
13
 
14
14
  def initialize(mruby_dir = nil)
15
15
  @mruby_dir = mruby_dir || self.class.mruby_dir
16
16
  @inc_path = File.join(@mruby_dir, 'include')
17
17
  @ar_path = File.join(@mruby_dir, 'build', 'host', 'lib', 'libmruby.a')
18
+ @bin_path = File.join(@mruby_dir, 'bin')
18
19
  end
19
20
 
20
21
  def src?
@@ -28,6 +29,7 @@ class MRubyTools
28
29
  def validate!
29
30
  raise(MRubyNotFound, @inc_path) unless File.directory? @inc_path
30
31
  raise(MRubyNotFound, @ar_path) unless File.readable? @ar_path
32
+ raise(MRubyNotFound, @bin_path) unless File.directory? @bin_path
31
33
  self
32
34
  end
33
35
 
@@ -39,7 +41,80 @@ class MRubyTools
39
41
  system('gcc', *self.gcc_args(c_file, out_file))
40
42
  end
41
43
 
44
+ # interpret several rb_files into a binary out_file contaning bytecode
45
+ def mrbc(rb_files, out_file)
46
+ system(File.join(@bin_path, 'mrbc'), '-o', out_file, *rb_files)
47
+ end
48
+
42
49
  module C
50
+ def self.slurp_mrb(bc_file, indent: ' ')
51
+ test_symbol = File.read(bc_file).each_byte.reduce('') { |memo, b|
52
+ memo + format("0x%0.2x,", b.ord)
53
+ }
54
+ ["/* #{bc_file} */",
55
+ "test_symbol[] = {",
56
+ test_symbol,
57
+ "};",
58
+ ].map { |s| indent + s }.join("\n")
59
+ end
60
+
61
+ def self.slurp_rb(rb_filename, indent: ' ')
62
+ c_str = File.read(rb_filename)
63
+ size = c_str.size
64
+ c_str = c_str.gsub("\n", '\n').gsub('"', '\"')
65
+ [ "/* #{rb_filename} */",
66
+ 'mrb_load_nstring(mrb, "' + c_str + '", ' + "#{size});",
67
+ "check_exc(mrb, \"#{rb_filename}\");",
68
+ ].map { |s| indent + s }.join("\n")
69
+ end
70
+
71
+ def self.bytecode_wrapper(bc_file)
72
+ c_code = <<'EOF'
73
+ #include <stdlib.h>
74
+ #include <stdint.h>
75
+ #include <mruby.h>
76
+ #include <mruby/string.h>
77
+ #include <mruby/irep.h>
78
+
79
+ const uint8_t
80
+ #if defined __GNUC__
81
+ __attribute__((aligned(4)))
82
+ #elif defined _MSC_VER
83
+ __declspec(align(4))
84
+ #endif
85
+ EOF
86
+ c_code += slurp_mrb(bc_file, indent: '') + "\n\n"
87
+
88
+ c_code += <<'EOF'
89
+ void check_exc(mrb_state *mrb) {
90
+ if (mrb->exc) {
91
+ mrb_value exc = mrb_obj_value(mrb->exc);
92
+ mrb_value exc_msg = mrb_funcall(mrb, exc, "to_s", 0);
93
+ fprintf(stderr, "ERROR %s: %s\n",
94
+ mrb_obj_classname(mrb, exc),
95
+ mrb_str_to_cstr(mrb, exc_msg));
96
+ /* mrb_print_backtrace(mrb); # empty */
97
+ exit(1);
98
+ }
99
+ }
100
+
101
+ int
102
+ main(void)
103
+ {
104
+ mrb_state *mrb = mrb_open();
105
+ if (!mrb) {
106
+ printf("mrb problem");
107
+ exit(1);
108
+ }
109
+ mrb_load_irep(mrb, test_symbol);
110
+ check_exc(mrb);
111
+ mrb_close(mrb);
112
+ return 0;
113
+ }
114
+ EOF
115
+ c_code
116
+ end
117
+
43
118
  def self.wrapper(rb_files)
44
119
  c_code = <<'EOF'
45
120
  #include <stdlib.h>
@@ -80,29 +155,22 @@ EOF
80
155
  EOF
81
156
  c_code
82
157
  end
83
-
84
- def self.slurp_rb(rb_filename, indent: ' ')
85
- c_str = File.read(rb_filename)
86
- size = c_str.size
87
- c_str = c_str.gsub("\n", '\n').gsub('"', '\"')
88
- [ "/* #{rb_filename} */",
89
- 'mrb_load_nstring(mrb, "' + c_str + '", ' + "#{size});",
90
- "check_exc(mrb, \"#{rb_filename}\");",
91
- ].map { |s| indent + s }.join("\n")
92
- end
93
158
  end
94
159
 
95
160
  module CLI
161
+ USAGE_EXIT = 1
162
+
96
163
  def self.usage(msg = nil)
97
164
  puts <<EOF
98
165
  USAGE: mrbt file1.rb file2.rb ...
99
166
  OPTIONS: -o outfile (provide a name for the standalone executable)
100
167
  -c generated.c (leave the specified C file on the filesystem)
101
168
  -m mruby_dir (provide the dir for mruby src)
169
+ -b (pregenerate ruby bytecode for faster execution)
102
170
  -v (verbose)
103
171
  EOF
104
172
  warn " ERROR: #{msg}" if msg
105
- exit(msg ? 1 : 0)
173
+ exit(msg ? USAGE_EXIT : 0)
106
174
  end
107
175
 
108
176
  def self.args(argv = ARGV)
@@ -112,6 +180,7 @@ EOF
112
180
  mruby_dir = nil
113
181
  verbose = false
114
182
  help = false
183
+ bytecode = false
115
184
 
116
185
  while !argv.empty?
117
186
  arg = argv.shift
@@ -128,19 +197,23 @@ EOF
128
197
  elsif arg == '-m'
129
198
  mruby_dir = argv.shift
130
199
  raise "no mruby_dir provided with -m" unless mruby_dir
200
+ elsif arg == '-b'
201
+ bytecode = true
131
202
  else
132
- rb_files << arg
203
+ rb_files << arg unless arg == '--'
133
204
  end
134
205
  end
135
206
 
136
207
  c_file ||= Tempfile.new(['mrbt-', '.c'])
208
+ out_file ||= 'outfile'
137
209
 
138
210
  { verbose: verbose,
139
211
  help: help,
140
212
  c_file: c_file,
141
- out_file: out_file || 'outfile',
213
+ out_file: out_file,
142
214
  rb_files: rb_files,
143
- mruby_dir: mruby_dir }
215
+ mruby_dir: mruby_dir,
216
+ bytecode: bytecode }
144
217
  end
145
218
  end
146
219
  end
@@ -18,9 +18,11 @@ describe MRubyTools do
18
18
  obj.mruby_dir.must_be_kind_of String
19
19
  obj.inc_path.must_be_kind_of String
20
20
  obj.ar_path.must_be_kind_of String
21
+ obj.bin_path.must_be_kind_of String
21
22
  }
22
23
  @valid.inc_path.must_match %r{include}
23
24
  @valid.ar_path.must_match %r{libmruby.a}
25
+ @valid.bin_path.must_match %r{bin}
24
26
  end
25
27
  end
26
28
 
@@ -29,8 +31,19 @@ describe MRubyTools do
29
31
  str = MRubyTools::C.slurp_rb(__FILE__)
30
32
  str.must_be_kind_of String
31
33
  str.wont_be_empty
32
- str.must_match(/mrb_load_n?string\(/)
33
- str.must_match(/exc/)
34
+ str.must_match %r{mrb_load_n?string\(}
35
+ str.must_match %r{exc}
36
+ end
37
+ end
38
+
39
+ describe "C.slurp_mrb" do
40
+ it "must inject the contents of a bytecode file into test_symbol[]" do
41
+ # bytecode is pure binary -- it doesn't matter what file we provide
42
+ str = MRubyTools::C.slurp_mrb(__FILE__)
43
+ str.must_be_kind_of String
44
+ str.wont_be_empty
45
+ str.must_match %r{test_symbol}
46
+ str.must_match %r{0x\d\d,}
34
47
  end
35
48
  end
36
49
 
@@ -40,15 +53,28 @@ describe MRubyTools do
40
53
  str = MRubyTools::C.wrapper(rb_files)
41
54
  str.must_be_kind_of String
42
55
  str.wont_be_empty
43
- str.must_match(/main\(void\)/)
44
- str.must_match(/return/)
45
- str.must_match(/exit/)
46
- str.must_match(/mruby/)
47
- str.must_match(/mrb/)
56
+ str.must_match %r{main\(void\)}
57
+ str.must_match %r{return}
58
+ str.must_match %r{exit}
59
+ str.must_match %r{mruby}
60
+ str.must_match %r{mrb}
48
61
  }
49
62
  end
50
63
  end
51
64
 
65
+ describe "C.bytecode_wrapper" do
66
+ it "must return a string of mruby C code" do
67
+ str = MRubyTools::C.bytecode_wrapper(__FILE__)
68
+ str.must_be_kind_of String
69
+ str.wont_be_empty
70
+ str.must_match %r{main\(void\)}
71
+ str.must_match %r{return}
72
+ str.must_match %r{exit}
73
+ str.must_match %r{mruby}
74
+ str.must_match %r{mrb}
75
+ end
76
+ end
77
+
52
78
  describe "CLI.args" do
53
79
  it "must provide a hash with expected keys" do
54
80
  h = MRubyTools::CLI.args([])
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mruby_tools
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3.1
4
+ version: 0.0.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-11-22 00:00:00.000000000 Z
11
+ date: 2017-11-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake