sourcify 0.4.1 → 0.4.2

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.
@@ -1,3 +1,8 @@
1
+ === 0.4.2 (Feb 06, 2011)
2
+
3
+ * fixes Sourcify::NoMatchingProcError when inline proc contains hashes (issue#7) [#ngty]
4
+ * uses RubyParser#parse to eval correctness of code instead of Kernel#eval [#ngty]
5
+
1
6
  === 0.4.1 (Jan 29, 2011)
2
7
 
3
8
  * fixes Sourcify::NoMatchingProcError when if/unless/until/while modifier follows
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.4.2
@@ -1,12 +1,12 @@
1
1
  Sourcify.require_rb('proc', 'scanner')
2
+ %w{normalizer code_scanner source_code converter}.each do |file|
3
+ Sourcify.require_rb('proc', 'parser', file)
4
+ end
2
5
 
3
6
  module Sourcify
4
7
  module Proc
5
8
  class Parser #:nodoc:all
6
9
 
7
- RUBY_PARSER = RubyParser.new
8
- RUBY_2_RUBY = Ruby2Ruby.new
9
-
10
10
  IS_19x = RUBY_VERSION.include?('1.9.')
11
11
 
12
12
  def initialize(_proc)
@@ -17,13 +17,12 @@ module Sourcify
17
17
  end
18
18
 
19
19
  def source(opts)
20
- (@sources ||= {})[opts.hash] ||=
21
- RUBY_2_RUBY.process(Sexp.from_array(sexp(opts).to_a))
20
+ (@sources ||= {})[opts.hash] ||= Converter.to_code(sexp(opts))
22
21
  end
23
22
 
24
23
  def sexp(opts)
25
24
  (@sexps ||= {})[opts.hash] ||= (
26
- raw_sexp = RUBY_PARSER.parse(raw_source(opts), @source_code.file)
25
+ raw_sexp = Converter.to_sexp(raw_source(opts), @source_code.file)
27
26
  sexp = Normalizer.process(raw_sexp, @binding)
28
27
  opts[:strip_enclosure] ? Sexp.from_array(sexp.to_a.last) : sexp
29
28
  )
@@ -41,110 +40,6 @@ module Sourcify
41
40
  end
42
41
  end
43
42
 
44
- class Normalizer
45
- class << self
46
-
47
- def process(sexp, binding)
48
- @binding = binding
49
- Sexp.from_array(fix_no_arg_method_calls(sexp.to_a))
50
- end
51
-
52
- def fix_no_arg_method_calls(array)
53
- return array if [:class, :sclass, :defn, :module].include?(array[0])
54
- array.map do |e|
55
- if e.is_a?(Array)
56
- no_arg_method_call?(e) or fix_no_arg_method_calls(e)
57
- else
58
- e
59
- end
60
- end
61
- end
62
-
63
- def no_arg_method_call?(e)
64
- if like_no_arg_method_call?(e)
65
- bounded_var?(var = e[2]) ? [:lvar, var] : e
66
- end
67
- end
68
-
69
- def like_no_arg_method_call?(e)
70
- e.size == 4 && e[0..1] == [:call, nil] &&
71
- e[3] == [:arglist] && (var = e[2]).is_a?(Symbol)
72
- end
73
-
74
- def bounded_var?(var)
75
- qvar = (@q ||= (IS_19x ? ":%s" : "'%s'")) % var
76
- @binding.eval("local_variables.include?(#{qvar})")
77
- end
78
-
79
- end
80
- end
81
-
82
- class CodeScanner
83
- class << self
84
-
85
- def process(source_code, opts, &matcher)
86
- results = rscan(source_code.to_s, {
87
- :start_pattern => scan_pattern_hint(opts[:attached_to]),
88
- :body_matcher => opts[:body_matcher],
89
- :ignore_nested => opts[:ignore_nested],
90
- :stop_on_newline => false,
91
- }).flatten.select(&matcher)
92
- case results.size
93
- when 0 then raise NoMatchingProcError
94
- when 1 then ("\n" * source_code.line) + results[0]
95
- else raise MultipleMatchingProcsPerLineError
96
- end
97
- end
98
-
99
- def scan_pattern_hint(val)
100
- case val
101
- when Regexp then val
102
- when String, Symbol then /^(?:.*?\W|)#{val}(?:\W)/
103
- when nil then /.*/
104
- else raise TypeError
105
- end
106
- end
107
-
108
- def rscan(str, opts)
109
- results = Scanner.process(str, opts) || []
110
- return results if opts[:ignore_nested]
111
- results.map do |outer|
112
- inner = rscan(outer.sub(/^proc\s*(do|\{)/,''), opts.merge(:stop_on_newline => true))
113
- [outer, inner]
114
- end
115
- end
116
-
117
- end
118
- end
119
-
120
- class SourceCode < Struct.new(:file, :line)
121
-
122
- def line
123
- super.pred
124
- end
125
-
126
- def to_s
127
- case file
128
- when /\(irb\)/ then from_irb_to_s
129
- else from_file_to_s
130
- end
131
- end
132
-
133
- def from_file_to_s
134
- File.open(file, 'r') do |fh|
135
- fh.extend(File::Tail).forward(line)
136
- fh.readlines.join
137
- end
138
- end
139
-
140
- def from_irb_to_s
141
- # Really owe it to Florian Groß's solution @ http://rubyquiz.com/quiz38.html ...
142
- # anyway, note that we use *line.succ* instead of *line* here.
143
- IRB.CurrentContext.io.line(line.succ .. -1).join
144
- end
145
-
146
- end
147
-
148
43
  end
149
44
  end
150
45
  end
@@ -0,0 +1,43 @@
1
+ module Sourcify
2
+ module Proc
3
+ class Parser #:nodoc:all
4
+ class CodeScanner
5
+ class << self
6
+
7
+ def process(source_code, opts, &matcher)
8
+ results = rscan(source_code.to_s, {
9
+ :start_pattern => scan_pattern_hint(opts[:attached_to]),
10
+ :body_matcher => opts[:body_matcher],
11
+ :ignore_nested => opts[:ignore_nested],
12
+ :stop_on_newline => false,
13
+ }).flatten.select(&matcher)
14
+ case results.size
15
+ when 0 then raise NoMatchingProcError
16
+ when 1 then ("\n" * source_code.line) + results[0]
17
+ else raise MultipleMatchingProcsPerLineError
18
+ end
19
+ end
20
+
21
+ def scan_pattern_hint(val)
22
+ case val
23
+ when Regexp then val
24
+ when String, Symbol then /^(?:.*?\W|)#{val}(?:\W)/
25
+ when nil then /.*/
26
+ else raise TypeError
27
+ end
28
+ end
29
+
30
+ def rscan(str, opts)
31
+ results = Scanner.process(str, opts) || []
32
+ return results if opts[:ignore_nested]
33
+ results.map do |outer|
34
+ inner = rscan(outer.sub(/^proc\s*(do|\{)/,''), opts.merge(:stop_on_newline => true))
35
+ [outer, inner]
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ module Sourcify
2
+ module Proc
3
+ class Parser #:nodoc:all
4
+ class Converter
5
+ class << self
6
+
7
+ RUBY_PARSER = RubyParser.new
8
+ RUBY_2_RUBY = Ruby2Ruby.new
9
+
10
+ def to_sexp(code, file = nil)
11
+ retried = false
12
+ begin
13
+ RUBY_PARSER.reset
14
+ (retried ? RubyParser.new : RUBY_PARSER).parse(*[code, file].compact)
15
+ rescue Racc::ParseError, SyntaxError
16
+ return nil if retried
17
+ retried = true; retry
18
+ end
19
+ end
20
+
21
+ def to_code(sexp)
22
+ RUBY_2_RUBY.process(Sexp.from_array(sexp.to_a))
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,43 @@
1
+ module Sourcify
2
+ module Proc
3
+ class Parser #:nodoc:all
4
+ class Normalizer
5
+ class << self
6
+
7
+ def process(sexp, binding)
8
+ @binding = binding
9
+ Sexp.from_array(fix_no_arg_method_calls(sexp.to_a))
10
+ end
11
+
12
+ def fix_no_arg_method_calls(array)
13
+ return array if [:class, :sclass, :defn, :module].include?(array[0])
14
+ array.map do |e|
15
+ if e.is_a?(Array)
16
+ no_arg_method_call?(e) or fix_no_arg_method_calls(e)
17
+ else
18
+ e
19
+ end
20
+ end
21
+ end
22
+
23
+ def no_arg_method_call?(e)
24
+ if like_no_arg_method_call?(e)
25
+ bounded_var?(var = e[2]) ? [:lvar, var] : e
26
+ end
27
+ end
28
+
29
+ def like_no_arg_method_call?(e)
30
+ e.size == 4 && e[0..1] == [:call, nil] &&
31
+ e[3] == [:arglist] && (var = e[2]).is_a?(Symbol)
32
+ end
33
+
34
+ def bounded_var?(var)
35
+ qvar = (@q ||= (IS_19x ? ":%s" : "'%s'")) % var
36
+ @binding.eval("local_variables.include?(#{qvar})")
37
+ end
38
+
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,33 @@
1
+ module Sourcify
2
+ module Proc
3
+ class Parser #:nodoc:all
4
+ class SourceCode < Struct.new(:file, :line)
5
+
6
+ def line
7
+ super.pred
8
+ end
9
+
10
+ def to_s
11
+ case file
12
+ when /\(irb\)/ then from_irb_to_s
13
+ else from_file_to_s
14
+ end
15
+ end
16
+
17
+ def from_file_to_s
18
+ File.open(file, 'r') do |fh|
19
+ fh.extend(File::Tail).forward(line)
20
+ fh.readlines.join
21
+ end
22
+ end
23
+
24
+ def from_irb_to_s
25
+ # Really owe it to Florian Groß's solution @ http://rubyquiz.com/quiz38.html ...
26
+ # anyway, note that we use *line.succ* instead of *line* here.
27
+ IRB.CurrentContext.io.line(line.succ .. -1).join
28
+ end
29
+
30
+ end
31
+ end
32
+ end
33
+ end
@@ -16,16 +16,7 @@ module Sourcify
16
16
  end
17
17
 
18
18
  def closed?
19
- # NOTE: The only real error is SyntaxError, other errors are due
20
- # to undefined variable or watever, which are perfectly ok when
21
- # testing for validity of the string.
22
- begin
23
- instance_eval(safe_contents) if evaluable?
24
- rescue SyntaxError
25
- false
26
- rescue Exception
27
- true
28
- end
19
+ evaluable? && parsable?
29
20
  end
30
21
 
31
22
  private
@@ -36,10 +27,20 @@ module Sourcify
36
27
  @contents[-1][-1].chr == end_tag
37
28
  end
38
29
 
30
+ def parsable?
31
+ begin
32
+ Parser::Converter.to_sexp <<-SOURCIFIED_HEREDOKIE.strip
33
+ #{safe_contents}
34
+ SOURCIFIED_HEREDOKIE
35
+ rescue Exception
36
+ true
37
+ end
38
+ end
39
+
39
40
  def safe_contents
40
41
  # NOTE: %x & ` strings are dangerous to eval cos they execute shell commands,
41
42
  # thus we convert them to normal strings 1st
42
- @contents.join.gsub(/(%x)(\W|\_)/, '%Q\2').gsub(/.{0,2}(`)/) do |s|
43
+ to_s.gsub(/(%x)(\W|\_)/, '%Q\2').gsub(/.{0,2}(`)/) do |s|
43
44
  s =~ /^(%Q|%W|%r|%x|.?%|.?\\)/ ? s : s.sub(/`$/,'%Q`')
44
45
  end
45
46
  end
@@ -8,6 +8,7 @@ module Sourcify
8
8
  module Extensions
9
9
 
10
10
  class Escape < Exception; end
11
+ RUBY_PARSER = RubyParser.new
11
12
 
12
13
  def process(data, opts={})
13
14
  begin
@@ -32,9 +33,7 @@ module Sourcify
32
33
 
33
34
  def push_dstring(ts, te)
34
35
  data = data_frag(ts .. te.pred)
35
- unless @dstring
36
- @dstring = DString.new(data[%r{^("|`|/|%(?:Q|W|r|x|)(?:\W|_))},1])
37
- end
36
+ @dstring ||= DString.new(data[%r{^("|`|/|%(?:Q|W|r|x|)(?:\W|_))},1])
38
37
  @dstring << data
39
38
  return true unless @dstring.closed?
40
39
  @tokens << [:dstring, @dstring.to_s]
@@ -104,6 +103,7 @@ module Sourcify
104
103
 
105
104
  def fix_counter_false_start(key)
106
105
  return unless this_counter(key).just_started?
106
+ return unless really_false_started?
107
107
  reset_attributes
108
108
  @tokens, @false_start_backup = @false_start_backup.dup, nil if @false_start_backup
109
109
  end
@@ -117,10 +117,12 @@ module Sourcify
117
117
  end
118
118
 
119
119
  def construct_result_code
120
+ code = <<-SOURCIFIED_HEREDOKIE.strip
121
+ proc #{codified_tokens}
122
+ SOURCIFIED_HEREDOKIE
123
+
120
124
  begin
121
- code = 'proc ' + codified_tokens
122
- eval(code) # TODO: any better way to check for completeness of proc code ??
123
- if @body_matcher.call(code)
125
+ if safe_eval(code) && @body_matcher.call(code)
124
126
  @results << code
125
127
  raise Escape if @stop_on_newline or @lineno != 1
126
128
  reset_attributes
@@ -143,6 +145,13 @@ module Sourcify
143
145
  @rejecting_block = false
144
146
  end
145
147
 
148
+ def really_false_started?
149
+ safe_eval(<<-SOURCIFIED_HEREDOKIE.strip
150
+ #{codified_tokens} 1}
151
+ SOURCIFIED_HEREDOKIE
152
+ ) && true
153
+ end
154
+
146
155
  def offset_attributes
147
156
  @lineno = 1 # Fixing JRuby's lineno bug (see http://jira.codehaus.org/browse/JRUBY-5014)
148
157
  unless @tokens.empty?
@@ -152,6 +161,10 @@ module Sourcify
152
161
  end
153
162
  end
154
163
 
164
+ def safe_eval(string)
165
+ Parser::Converter.to_sexp(string)
166
+ end
167
+
155
168
  end
156
169
  end
157
170
  end
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["NgTzeYang"]
12
- s.date = %q{2011-01-29}
12
+ s.date = %q{2011-02-06}
13
13
  s.description = %q{}
14
14
  s.email = %q{ngty77@gmail.com}
15
15
  s.extra_rdoc_files = [
@@ -19,6 +19,7 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".infinity_test",
22
+ ".rvmrc",
22
23
  "HISTORY.txt",
23
24
  "LICENSE",
24
25
  "README.rdoc",
@@ -31,6 +32,10 @@ Gem::Specification.new do |s|
31
32
  "lib/sourcify/proc/methods/to_sexp.rb",
32
33
  "lib/sourcify/proc/methods/to_source.rb",
33
34
  "lib/sourcify/proc/parser.rb",
35
+ "lib/sourcify/proc/parser/code_scanner.rb",
36
+ "lib/sourcify/proc/parser/converter.rb",
37
+ "lib/sourcify/proc/parser/normalizer.rb",
38
+ "lib/sourcify/proc/parser/source_code.rb",
34
39
  "lib/sourcify/proc/scanner.rb",
35
40
  "lib/sourcify/proc/scanner.rl",
36
41
  "lib/sourcify/proc/scanner/comment.rb",
@@ -162,15 +167,18 @@ Gem::Specification.new do |s|
162
167
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
163
168
  s.add_runtime_dependency(%q<ruby2ruby>, [">= 1.2.5"])
164
169
  s.add_runtime_dependency(%q<sexp_processor>, [">= 3.0.5"])
170
+ s.add_runtime_dependency(%q<file-tail>, [">= 1.0.5"])
165
171
  s.add_development_dependency(%q<bacon>, [">= 0"])
166
172
  else
167
173
  s.add_dependency(%q<ruby2ruby>, [">= 1.2.5"])
168
174
  s.add_dependency(%q<sexp_processor>, [">= 3.0.5"])
175
+ s.add_dependency(%q<file-tail>, [">= 1.0.5"])
169
176
  s.add_dependency(%q<bacon>, [">= 0"])
170
177
  end
171
178
  else
172
179
  s.add_dependency(%q<ruby2ruby>, [">= 1.2.5"])
173
180
  s.add_dependency(%q<sexp_processor>, [">= 3.0.5"])
181
+ s.add_dependency(%q<file-tail>, [">= 1.0.5"])
174
182
  s.add_dependency(%q<bacon>, [">= 0"])
175
183
  end
176
184
  end
@@ -26,6 +26,30 @@ describe "Proc#to_source from { ... } block (w nested hash)" do
26
26
  \)
27
27
  end
28
28
 
29
+ should 'handle method w only hash args' do
30
+ (
31
+ lambda {
32
+ test :a => 2
33
+ }
34
+ ).should.be having_source(%Q\
35
+ proc do
36
+ test(:a => 2)
37
+ end
38
+ \)
39
+ end
40
+
41
+ should 'handle method w mixed args' do
42
+ (
43
+ lambda {
44
+ test 1, :a => 2
45
+ }
46
+ ).should.be having_source(%Q\
47
+ proc do
48
+ test(1, :a => 2)
49
+ end
50
+ \)
51
+ end
52
+
29
53
  if RUBY_VERSION.include?('1.9.')
30
54
  require File.join(File.expand_path(File.dirname(__FILE__)), '19x_extras')
31
55
  behaves_like 'Proc#to_source from { ... } block (1.9.*)'
@@ -66,14 +66,15 @@ describe 'Proc#to_source w specified {:attached_to => ...}' do
66
66
  describe '>> w false start as a result of preceding hash' do
67
67
 
68
68
  option = {:attached_to => :watever}
69
+ aa = :aa
69
70
 
70
71
  should 'handle for do ... end block' do
71
- x = watever({:aa => 1, :bb => 3}) do :blah end
72
+ x = watever({aa => 1, :bb => 3}) do :blah end
72
73
  x.should.be having_source('proc { :blah }', option)
73
74
  end
74
75
 
75
76
  should 'handle for { ... } block' do
76
- x = watever({:aa => 1, :bb => 3}) { :blah }
77
+ x = watever({aa => 1, :bb => 3}) { :blah }
77
78
  x.should.be having_source('proc { :blah }', option)
78
79
  end
79
80
 
@@ -5,22 +5,6 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
5
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
6
  require 'sourcify'
7
7
 
8
- # ///////////////////////////////////////////////////////////
9
- # Regenerate ragel-based scanner
10
- # ///////////////////////////////////////////////////////////
11
-
12
- ragel_dir = File.join(File.dirname(__FILE__), '..', 'lib', 'sourcify', 'proc')
13
- ragel_file = File.join(ragel_dir, 'scanner.rl')
14
- ruby_file = File.join(ragel_dir, 'scanner.rb')
15
- File.delete(ruby_file) rescue nil
16
- system("ragel -R #{ragel_file}")
17
-
18
- begin
19
- require File.join(ragel_dir, 'scanner.rb')
20
- rescue LoadError
21
- raise $!
22
- end
23
-
24
8
  # ///////////////////////////////////////////////////////////
25
9
  # Bacon
26
10
  # ///////////////////////////////////////////////////////////
@@ -75,11 +59,9 @@ end
75
59
 
76
60
  def having_source(expected, opts={}, &matcher)
77
61
  lambda do |_proc|
78
- if block_given?
79
- normalize_code(_proc.to_source(&matcher)) == normalize_code(expected)
80
- else
81
- normalize_code(_proc.to_source(opts)) == normalize_code(expected)
82
- end
62
+ normalize_code(expected) # added for bug fixing
63
+ normalize_code(block_given? ? _proc.to_source(&matcher) : _proc.to_source(opts)) \
64
+ == normalize_code(expected)
83
65
  end
84
66
  end
85
67
 
@@ -117,3 +99,20 @@ def irb_exec(stdin_str)
117
99
  (values[-1].nil? && RUBY_VERSION.include?('1.9.2')) ? values[0 .. -2] : values
118
100
  end
119
101
 
102
+ # ///////////////////////////////////////////////////////////
103
+ # Regenerate ragel-based scanner
104
+ # ///////////////////////////////////////////////////////////
105
+
106
+ unless has_parsetree?
107
+ ragel_dir = File.join(File.dirname(__FILE__), '..', 'lib', 'sourcify', 'proc')
108
+ ragel_file = File.join(ragel_dir, 'scanner.rl')
109
+ ruby_file = File.join(ragel_dir, 'scanner.rb')
110
+ File.delete(ruby_file) rescue nil
111
+ system("ragel -R #{ragel_file}")
112
+
113
+ begin
114
+ require File.join(ragel_dir, 'scanner.rb')
115
+ rescue LoadError
116
+ raise $!
117
+ end
118
+ end
metadata CHANGED
@@ -1,13 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sourcify
3
3
  version: !ruby/object:Gem::Version
4
- hash: 13
5
4
  prerelease: false
6
5
  segments:
7
6
  - 0
8
7
  - 4
9
- - 1
10
- version: 0.4.1
8
+ - 2
9
+ version: 0.4.2
11
10
  platform: ruby
12
11
  authors:
13
12
  - NgTzeYang
@@ -15,7 +14,7 @@ autorequire:
15
14
  bindir: bin
16
15
  cert_chain: []
17
16
 
18
- date: 2011-01-30 00:00:00 +08:00
17
+ date: 2011-02-06 00:00:00 +08:00
19
18
  default_executable:
20
19
  dependencies:
21
20
  - !ruby/object:Gem::Dependency
@@ -26,7 +25,6 @@ dependencies:
26
25
  requirements:
27
26
  - - ">="
28
27
  - !ruby/object:Gem::Version
29
- hash: 21
30
28
  segments:
31
29
  - 1
32
30
  - 2
@@ -42,7 +40,6 @@ dependencies:
42
40
  requirements:
43
41
  - - ">="
44
42
  - !ruby/object:Gem::Version
45
- hash: 13
46
43
  segments:
47
44
  - 3
48
45
  - 0
@@ -58,7 +55,6 @@ dependencies:
58
55
  requirements:
59
56
  - - ">="
60
57
  - !ruby/object:Gem::Version
61
- hash: 29
62
58
  segments:
63
59
  - 1
64
60
  - 0
@@ -74,7 +70,6 @@ dependencies:
74
70
  requirements:
75
71
  - - ">="
76
72
  - !ruby/object:Gem::Version
77
- hash: 3
78
73
  segments:
79
74
  - 0
80
75
  version: "0"
@@ -105,6 +100,10 @@ files:
105
100
  - lib/sourcify/proc/methods/to_sexp.rb
106
101
  - lib/sourcify/proc/methods/to_source.rb
107
102
  - lib/sourcify/proc/parser.rb
103
+ - lib/sourcify/proc/parser/code_scanner.rb
104
+ - lib/sourcify/proc/parser/converter.rb
105
+ - lib/sourcify/proc/parser/normalizer.rb
106
+ - lib/sourcify/proc/parser/source_code.rb
108
107
  - lib/sourcify/proc/scanner.rb
109
108
  - lib/sourcify/proc/scanner.rl
110
109
  - lib/sourcify/proc/scanner/comment.rb
@@ -181,7 +180,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
181
180
  requirements:
182
181
  - - ">="
183
182
  - !ruby/object:Gem::Version
184
- hash: 59
185
183
  segments:
186
184
  - 1
187
185
  - 8
@@ -192,7 +190,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
192
190
  requirements:
193
191
  - - ">="
194
192
  - !ruby/object:Gem::Version
195
- hash: 3
196
193
  segments:
197
194
  - 0
198
195
  version: "0"