xmp2assert 1

Sign up to get free protection for your applications and to get access to all the features.
data/exe/xmpcheck.rb ADDED
@@ -0,0 +1,75 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ abort "usage: #$0 FILES..." if ARGV.empty?
27
+
28
+ require 'test/unit/autorunner'
29
+ require 'pathname'
30
+ require 'xmp2assert'
31
+
32
+ class TC_Main < Test::Unit::TestCase
33
+ include XMP2Assert::Assertions
34
+
35
+ def self.expandfs argv
36
+ files = []
37
+ argv.each do |i|
38
+ p = Pathname.new i
39
+ if p.directory? then
40
+ files += expandfs p.children # recur
41
+ else
42
+ files << p
43
+ end
44
+ end
45
+
46
+ files.map! do |i|
47
+ XMP2Assert::Quasifile.new i
48
+ end
49
+
50
+ return files
51
+ end
52
+ private_class_method :expandfs
53
+
54
+ a = expandfs ARGV
55
+ p a
56
+ a.each do |f|
57
+ klass = XMP2Assert::Classifier.classify f
58
+
59
+ if klass.empty? then
60
+ test f.__FILE__ do
61
+ pend "no tests for #{f.__FILE__}"
62
+ end
63
+ else
64
+ test f.__FILE__ do
65
+ t, o = XMP2Assert::Converter.convert f
66
+ if klass.include? :'=>' then
67
+ t.eval binding
68
+ end
69
+ if klass.include? :'>>' then
70
+ assert_capture2e o, f
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,157 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'rbconfig'
27
+ require 'open3'
28
+ require 'tempfile'
29
+ require 'erb'
30
+ require 'test/unit'
31
+ require 'test/unit/assertions'
32
+ require 'test/unit/assertion-failed-error'
33
+ require_relative 'xmp2rexp'
34
+
35
+ # Helper module that implements assertions.
36
+ module XMP2Assert::Assertions
37
+ include Test::Unit::Assertions
38
+ include XMP2Assert::XMP2Rexp
39
+
40
+ # Run a ruby script and assert for its output.
41
+ #
42
+ # ```ruby
43
+ # assert_capture2e "foo\n", Quasifile.new("puts 'foo'")
44
+ # ```
45
+ #
46
+ # @param expected [String] expected output.
47
+ # @param script [Quasifile] a ruby script.
48
+ # @param message [String] extra failure message.
49
+ # @param rubyopts [Array<String>] extra opts to pass to ruby process.
50
+ # @param opts [Hash{Symbol=>Object}] extra opts to pass to spawn.
51
+ # @note
52
+ # As the method name implies the assertion is against both stdin and stderr
53
+ # at once. This is for convenience.
54
+ def assert_capture2e expected, script, message = nil, rubyopts: nil, **opts
55
+ actual, _ = ruby script, rubyopts: rubyopts, **opts
56
+ actual.force_encoding expected.encoding
57
+ return assert_xmp_raw expected, actual, message
58
+ end
59
+
60
+ # Assert if the given expression is in the same form of xmp.
61
+ #
62
+ # ```ruby
63
+ # assert_xmp '#<Object:0x007f896c9b49c8>', Object.new
64
+ # ```
65
+ #
66
+ # @param xmp [String] expected pattern of inspect.
67
+ # @param expr [Object] object to check.
68
+ # @param message [String] extra failure message.
69
+ def assert_xmp xmp, expr, message = nil
70
+ assert_xmp_raw xmp, expr.inspect, message
71
+ end
72
+
73
+ private
74
+
75
+ # :TODO: is it private?
76
+ def assert_xmp_raw xmp, actual, message = nil
77
+ msg = genmsg xmp, actual, message
78
+ expected = xmp2rexp xmp
79
+
80
+ raise unless expected.match actual
81
+ rescue
82
+ # Regexp#match can raise. That should also be a failure.
83
+ ix = Test::Unit::Assertions::AssertionMessage.convert xmp
84
+ ia = Test::Unit::Assertions::AssertionMessage.convert actual
85
+ ex = Test::Unit::AssertionFailedError.new(msg,
86
+ expected: xmp,
87
+ actual: actual,
88
+ inspected_expected: ix,
89
+ inspected_actual: ia,
90
+ user_message: message)
91
+ raise ex
92
+ else
93
+ return self # or...?
94
+ end
95
+
96
+ # We support pre-&. versions
97
+ def try obj, msg
98
+ return obj.send msg
99
+ rescue NoMethodError
100
+ return nil
101
+ end
102
+
103
+ def genmsg x, y, z = nil
104
+ diff = Test::Unit::Assertions::AssertionMessage.delayed_diff x, y
105
+ if try(x, :encoding) != try(y, :encoding) then
106
+ fmt = "<?>(?) expected but was\n<?>(?).?"
107
+ argv = [x, x.encoding.name, y, y.encoding.name, diff]
108
+ else
109
+ fmt = "<?> expected but was\n<?>.?"
110
+ argv = [x, y, diff]
111
+ end
112
+ return Test::Unit::Assertions::AssertionMessage.new z, fmt, argv
113
+ end
114
+
115
+ def erb
116
+ unless defined? @@erb
117
+ myself = Pathname.new __FILE__
118
+ path = myself + '../template.erb'
119
+ src = path.read mode: 'rb:binary:binary'
120
+ @@erb = ERB.new src, nil, '%-'
121
+ @@erb.filename = path.realpath.to_path if defined? $DEBUG
122
+ end
123
+ return @@erb
124
+ end
125
+
126
+ def empty_binding
127
+ # This `eval 'binding'` does not return the current binding but creates one
128
+ # on top of it. To make it really empty, this method has to have zero
129
+ # arity, and zero local variables.
130
+ return eval 'binding'
131
+ end
132
+
133
+ def empty_binding_with hash
134
+ return empty_binding.tap do |b|
135
+ hash.each_pair do |k, v|
136
+ b.local_variable_set k, v
137
+ end
138
+ end
139
+ end
140
+
141
+ def ruby script, rubyopts: nil, **opts
142
+ Tempfile.create '' do |f|
143
+ b = empty_binding_with script: script
144
+ s = erb.result b
145
+ f.write s
146
+ argv = [RbConfig.ruby, rubyopts, f.path]
147
+ if defined? ENV['BUNDLE_BIN_PATH']
148
+ argv = [ENV['BUNDLE_BIN_PATH'], 'exec'] + argv
149
+ end
150
+ argv.flatten!
151
+ argv.compact!
152
+ f.flush
153
+ # STDERR.puts(f.path) ; sleep # for debug
154
+ return Open3.capture2e(*argv, binmode: true, **opts)
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,71 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'ripper'
27
+
28
+ # Usually, you want to check LOTS of files that may or may not contain xmp
29
+ # comments at once, maybe from inside of a CI process. That's OK but we want
30
+ # to speed things up, so here we filter out files that are not necessary to
31
+ # convert.
32
+ #
33
+ # Typical usage:
34
+ #
35
+ # ```ruby
36
+ # Pathname.glob('**/*.rb').select do |f|
37
+ # XMP2Assert::Classifier.classify(f)
38
+ # end
39
+ # ```
40
+ class XMP2Assert::Classifier < Ripper
41
+ private_class_method :new
42
+
43
+ # @param qfile [Quasifile] file-ish
44
+ # @return [<Symbol>] either empty, :=>, :>>, or both.
45
+ # @note syntax error results in empty return value.
46
+ def self.classify qfile
47
+ case qfile when XMP2Assert::Quasifile then
48
+ this = new qfile.read, qfile.__FILE__, qfile.__LINE__
49
+ return this.send :parse
50
+ else
51
+ q = XMP2Assert::Quasifile.new qfile
52
+ return classify q
53
+ end
54
+ end
55
+
56
+ private
57
+
58
+ def parse
59
+ @ret = []
60
+ super
61
+ return @ret
62
+ end
63
+
64
+ def on_comment tok
65
+ case tok
66
+ when /^# =>/ then @ret |= [:'=>']
67
+ when /^# >>/ then @ret |= [:'>>']
68
+ when /^# ~>/ then @ret |= [:'>>']
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,113 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'ripper'
27
+ require 'uuid'
28
+
29
+ class XMP2Assert::Converter < Ripper
30
+ private_class_method :new
31
+
32
+ def self.convert qfile
33
+ case qfile when XMP2Assert::Quasifile then
34
+ this = new qfile.read, qfile.__FILE__, qfile.__LINE__
35
+ s, o = this.send :convert
36
+ r = XMP2Assert::Quasifile.new s, qfile.__FILE__, qfile.__LINE__
37
+ return r, o
38
+ else
39
+ q = XMP2Assert::Quasifile.new qfile
40
+ return convert q
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def pos
47
+ return [filename, lineno, column]
48
+ end
49
+
50
+ def convert
51
+ @ret = []
52
+ @xmps = []
53
+ @last_seen_xmp = nil
54
+ @outputs = String.new
55
+ parse
56
+ postprocess
57
+ return @ret.join, @outputs
58
+ end
59
+
60
+ def postprocess
61
+ @ret.sort!
62
+ @ret.map! do |(_, xmp, tok)|
63
+ if xmp
64
+ n = UUID.create_sha1 tok, Namespace
65
+ n = n.to_uri
66
+ n.gsub! %r/[:-]/, '_'
67
+ sprintf ".tap {|%s| assert_xmp(%s, %s) }\n", n, tok.chomp.dump, n
68
+ else
69
+ tok
70
+ end
71
+ end
72
+ end
73
+
74
+ Namespace = UUID.create_random
75
+ private_constant :Namespace
76
+
77
+ def on_comment tok
78
+ xmp = false
79
+ case tok
80
+ when /^\# [~>]> (.+\n)/ then
81
+ @outputs << $1
82
+ when /^\# => (.+\n)/ then
83
+ if @last_seen_xmp
84
+ @last_seen_xmp << $1
85
+ tok = "#\n"
86
+ else
87
+ tok = @last_seen_xmp = $1
88
+ xmp = true
89
+ end
90
+ else
91
+ @last_seen_xmp = nil
92
+ end
93
+ @ret << [pos, xmp, tok]
94
+ end
95
+
96
+ def on_sp tok
97
+ # no reset @last_seen_xmp
98
+ @ret << [pos, false, tok]
99
+ end
100
+
101
+ def on_scanner_event tok
102
+ @last_seen_xmp = nil
103
+ @ret << [pos, false, tok]
104
+ end
105
+
106
+ pim = private_instance_methods false
107
+ SCANNER_EVENTS.each do |e|
108
+ m = :"on_#{e}"
109
+ unless pim.include? m then
110
+ alias_method m, :on_scanner_event
111
+ end
112
+ end
113
+ end
@@ -0,0 +1,50 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: false -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'pp'
27
+
28
+ # By including this module your class gets a {#inspect} method which uses
29
+ # {::PP} methods to control outputs. You don't have to worry about redefining
30
+ # both. Just define your {#pretty_print} and that should also work for
31
+ # inspection.
32
+ module XMP2Assert::PrettierInspect
33
+
34
+ # (see Object#inspect)
35
+ #
36
+ # Prettier inspection.
37
+ def inspect
38
+ str = PP.pp self, '', Float::INFINITY
39
+ str.chomp!
40
+ return str
41
+ end
42
+
43
+ # System-provided pretty print honors {#inspect} when present but we don't
44
+ # like that. Force it behave as if inspect wasn't there.
45
+ #
46
+ # @param pp [PP] pp.
47
+ def pretty_print pp
48
+ pp.pp_object self
49
+ end
50
+ end
@@ -0,0 +1,144 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+
26
+ require 'open-uri'
27
+ require 'pathname'
28
+
29
+ # XMP2Assert converts a ruby script into a test file but we want to hold
30
+ # original path name / line number for diagnostic purposes. So this class.
31
+ class XMP2Assert::Quasifile
32
+ include XMP2Assert::PrettierInspect
33
+
34
+ # @return [Quasifile] a new quasifile.
35
+ #
36
+ # @overload new(qfile)
37
+ # Just return the given object (for possible recursive calls).
38
+ #
39
+ # @param qfile [Quasifile] an instance of this class.
40
+ # @return [Quasifile] identical to the argunemt.
41
+ #
42
+ # @overload new(uri, file = uri.to_s, line = 1)
43
+ # Obtains the resource pointed by the URI, parses the resource as a ruby
44
+ # script, and constructs a quasifile according to that.
45
+ #
46
+ # @param uri [URI] a URI of a ruby script.
47
+ # @param file [String] file path.
48
+ # @param line [Integer] line offset.
49
+ # @return [Quasifile] generated quasifile.
50
+ # @raise [OpenURI::HTTPError] 404 and such.
51
+ #
52
+ # @overload new(path, file = path.to_path, line = 1)
53
+ # Same as uri version, but accepts a pathname instead.
54
+ #
55
+ # @param path [#to_path] a pathname that points to a ruby script.
56
+ # @param file [String] file path.
57
+ # @param line [Integer] line offset.
58
+ # @return [Quasifile] generated qiasifile.
59
+ # @raise [Errno::ENOENT] not found.
60
+ # @raise [Errno::EISDIR] path is directory.
61
+ # @raise [Errno::EACCESS] permission denied.
62
+ # @raise [Errno::ELOOP] infinite symlink.
63
+ #
64
+ # @overload new(io, file = '(eval)', line = io.lineno + 1)
65
+ # Same as pathname version, but it also directly accepts arbitrary IO
66
+ # instances to read ruby scripts from. It migth be handy for you to pass a
67
+ # pipe here. The script filename may or may not be inferred depending on
68
+ # the IO (Files might be able to, Sockets hardly likely). Failures in
69
+ # filename resolution do not render exceptions. Rather the info lacks
70
+ # silently.
71
+ #
72
+ # @param io [#to_io] an IO that can be read.
73
+ # @param file [String] file path.
74
+ # @param line [Integer] line offset.
75
+ # @return [Quasifile] generated qiasifile.
76
+ # @raise [IOError] io not open for read, already closed, etc.
77
+ #
78
+ # @overload new(str, file = '(eval)', line = 1)
79
+ # Same as io version, but it also directly accepts a ruby script as a
80
+ # string. Obviously in this case, you cannot infer its filename.
81
+ #
82
+ # @param str [#to_io] a content of a ruby script.
83
+ # @param file [String] file path.
84
+ # @param line [Integer] line offset.
85
+ # @return [Quasifile] generated qiasifile.
86
+ #
87
+ def self.new(obj, file = nil, line = nil)
88
+ case
89
+ when src = switch { obj.to_str } then # LIKELY
90
+ return allocate.tap do |ret|
91
+ ret.send(:initialize, src, file||'(eval)', line||1)
92
+ end
93
+ when self === obj then return obj
94
+ when OpenURI::OpenRead === obj then src, path = obj.read, obj.to_s
95
+ when path = switch { obj.to_path } then src = obj.read
96
+ when io = switch { obj.to_io } then off, src = io.lineno+1, io.read
97
+ when src = switch { obj.read } then # unknown class but works
98
+ else
99
+ raise TypeError, "something readable expected but given: #{obj.class}"
100
+ end
101
+
102
+ return new(src, file || path, line || off) # recur
103
+ end
104
+
105
+ def self.switch
106
+ return yield
107
+ rescue NoMethodError
108
+ return nil
109
+ end
110
+ private_class_method :switch
111
+
112
+ attr_reader :__FILE__ # @return [String] file name of this script.
113
+ attr_reader :__LINE__ # @return [Integer] line offset.
114
+ attr_reader :__ENCODING__ # @return [Encoding] script encoding.
115
+ attr_reader :read # @return [String] content of the ruby script.
116
+
117
+ # @param content [String] a content of a ruby script.
118
+ # @param file [String] file path.
119
+ # @param line [Integer] line offset.
120
+ def initialize(content, file, line)
121
+ @__FILE__ = file
122
+ @__LINE__ = line
123
+ @__ENCODING__ = content.encoding
124
+ @read = content
125
+ end
126
+
127
+ # Eavluate the content script
128
+ # @param b [Binding] target binding (default toplevel).
129
+ # @return anything that the content evaluates.
130
+ def eval b = TOPLEVEL_BINDING
131
+ Kernel.eval @read, b, @__FILE__, @__LINE__
132
+ end
133
+
134
+ unless $DEBUG
135
+ # @!group Inspection
136
+
137
+ # For pretty print
138
+ def pretty_print_instance_variables
139
+ return %w'@__FILE__ @__LINE__'
140
+ end
141
+
142
+ # @!endgroup
143
+ end
144
+ end
@@ -0,0 +1,37 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; <%=
3
+ "coding: #{script.__ENCODING__}"
4
+ %>; indent-tabs-mode: nil; ruby-indent-level: 2 -*-
5
+ %#> # <- :HACK: emacs font-lock
6
+ # -*- frozen_string_literal: true -*-
7
+ # -*- warn_indent: true -*-
8
+
9
+ # Copyright (c) 2017 Urabe, Shyouhei
10
+ #
11
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ # of this software and associated documentation files (the "Software"), to deal
13
+ # in the Software without restriction, including without limitation the rights
14
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ # copies of the Software, and to permit persons to whom the Software is
16
+ # furnished to do so, subject to the following conditions:
17
+ #
18
+ # The above copyright notice and this permission notice shall be
19
+ # included in all copies or substantial portions of the Software.
20
+ #
21
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
27
+ # SOFTWARE.
28
+
29
+ file = "<%= script.__FILE__ %>" #=
30
+ line = <%= script.__LINE__ %> #=
31
+ src = ::DATA.read
32
+ eval src, binding, file, line
33
+
34
+ # Below is a generated software, sourced from <%= script.__FILE__ %>.
35
+ # Above copyright notice does not apply any further. Consult the original.
36
+ __END__
37
+ <%= script.read.dup.force_encoding('binary') -%>
@@ -0,0 +1,27 @@
1
+ #! /your/favourite/path/to/ruby
2
+ # -*- mode: ruby; coding: utf-8; indent-tabs-mode: nil; ruby-indent-level 2 -*-
3
+ # -*- frozen_string_literal: true -*-
4
+ # -*- warn_indent: true -*-
5
+
6
+ # Copyright (c) 2017 Urabe, Shyouhei
7
+ #
8
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
9
+ # of this software and associated documentation files (the "Software"), to deal
10
+ # in the Software without restriction, including without limitation the rights
11
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12
+ # copies of the Software, and to permit persons to whom the Software is
13
+ # furnished to do so, subject to the following conditions:
14
+ #
15
+ # The above copyright notice and this permission notice shall be
16
+ # included in all copies or substantial portions of the Software.
17
+ #
18
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24
+ # SOFTWARE.
25
+ ;
26
+
27
+ XMP2Assert::VERSION = 1