boca-golf 0.0.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.
- data/.gitignore +3 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +20 -0
- data/Rakefile +8 -0
- data/bin/boca-golf +7 -0
- data/boca-golf.gemspec +23 -0
- data/lib/boca-golf.rb +1 -0
- data/lib/boca_golf/checker.rb +11 -0
- data/lib/boca_golf/command_line.rb +21 -0
- data/lib/boca_golf/gist.rb +60 -0
- data/lib/boca_golf/result.rb +7 -0
- data/lib/boca_golf/scorer.rb +7 -0
- data/lib/boca_golf/version.rb +3 -0
- data/lib/boca_golf.rb +20 -0
- data/spec/boca_golf_spec.rb +27 -0
- data/spec/checker_spec.rb +23 -0
- data/spec/command_line_spec.rb +27 -0
- data/spec/gist_spec.rb +60 -0
- data/spec/infrastructure/reverse_specs/spec.rb +13 -0
- data/spec/scorer_spec.rb +14 -0
- data/spec/spec_helper.rb +29 -0
- metadata +94 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
GEM
|
2
|
+
remote: http://rubygems.org/
|
3
|
+
specs:
|
4
|
+
diff-lcs (1.1.2)
|
5
|
+
fakeweb (1.3.0)
|
6
|
+
rspec (2.3.0)
|
7
|
+
rspec-core (~> 2.3.0)
|
8
|
+
rspec-expectations (~> 2.3.0)
|
9
|
+
rspec-mocks (~> 2.3.0)
|
10
|
+
rspec-core (2.3.1)
|
11
|
+
rspec-expectations (2.3.0)
|
12
|
+
diff-lcs (~> 1.1.2)
|
13
|
+
rspec-mocks (2.3.0)
|
14
|
+
|
15
|
+
PLATFORMS
|
16
|
+
ruby
|
17
|
+
|
18
|
+
DEPENDENCIES
|
19
|
+
fakeweb
|
20
|
+
rspec
|
data/Rakefile
ADDED
data/bin/boca-golf
ADDED
data/boca-golf.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "boca_golf/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'boca-golf'
|
7
|
+
s.version = BocaGolf::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["David Vollbracht"]
|
10
|
+
s.email = ["david@flipstone.com"]
|
11
|
+
s.homepage = "https://github.com/flipstone/boca-golf"
|
12
|
+
s.summary = %q{A simple rspec-based engine for playing ruby golf coding problems}
|
13
|
+
s.description = %q{boca-golf with securely load and run from the filesystem or a gist url,
|
14
|
+
execute a user-specified set of rspec examples against it, and also print of the score
|
15
|
+
(# number of charaters - lower == better!). It was built for playing ruby golf at the
|
16
|
+
Boca Ruby Meetup sessions.
|
17
|
+
}
|
18
|
+
|
19
|
+
s.files = `git ls-files`.split("\n")
|
20
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
21
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
22
|
+
s.require_paths = ["lib"]
|
23
|
+
end
|
data/lib/boca-golf.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'boca_golf'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
class BocaGolf
|
2
|
+
class CommandLine
|
3
|
+
def run(args, stdout, stderr)
|
4
|
+
::Rspec::Core::Runner.disable_autorun!
|
5
|
+
|
6
|
+
gist_location, *specs = *args
|
7
|
+
stdout.puts "Testing #{gist_location} against specs:"
|
8
|
+
specs.each do |spec|
|
9
|
+
stdout.puts " - #{spec}"
|
10
|
+
end
|
11
|
+
stdout.puts
|
12
|
+
|
13
|
+
result = BocaGolf.new.run args, stdout, stderr
|
14
|
+
|
15
|
+
stdout.puts "\nCode:"
|
16
|
+
stdout.puts result.gist.code
|
17
|
+
stdout.puts "\nScore:"
|
18
|
+
stdout.puts result.score
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
class BocaGolf
|
2
|
+
class Gist
|
3
|
+
attr_reader :code
|
4
|
+
|
5
|
+
URI_REGEXP = URI.regexp(['http', 'https', 'ftp'])
|
6
|
+
|
7
|
+
def self.load_from_location(location)
|
8
|
+
if location =~ URI_REGEXP
|
9
|
+
load_from_url location
|
10
|
+
else
|
11
|
+
load_from_file location
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.load_from_url(gist_url)
|
16
|
+
new URI.parse(gist_url + ".txt").read
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.load_from_file(file)
|
20
|
+
new File.read(file)
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(code)
|
24
|
+
@code = code
|
25
|
+
end
|
26
|
+
|
27
|
+
def safe_module
|
28
|
+
Module.new.tap do |m|
|
29
|
+
m.send :include, insecure_module
|
30
|
+
m.send :include, proxy_module(insecure_module)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
protected
|
35
|
+
|
36
|
+
def insecure_module
|
37
|
+
-> do
|
38
|
+
$SAFE = 4
|
39
|
+
Module.new.tap do |m|
|
40
|
+
m.module_eval code
|
41
|
+
end
|
42
|
+
end.call
|
43
|
+
end
|
44
|
+
|
45
|
+
def proxy_module(mod)
|
46
|
+
Module.new.tap do |proxy|
|
47
|
+
mod.instance_methods.each do |method|
|
48
|
+
proxy.module_eval %{
|
49
|
+
def #{method}(*args, &block)
|
50
|
+
-> do
|
51
|
+
$SAFE = 4
|
52
|
+
super
|
53
|
+
end.call
|
54
|
+
end
|
55
|
+
}
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/lib/boca_golf.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'rspec'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
require 'boca_golf/checker'
|
6
|
+
require 'boca_golf/command_line'
|
7
|
+
require 'boca_golf/gist'
|
8
|
+
require 'boca_golf/result'
|
9
|
+
require 'boca_golf/scorer'
|
10
|
+
|
11
|
+
class BocaGolf
|
12
|
+
def run(args, stdout, stderr)
|
13
|
+
gist_location, *rspec_args = args
|
14
|
+
gist = Gist.load_from_location(gist_location)
|
15
|
+
passed = Checker.new.run gist, rspec_args, stdout, stderr
|
16
|
+
score = Scorer.new.score gist
|
17
|
+
|
18
|
+
Result.new gist, passed, score
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BocaGolf do
|
4
|
+
it "passes if all specs passed" do
|
5
|
+
run_specs_on_gist("def reverse(a) a.reverse; end").should be_passed
|
6
|
+
end
|
7
|
+
|
8
|
+
it "doesn't pass if not all specs pass" do
|
9
|
+
run_specs_on_gist("def reverse(a) a; end").should_not be_passed
|
10
|
+
end
|
11
|
+
|
12
|
+
it "scores the gist" do
|
13
|
+
run_specs_on_gist("def reverse(a) a.reverse; end")
|
14
|
+
.score.should == 29
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_specs_on_gist(gist)
|
18
|
+
gist.taint
|
19
|
+
gist.untrust
|
20
|
+
|
21
|
+
FakeWeb.register_uri :get, "https://gist.github.com/746166.txt", body: gist
|
22
|
+
stdout, stderr = StringIO.new, StringIO.new
|
23
|
+
sandboxed do
|
24
|
+
BocaGolf.new.run(["https://gist.github.com/746166", "spec/infrastructure/reverse_specs/spec.rb"], stdout, stderr)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BocaGolf::Checker do
|
4
|
+
it "returns true if all specs pass" do
|
5
|
+
run_specs_on_gist("def reverse(a) a.reverse; end").should be_true
|
6
|
+
end
|
7
|
+
|
8
|
+
it "returns false if some specs fail" do
|
9
|
+
run_specs_on_gist("def reverse(a) a; end").should be_false
|
10
|
+
end
|
11
|
+
|
12
|
+
it "doesn't make methods available everywhere" do
|
13
|
+
run_specs_on_gist "def foobar(a) a; end"
|
14
|
+
-> { foobar '1' }.should raise_error NoMethodError
|
15
|
+
end
|
16
|
+
|
17
|
+
def run_specs_on_gist(code)
|
18
|
+
stdout, stderr = StringIO.new, StringIO.new
|
19
|
+
sandboxed do
|
20
|
+
BocaGolf::Checker.new.run(BocaGolf::Gist.new(code), ["spec/infrastructure/reverse_specs/spec.rb"], stdout, stderr)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BocaGolf::CommandLine do
|
4
|
+
it "prints the expected result" do
|
5
|
+
gist = "def reverse(a) a.reverse; end"
|
6
|
+
FakeWeb.register_uri :get, "https://gist.github.com/746166.txt", body: gist
|
7
|
+
stdout, stderr = StringIO.new, StringIO.new
|
8
|
+
|
9
|
+
sandboxed do
|
10
|
+
BocaGolf::CommandLine.new.run(["https://gist.github.com/746166", "spec/infrastructure/reverse_specs/spec.rb"], stdout, stderr)
|
11
|
+
end
|
12
|
+
|
13
|
+
stdout.string.should =~ %r|Testing https://gist.github.com/746166 against specs:
|
14
|
+
- spec/infrastructure/reverse_specs/spec.rb
|
15
|
+
|
16
|
+
\.\.\.
|
17
|
+
|
18
|
+
Finished in \d\.\d+ seconds
|
19
|
+
3 examples, 0 failures
|
20
|
+
|
21
|
+
Code:
|
22
|
+
def reverse\(a\) a.reverse; end
|
23
|
+
|
24
|
+
Score:
|
25
|
+
29|
|
26
|
+
end
|
27
|
+
end
|
data/spec/gist_spec.rb
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative 'spec_helper'
|
2
|
+
|
3
|
+
describe BocaGolf::Gist do
|
4
|
+
describe "safe_module" do
|
5
|
+
it "creates a module to call methods" do
|
6
|
+
o = Object.new
|
7
|
+
o.extend BocaGolf::Gist.new("def foobar(a) a*2; end").safe_module
|
8
|
+
o.foobar(3).should == 6
|
9
|
+
end
|
10
|
+
|
11
|
+
it "evals code at safe level 4" do
|
12
|
+
-> do
|
13
|
+
BocaGolf::Gist.new(%{
|
14
|
+
def reverse(a) a.reverse; end;
|
15
|
+
class ::Object; def foo() end; end
|
16
|
+
}).safe_module
|
17
|
+
end.should raise_error(SecurityError)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "calls methods at safe level 4" do
|
21
|
+
o = Object.new.tap do |o|
|
22
|
+
o.extend BocaGolf::Gist.new(%{
|
23
|
+
def foo
|
24
|
+
::Object.class_eval { def bar() end }
|
25
|
+
end
|
26
|
+
}).safe_module
|
27
|
+
end
|
28
|
+
|
29
|
+
-> { o.foo }.should raise_error(SecurityError)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
describe "load_from_url" do
|
34
|
+
it "requests the .txt version of gist" do
|
35
|
+
code = "def a(); end"
|
36
|
+
FakeWeb.register_uri :get, "https://gist.github.com/746166.txt", body: code
|
37
|
+
BocaGolf::Gist.load_from_url("https://gist.github.com/746166").code.should == code
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "load_from_file" do
|
42
|
+
it "reads the file from disk" do
|
43
|
+
File.should_receive(:read).with("/foo/bar.rb").and_return(code = "def a(); end")
|
44
|
+
BocaGolf::Gist.load_from_file("/foo/bar.rb").code.should == code
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "load_from_location" do
|
49
|
+
it "loads from url when argument is a valid url" do
|
50
|
+
code = "def a(); end"
|
51
|
+
FakeWeb.register_uri :get, "https://gist.github.com/746166.txt", body: code
|
52
|
+
BocaGolf::Gist.load_from_location("https://gist.github.com/746166").code.should == code
|
53
|
+
end
|
54
|
+
|
55
|
+
it "loads from file when argument is not a full url" do
|
56
|
+
File.should_receive(:read).with("/foo/bar.rb").and_return(code = "def a(); end")
|
57
|
+
BocaGolf::Gist.load_from_location("/foo/bar.rb").code.should == code
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
describe "reverse" do
|
2
|
+
it "preserves length" do
|
3
|
+
reverse("a").length.should == 1
|
4
|
+
end
|
5
|
+
|
6
|
+
it "makes the first character the last" do
|
7
|
+
reverse("abc").chars.to_a.last.should == "a"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "makes the last character first" do
|
11
|
+
reverse("abc").chars.to_a.first.should == "c"
|
12
|
+
end
|
13
|
+
end
|
data/spec/scorer_spec.rb
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require_relative 'spec_helper'
|
3
|
+
|
4
|
+
describe BocaGolf::Scorer do
|
5
|
+
describe "score" do
|
6
|
+
it "returns the length of code" do
|
7
|
+
subject.score(BocaGolf::Gist.new("abc123")).should == 6
|
8
|
+
end
|
9
|
+
|
10
|
+
it "returns the counts characters rather than bytes" do
|
11
|
+
subject.score(BocaGolf::Gist.new("abc♥")).should == 4
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'fakeweb'
|
3
|
+
|
4
|
+
require 'boca_golf'
|
5
|
+
|
6
|
+
FakeWeb.allow_net_connect = false
|
7
|
+
|
8
|
+
module RSpecTestHelper
|
9
|
+
def sandboxed(&block)
|
10
|
+
begin
|
11
|
+
@orig_config = RSpec.configuration
|
12
|
+
@orig_world = RSpec.world
|
13
|
+
|
14
|
+
new_config = RSpec::Core::Configuration.new
|
15
|
+
new_world = RSpec::Core::World.new(new_config)
|
16
|
+
|
17
|
+
RSpec.instance_variable_set(:@configuration, new_config)
|
18
|
+
RSpec.instance_variable_set(:@world, new_world)
|
19
|
+
yield
|
20
|
+
ensure
|
21
|
+
RSpec.instance_variable_set(:@configuration, @orig_config)
|
22
|
+
RSpec.instance_variable_set(:@world, @orig_world)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
RSpec.configure do |c|
|
28
|
+
c.include RSpecTestHelper
|
29
|
+
end
|
metadata
ADDED
@@ -0,0 +1,94 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: boca-golf
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 0
|
8
|
+
- 1
|
9
|
+
version: 0.0.1
|
10
|
+
platform: ruby
|
11
|
+
authors:
|
12
|
+
- David Vollbracht
|
13
|
+
autorequire:
|
14
|
+
bindir: bin
|
15
|
+
cert_chain: []
|
16
|
+
|
17
|
+
date: 2011-01-06 00:00:00 -05:00
|
18
|
+
default_executable:
|
19
|
+
dependencies: []
|
20
|
+
|
21
|
+
description: "boca-golf with securely load and run from the filesystem or a gist url,\n\
|
22
|
+
execute a user-specified set of rspec examples against it, and also print of the score\n\
|
23
|
+
(# number of charaters - lower == better!). It was built for playing ruby golf at the\n\
|
24
|
+
Boca Ruby Meetup sessions.\n "
|
25
|
+
email:
|
26
|
+
- david@flipstone.com
|
27
|
+
executables:
|
28
|
+
- boca-golf
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files: []
|
32
|
+
|
33
|
+
files:
|
34
|
+
- .gitignore
|
35
|
+
- Gemfile
|
36
|
+
- Gemfile.lock
|
37
|
+
- Rakefile
|
38
|
+
- bin/boca-golf
|
39
|
+
- boca-golf.gemspec
|
40
|
+
- lib/boca-golf.rb
|
41
|
+
- lib/boca_golf.rb
|
42
|
+
- lib/boca_golf/checker.rb
|
43
|
+
- lib/boca_golf/command_line.rb
|
44
|
+
- lib/boca_golf/gist.rb
|
45
|
+
- lib/boca_golf/result.rb
|
46
|
+
- lib/boca_golf/scorer.rb
|
47
|
+
- lib/boca_golf/version.rb
|
48
|
+
- spec/boca_golf_spec.rb
|
49
|
+
- spec/checker_spec.rb
|
50
|
+
- spec/command_line_spec.rb
|
51
|
+
- spec/gist_spec.rb
|
52
|
+
- spec/infrastructure/reverse_specs/spec.rb
|
53
|
+
- spec/scorer_spec.rb
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: https://github.com/flipstone/boca-golf
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ">="
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
segments:
|
70
|
+
- 0
|
71
|
+
version: "0"
|
72
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
requirements: []
|
81
|
+
|
82
|
+
rubyforge_project:
|
83
|
+
rubygems_version: 1.3.7
|
84
|
+
signing_key:
|
85
|
+
specification_version: 3
|
86
|
+
summary: A simple rspec-based engine for playing ruby golf coding problems
|
87
|
+
test_files:
|
88
|
+
- spec/boca_golf_spec.rb
|
89
|
+
- spec/checker_spec.rb
|
90
|
+
- spec/command_line_spec.rb
|
91
|
+
- spec/gist_spec.rb
|
92
|
+
- spec/infrastructure/reverse_specs/spec.rb
|
93
|
+
- spec/scorer_spec.rb
|
94
|
+
- spec/spec_helper.rb
|