behold 0.0.0

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.
@@ -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: []