behold 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 37890fb8452e948a00eb6cb45722089f52ddf26510986f3c57be2b81d52ac72f
4
+ data.tar.gz: 9d2a93f2b5a7d23f925fdbe9b62252bdf7a2501a56ab5c8e53d9a73784ed5f2f
5
+ SHA512:
6
+ metadata.gz: 056f7dc6ba24fe4cf65bce11f2a151747be983be96a5209928821f60b4a330bb553085f82c6f11075444c45ce8039b5d0400464a02952dd7f7853161d99daf74
7
+ data.tar.gz: 9236da3dc3dd35003c4df75145eb8d1db1f353c078731a434301942d6b782c0716251bc699c4db8a64875deaf1e2575946d876d26ba7e85baf7e4bfcd91f5392
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2020 Shannon Skipper
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,65 @@
1
+ # Behold
2
+
3
+ ## Warning
4
+
5
+ *THIS IS UNSAFE, EXPERIMENTAL CODE.*
6
+
7
+ This gem runs arbitrary code and can be capriciously destructive. Be warned!
8
+
9
+ ## Installation
10
+
11
+ ```sh
12
+ gem install behold
13
+ ```
14
+
15
+ ## Examples
16
+
17
+ ```ruby
18
+ require 'behold'
19
+
20
+ Behold.call 5, 25
21
+ #=> [[:abs2], [:**, 2], [:pow, 2], [:*, 5], [:+, 20], [:lcm, 25]]
22
+
23
+ Behold.call 'hi', 'hi!'
24
+ #=> [[:+, "!"], [:<<, "!"], [:concat, "!"], [:<<, 33], [:concat, 33], [:insert, 2, "!"]]
25
+
26
+ Behold.call [1, 2, 3], '1,2,3'
27
+ #=> [[:*, ","], [:join, ","]]
28
+
29
+ Behold.call Object, 'Object'
30
+ #=> [[:inspect], [:to_s], [:name], [:class_name], [:pretty_print_inspect]]
31
+
32
+ Behold.call 1, 2
33
+ #=> [[:next], [:succ], [:<<, 1], [:+, 1], [:lcm, 2], [:*, 2]]
34
+
35
+ Behold.call 'BBQ', ['B', 'B', 'Q']
36
+ #=> [[:grapheme_clusters], [:chars], [:split, ""], [:lines, "B"], [:rpartition, "B"], [:split, //]]
37
+
38
+ puts Behold.code 'BBQ', ['B', 'B', 'Q']
39
+ #>> "BBQ".grapheme_clusters
40
+ #>> "BBQ".chars
41
+ #>> "BBQ".split("")
42
+ #>> "BBQ".lines("B")
43
+ #>> "BBQ".rpartition("B")
44
+ #>> "BBQ".split(//)
45
+ ```
46
+
47
+ ## Command Line Examples
48
+
49
+ ```sh
50
+ behold 21 42
51
+ #>> 21.<<(1)
52
+ #>> 21.lcm(2)
53
+ #>> 21.*(2)
54
+ #>> 21.lcm(6)
55
+ #>> 21.>>(-1.0)
56
+ #>> 21.<<(1.0)
57
+
58
+ behold 42 "'42'"
59
+ #>> 42.inspect
60
+ #>> 42.to_s
61
+ #>> 42.inspect(10)
62
+ #>> 42.to_s(10)
63
+ #>> 42.inspect(10)
64
+ #>> 42.to_s(10)
65
+ ```
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rake/testtask'
4
+
5
+ task default: %w[test]
6
+
7
+ Rake::TestTask.new do |spec|
8
+ spec.pattern = 'spec/*_spec.rb'
9
+ end
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'behold'
4
+
5
+ puts Behold.code eval(ARGV.shift), eval(ARGV.shift)
@@ -0,0 +1,120 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+ require 'timeout'
5
+ require_relative 'behold/arity_range'
6
+ require_relative 'behold/version'
7
+
8
+ module Behold
9
+ using ArityRange
10
+
11
+ FORBIDDEN = %i[__binding__ byebug debugger instance_eval pry
12
+ public_send __send__ send].freeze
13
+ EMPTY_FUZZ = [nil].freeze
14
+ FUZZ = [*0..10,
15
+ -1.0, 0.0, 1.0,
16
+ [], {},
17
+ Float::INFINITY, Float::NAN,
18
+ '', *' '..'~', ' ', ', ', "\n", "\t", "\r",
19
+ 'lowercase words', 'Capitalized Words', 'UPPERCASE WORDS',
20
+ //, /.+/, /\h+/,
21
+ :to_s, :to_i, :to_h, :to_a,
22
+ :join, :<<, :+, :-, :*, :inject, :new,
23
+ nil, true, false,
24
+ nil..nil, 1..10, 'a'..'f',
25
+ Time.now, Object, Module, Kernel,
26
+ *-10..-1, *11..101, *-101..11, 1_000, 10_000, -1_000, -10_000].freeze
27
+ DOUBLE_FUZZ = FUZZ.repeated_combination(2).to_a.freeze
28
+ FUZZES = [EMPTY_FUZZ, FUZZ, DOUBLE_FUZZ].freeze
29
+ RESULT_COUNT = 6
30
+ DEFAULT_TIMEOUT = 3
31
+
32
+ class << self
33
+ def code(from, to)
34
+ call(from, to).map do |meth, *args|
35
+ "#{from.inspect}.#{meth}#{"(#{args.map(&:inspect).join ', '})" unless args.empty?}"
36
+ end
37
+ end
38
+
39
+ def call(from, to)
40
+ black_hole do
41
+ lazy_tries = FUZZES.map.with_index do |fuzz, index|
42
+ match(from: from, to: to, fuzz: fuzz, arg_count: index)
43
+ end
44
+
45
+ best_matches(lazy_tries)
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def best_matches(lazy_tries, matches = [])
52
+ Timeout.timeout DEFAULT_TIMEOUT do
53
+ lazy_matches(lazy_tries).each { |match| matches << match }
54
+ end
55
+
56
+ matches
57
+ rescue Timeout::Error
58
+ matches
59
+ end
60
+
61
+ def match(from:, to:, fuzz:, arg_count:)
62
+ fuzz.lazy.flat_map do |args|
63
+ found = arg_methods(soft_dup(from), arg_count).select do |meth|
64
+ case arg_count
65
+ when 1
66
+ check_method(meth, args, from: soft_dup(from), to: to)
67
+ else
68
+ check_method(meth, *args, from: soft_dup(from), to: to)
69
+ end
70
+ end
71
+ found = found.with_object(args) if args
72
+
73
+ found.map { |*send_this| [*send_this].flatten }
74
+ end
75
+ end
76
+
77
+ def black_hole
78
+ File.open File::NULL, File::APPEND do |dev_null|
79
+ $stdout = $stderr = dev_null
80
+
81
+ yield
82
+ ensure
83
+ $stdout = STDOUT
84
+ $stderr = STDERR
85
+ end
86
+ end
87
+
88
+ def lazy_matches(matches)
89
+ matches.reduce(:+).lazy.take(RESULT_COUNT)
90
+ end
91
+
92
+ def check_method(meth, *args, from:, to:)
93
+ from.public_send(meth, *args) == to
94
+ rescue StandardError, SyntaxError
95
+ nil
96
+ end
97
+
98
+ def soft_dup(from)
99
+ duped_from = from.dup
100
+ return from unless from == from.dup
101
+
102
+ duped_from
103
+ rescue FrozenError
104
+ from
105
+ end
106
+
107
+ def arg_methods(object, args)
108
+ object.public_methods.select do |meth|
109
+ next if FORBIDDEN.include? meth
110
+
111
+ meth_arity_range = object.public_method(meth).arity_range
112
+ next unless meth_arity_range.fetch(:keywords).min.zero?
113
+
114
+ mandatory_args, allowable_args = meth_arity_range.fetch(:arguments).minmax
115
+
116
+ mandatory_args <= args && allowable_args >= args
117
+ end.lazy
118
+ end
119
+ end
120
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Behold
4
+ module ArityRange
5
+ refine Method do
6
+ def arity_range
7
+ args = parameters.map(&:first)
8
+ req = args.count :req
9
+ keyreq = args.count :keyreq
10
+ opt = args.include?(:rest) ? Float::INFINITY : args.count(:opt)
11
+ keyopt = args.include?(:keyrest) ? Float::INFINITY : args.count(:key)
12
+
13
+ {arguments: req..req + opt, keywords: keyreq..keyreq + keyopt}
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Behold
4
+ MAJOR_VERSION = 0
5
+ MINOR_VERSION = 0
6
+ TEENY_VERSION = 0
7
+
8
+ VERSION = "#{MAJOR_VERSION}.#{MINOR_VERSION}.#{TEENY_VERSION}"
9
+ end
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'helper'
4
+
5
+ ##
6
+ # TODO: Tests. These spiked tests arbitrarily fail since Behold writes its own assertions.
7
+ describe Behold do
8
+ describe 'calling' do
9
+ it 'works with numbers' do
10
+ assert_equal Behold.call(42, 42), [[:rationalize], [:ord], [:to_int], [:to_i], [:to_f], [:to_r]]
11
+ assert_equal Behold.call(42, 43), [[:next], [:succ], [:|, 1], [:+, 1], [:^, 1], [:|, 3]]
12
+ end
13
+
14
+ it 'works with strings' do
15
+ assert_equal Behold.call('shannon', 'Shannon'), [[:capitalize], [:capitalize!]]
16
+ end
17
+
18
+ it 'works with classes' do
19
+ assert_equal Behold.call(Object, 'Object'), [[:inspect], [:to_s], [:name]]
20
+ end
21
+ end
22
+
23
+ describe 'code' do
24
+ it 'works with numbers' do
25
+ assert_equal Behold.code(42, 42), ['42.rationalize', '42.ord', '42.to_int',
26
+ '42.to_i', '42.to_f', '42.to_r']
27
+ assert_equal Behold.code(42, 43), ['42.next', '42.succ', '42.|(1)', '42.+(1)',
28
+ '42.^(1)', '42.|(3)']
29
+ end
30
+
31
+ it 'works with strings' do
32
+ assert_equal Behold.code('shannon', 'Shannon'), ['"shannon".capitalize', '"shannon".capitalize!']
33
+ end
34
+
35
+ it 'works with classes' do
36
+ assert_equal Behold.code(Object, 'Object'), ["Object.inspect", "Object.to_s", "Object.name"]
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path '../lib', __dir__
4
+ $LOAD_PATH.prepend lib unless $LOAD_PATH.include? lib
5
+
6
+ require 'behold'
7
+ require 'minitest/autorun'
8
+ require 'minitest/hell'
9
+ require 'minitest/pride'
10
+
11
+ module Minitest
12
+ class Test
13
+ prove_it!
14
+ end
15
+ end
metadata ADDED
@@ -0,0 +1,96 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: behold
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Shannon Skipper
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2020-04-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-proveit
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13'
55
+ description: Behold!
56
+ email:
57
+ - shannonskipper@gmail.com
58
+ executables:
59
+ - behold
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - Gemfile
64
+ - LICENSE
65
+ - README.md
66
+ - Rakefile
67
+ - bin/behold
68
+ - lib/behold.rb
69
+ - lib/behold/arity_range.rb
70
+ - lib/behold/version.rb
71
+ - spec/behold_spec.rb
72
+ - spec/helper.rb
73
+ homepage: https://github.com/havenwood/behold
74
+ licenses:
75
+ - MIT
76
+ metadata: {}
77
+ post_install_message:
78
+ rdoc_options: []
79
+ require_paths:
80
+ - lib
81
+ required_ruby_version: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - ">="
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ required_rubygems_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: '0'
91
+ requirements: []
92
+ rubygems_version: 3.1.2
93
+ signing_key:
94
+ specification_version: 4
95
+ summary: I looked, and there before me was a gem.
96
+ test_files: []