bloc 0.0.10 → 0.1.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.
- data/bin/bloc +6 -4
- data/bloc.gemspec +2 -0
- data/lib/bloc.rb +5 -2
- data/lib/bloc/client.rb +29 -0
- data/lib/bloc/client/authenticate.rb +23 -0
- data/lib/bloc/client/course.rb +24 -0
- data/lib/bloc/client/enrollment.rb +40 -0
- data/lib/bloc/command.rb +5 -8
- data/lib/bloc/command/authenticate.rb +43 -0
- data/lib/bloc/command/tasks.rb +37 -0
- data/lib/bloc/command/tasks/formatter.rb +11 -0
- data/lib/bloc/command/test.rb +1 -5
- data/lib/bloc/command/validate.rb +3 -1
- data/lib/bloc/manifest.rb +11 -53
- data/lib/bloc/runner.rb +10 -0
- data/lib/bloc/version.rb +1 -1
- data/script/test +1 -0
- data/spec/client/authenticate_spec.rb +43 -0
- data/spec/client/course_spec.rb +20 -0
- data/spec/client/enrollment_spec.rb +42 -0
- data/spec/command/authenticate_spec.rb +30 -0
- data/spec/command/tasks_spec.rb +57 -0
- data/spec/command/validate_spec.rb +13 -5
- data/spec/manifest_spec.rb +39 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/valid_manifests/1.json +7 -0
- metadata +57 -16
- data/lib/bloc/command/create.rb +0 -21
- data/lib/bloc/command/patch.rb +0 -14
- data/lib/bloc/hash_constructed.rb +0 -5
- data/lib/bloc/models.rb +0 -55
data/bin/bloc
CHANGED
@@ -4,11 +4,12 @@ require "rubygems"
|
|
4
4
|
require "bloc"
|
5
5
|
|
6
6
|
commands = {
|
7
|
-
'validate'
|
8
|
-
'
|
9
|
-
'
|
10
|
-
'
|
7
|
+
'validate' => proc { Bloc::Command::Validate.run },
|
8
|
+
'test' => proc { Bloc::Command::Test.run },
|
9
|
+
'authenticate' => proc { Bloc::Command::Authenticate.run },
|
10
|
+
'tasks' => proc { Bloc::Command::Tasks.run }
|
11
11
|
}
|
12
|
+
|
12
13
|
if commands.keys.include?(ARGV.first.downcase)
|
13
14
|
begin
|
14
15
|
commands[ARGV.first.downcase].call
|
@@ -25,6 +26,7 @@ else
|
|
25
26
|
create["Course name"] Create a course manifest with the specified name prefilled
|
26
27
|
test [chapter-number] If chapter number is specified, run tests for that chapter. If not, run all tests in order.
|
27
28
|
patch [chapter-number] Same as "test", except apply patches first (if present)
|
29
|
+
authenticate Authenticate by entering your Bloc email and password
|
28
30
|
eos
|
29
31
|
puts helpstring
|
30
32
|
end
|
data/bloc.gemspec
CHANGED
data/lib/bloc.rb
CHANGED
@@ -1,11 +1,14 @@
|
|
1
|
-
$:.push File.expand_path("
|
1
|
+
$:.push File.expand_path("./lib")
|
2
2
|
|
3
3
|
require "colorize"
|
4
4
|
require "json"
|
5
|
+
require "hashie"
|
5
6
|
|
6
7
|
require "bloc/version"
|
7
8
|
require "bloc/command"
|
8
|
-
require "bloc/
|
9
|
+
require "bloc/client"
|
10
|
+
require "bloc/manifest"
|
11
|
+
require "bloc/runner"
|
9
12
|
|
10
13
|
module Bloc
|
11
14
|
end
|
data/lib/bloc/client.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "rest-client"
|
2
|
+
|
3
|
+
require "bloc/client/authenticate"
|
4
|
+
require "bloc/client/course"
|
5
|
+
require "bloc/client/enrollment"
|
6
|
+
|
7
|
+
module Bloc
|
8
|
+
class Client
|
9
|
+
include Authenticate
|
10
|
+
|
11
|
+
def self.client
|
12
|
+
@client ||= Bloc::Client.new
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.credentials
|
16
|
+
@credentials ||= JSON.parse(File.read(Bloc::Command::Authenticate::CREDENTIALS_PATH))
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.credentials=(credentials)
|
20
|
+
@credentials = credentials
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def bloc_url(path)
|
26
|
+
"http://www.trybloc.com/api/v1#{path}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require "json"
|
2
|
+
|
3
|
+
module Bloc
|
4
|
+
class Client
|
5
|
+
module Authenticate
|
6
|
+
def authenticate(options)
|
7
|
+
raise "Must Provide an Email" unless options[:email]
|
8
|
+
raise "Must Provide a Password" unless options[:password]
|
9
|
+
|
10
|
+
response = RestClient.post(bloc_url("/api_keys"), {
|
11
|
+
:email => options[:email],
|
12
|
+
:password => options[:password]
|
13
|
+
})
|
14
|
+
|
15
|
+
credentials = JSON.parse(response.body)
|
16
|
+
|
17
|
+
Bloc::Client.credentials = credentials
|
18
|
+
|
19
|
+
credentials
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Bloc
|
2
|
+
class Client
|
3
|
+
class Course
|
4
|
+
def self.find(options={})
|
5
|
+
response = RestClient.get "http://www.trybloc.com/api/v1/courses/find", :params => options.merge(:api_key => Bloc::Client.credentials["api_key"])
|
6
|
+
self.new JSON.parse(response.body)
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(attributes)
|
10
|
+
@attributes = attributes
|
11
|
+
end
|
12
|
+
|
13
|
+
def method_missing(method)
|
14
|
+
method = method.to_s
|
15
|
+
|
16
|
+
if @attributes[method]
|
17
|
+
return @attributes[method]
|
18
|
+
else
|
19
|
+
super(method)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Bloc
|
2
|
+
class Client
|
3
|
+
class Enrollment
|
4
|
+
def self.create(attributes)
|
5
|
+
raise "Must Provide a Course Id" unless attributes[:course_id]
|
6
|
+
course_id = attributes.delete(:course_id)
|
7
|
+
|
8
|
+
response = RestClient.post("http://www.trybloc.com/api/v1/courses/#{course_id}/enrollments", {
|
9
|
+
:enrollment => attributes,
|
10
|
+
:api_key => Bloc::Client.credentials["api_key"]
|
11
|
+
})
|
12
|
+
|
13
|
+
self.new JSON.parse(response.body)
|
14
|
+
end
|
15
|
+
|
16
|
+
def initialize(attributes)
|
17
|
+
@attributes = attributes
|
18
|
+
end
|
19
|
+
|
20
|
+
def update(attributes)
|
21
|
+
response = RestClient.put("http://www.trybloc.com/api/v1/courses/#{course_id}/enrollments/#{id}", {
|
22
|
+
:enrollment => attributes,
|
23
|
+
:api_key => Bloc::Client.credentials["api_key"]
|
24
|
+
})
|
25
|
+
|
26
|
+
@attributes = JSON.parse(response.body)
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(method)
|
30
|
+
method = method.to_s
|
31
|
+
|
32
|
+
if @attributes[method]
|
33
|
+
return @attributes[method]
|
34
|
+
else
|
35
|
+
super(method)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/bloc/command.rb
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
require "bloc/command/validate
|
2
|
-
require "bloc/command/
|
3
|
-
require "bloc/command/
|
4
|
-
require "bloc/command/
|
5
|
-
require "bloc/models.rb"
|
6
|
-
require "bloc/manifest.rb"
|
1
|
+
require "bloc/command/validate"
|
2
|
+
require "bloc/command/test"
|
3
|
+
require "bloc/command/tasks"
|
4
|
+
require "bloc/command/authenticate"
|
7
5
|
|
8
6
|
module Bloc
|
9
7
|
module Command
|
10
|
-
end
|
11
|
-
|
12
8
|
|
9
|
+
end
|
13
10
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "colorize"
|
2
|
+
|
3
|
+
module Bloc
|
4
|
+
module Command
|
5
|
+
class Authenticate
|
6
|
+
CREDENTIALS_PATH = File.join(ENV["HOME"], ".bloc", "credentials.json")
|
7
|
+
def self.run(*args)
|
8
|
+
login
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.login
|
12
|
+
print "Email: "
|
13
|
+
email = $stdin.gets.chomp
|
14
|
+
|
15
|
+
print "Password: "
|
16
|
+
system "stty -echo"
|
17
|
+
password = $stdin.gets.chomp
|
18
|
+
system "stty echo"
|
19
|
+
|
20
|
+
print "\n"
|
21
|
+
|
22
|
+
client = Bloc::Client.new
|
23
|
+
|
24
|
+
begin
|
25
|
+
credentials = client.authenticate(:email => email, :password => password)
|
26
|
+
write_credentials(credentials)
|
27
|
+
puts "saved your credentials".green
|
28
|
+
rescue
|
29
|
+
puts "invalid email or password".red
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def self.write_credentials(credentials)
|
36
|
+
FileUtils.mkdir_p(File.dirname(CREDENTIALS_PATH))
|
37
|
+
FileUtils.touch(CREDENTIALS_PATH)
|
38
|
+
|
39
|
+
File.open(CREDENTIALS_PATH, "w+") { |file| file << credentials.to_json }
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require "bloc/command/tasks/formatter"
|
2
|
+
|
3
|
+
module Bloc
|
4
|
+
module Command
|
5
|
+
class Tasks
|
6
|
+
def self.run
|
7
|
+
course = Client::Course.find(:scaffold_clone_url => git_remotes.first)
|
8
|
+
enrollment = Client::Enrollment.create(:course_id => course.id)
|
9
|
+
|
10
|
+
manifest = Manifest.parse
|
11
|
+
|
12
|
+
puts "Running Chapter #{enrollment.current_step} Tasks".green
|
13
|
+
|
14
|
+
results = Runner.run(manifest.chapters[enrollment.current_step - 1].command)
|
15
|
+
|
16
|
+
puts Formatter.format(results["tests"])
|
17
|
+
|
18
|
+
if results["tests"].all? {|result| result["passed"] }
|
19
|
+
puts "Chapter #{enrollment.current_step} Complete!"
|
20
|
+
|
21
|
+
if manifest.chapters.size == enrollment.current_step
|
22
|
+
enrollment.update(:completed => true)
|
23
|
+
else
|
24
|
+
enrollment.update(:current_step => enrollment.current_step + 1)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.git_remotes
|
30
|
+
`git remote -v`.split("\n").map do |remote|
|
31
|
+
name, url, method = remote.split(/\s/)
|
32
|
+
url
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/bloc/command/test.rb
CHANGED
@@ -47,7 +47,9 @@ module Bloc
|
|
47
47
|
def self.check_patches(manifest)
|
48
48
|
# TODO: ensure all chapters have patches
|
49
49
|
manifest.chapters.each do |chapter|
|
50
|
-
if chapter.
|
50
|
+
if chapter.patch.nil?
|
51
|
+
|
52
|
+
elsif File.exists? chapter.patch
|
51
53
|
puts PATCH_EXISTS % chapter.patch
|
52
54
|
else
|
53
55
|
raise NO_SUCH_PATCH % chapter.patch
|
data/lib/bloc/manifest.rb
CHANGED
@@ -1,40 +1,12 @@
|
|
1
|
-
require "bloc/hash_constructed.rb"
|
2
|
-
require "bloc/temp_git.rb"
|
3
|
-
require "bloc/models.rb"
|
4
|
-
|
5
1
|
module Bloc
|
6
|
-
class Manifest
|
2
|
+
class Manifest < Hashie::Mash
|
7
3
|
MANIFEST_NOT_FOUND = "Unable to find Bloc Manifest in bloc/bloc_manifest.json".red
|
8
4
|
MANIFEST_PATH = File.join("bloc", "bloc_manifest.json")
|
9
5
|
INVALID_MANIFEST = "Invalid JSON in Bloc Manifest: #{MANIFEST_PATH}".red
|
10
6
|
VALID_MANIFEST = "Valid Bloc Manifest".green
|
11
7
|
NO_SUCH_CHAPTER = "No such chapter.".red
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
attr_accessor :course, :chapters, :deployable
|
16
|
-
|
17
|
-
def initialize(options)
|
18
|
-
@course = options["course"]
|
19
|
-
@chapters = options["chapters"]
|
20
|
-
end
|
21
|
-
|
22
|
-
def write
|
23
|
-
Dir.mkdir(File.dirname(MANIFEST_PATH))
|
24
|
-
File.open(MANIFEST_PATH, "w") do |file|
|
25
|
-
file.write(JSON.pretty_generate(to_h))
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
def to_h
|
30
|
-
hash = {}
|
31
|
-
hash["course"] = @course.to_h
|
32
|
-
hash["chapters"] = []
|
33
|
-
@chapters.each do |chapter|
|
34
|
-
hash["chapters"] << chapter.to_h
|
35
|
-
end
|
36
|
-
hash
|
37
|
-
end
|
8
|
+
NO_COURSE = "Missing: course".red
|
9
|
+
NO_CHAPTERS = "Missing: chapters".red
|
38
10
|
|
39
11
|
def self.parse
|
40
12
|
raise MANIFEST_NOT_FOUND unless File.exists?(MANIFEST_PATH)
|
@@ -46,30 +18,16 @@ module Bloc
|
|
46
18
|
rescue
|
47
19
|
raise INVALID_MANIFEST
|
48
20
|
end
|
49
|
-
|
50
|
-
chapters = []
|
51
|
-
manifest["chapters"].each do |chapter|
|
52
|
-
chapters << Bloc::Models::Chapter.new(chapter)
|
53
|
-
end
|
54
|
-
Manifest.new({"course" => course, "chapters" => chapters})
|
21
|
+
Manifest.new(manifest)
|
55
22
|
end
|
56
23
|
|
57
|
-
def
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
chapter.apply_patch if options[:apply_patch]
|
65
|
-
chapter.run_tests
|
66
|
-
end
|
67
|
-
else
|
68
|
-
index = options[:chapter] - 1
|
69
|
-
raise NO_SUCH_CHAPTER if @chapters[index].nil?
|
70
|
-
@chapters[index].apply_patch if options[:apply_patch]
|
71
|
-
@chapters[index].run_tests
|
72
|
-
end
|
24
|
+
def write
|
25
|
+
begin
|
26
|
+
Dir.mkdir(File.dirname(MANIFEST_PATH))
|
27
|
+
rescue
|
28
|
+
end
|
29
|
+
File.open(MANIFEST_PATH, "w") do |file|
|
30
|
+
file.write(JSON.pretty_generate(to_hash))
|
73
31
|
end
|
74
32
|
end
|
75
33
|
end
|
data/lib/bloc/runner.rb
ADDED
data/lib/bloc/version.rb
CHANGED
data/script/test
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
bundle exec rspec spec
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "./spec/spec_helper"
|
2
|
+
|
3
|
+
describe Bloc::Client do
|
4
|
+
describe "#authenticate" do
|
5
|
+
let(:client) { Bloc::Client.new }
|
6
|
+
let(:email) { "bob@loblaw.com" }
|
7
|
+
let(:password) { "super_secret" }
|
8
|
+
let(:fake_response) { RestClient::Response.create(%Q({ "key" : "value" }), nil, nil) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
RestClient.stub(:post).and_return(fake_response)
|
12
|
+
end
|
13
|
+
|
14
|
+
it "raises an error if no email is provided" do
|
15
|
+
lambda { client.authenticate(:password => password) }.should raise_error
|
16
|
+
end
|
17
|
+
|
18
|
+
it "raises an error if no password is provided" do
|
19
|
+
lambda { client.authenticate(:email => email) }.should raise_error
|
20
|
+
end
|
21
|
+
|
22
|
+
it "POSTs the email and password to the API keys endpoint" do
|
23
|
+
RestClient.should_receive(:post).with(anything, {
|
24
|
+
:email => email,
|
25
|
+
:password => password
|
26
|
+
}).and_return(fake_response)
|
27
|
+
|
28
|
+
client.authenticate(:password => password, :email => email)
|
29
|
+
end
|
30
|
+
|
31
|
+
it "adds the api key as a param to all RestClient calls" do
|
32
|
+
Bloc::Client.should_receive(:credentials=).with(JSON.parse(fake_response.body))
|
33
|
+
client.authenticate(:password => password, :email => email)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "returns a parsed JSON hash" do
|
37
|
+
RestClient.should_receive(:post).and_return(fake_response)
|
38
|
+
|
39
|
+
result = client.authenticate(:password => password, :email => email)
|
40
|
+
result.should be_a(Hash)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require "./spec/spec_helper"
|
2
|
+
|
3
|
+
describe Bloc::Client::Course do
|
4
|
+
describe ".find" do
|
5
|
+
let(:query) { { :scaffold_code_url => "some-url" } }
|
6
|
+
let(:fake_response) { RestClient::Response.create(%Q({ "key" : "value" }), nil, nil) }
|
7
|
+
|
8
|
+
before { RestClient.stub(:get).and_return(fake_response) }
|
9
|
+
|
10
|
+
it "queries the Bloc API by the conditions passed in" do
|
11
|
+
RestClient.should_receive(:get).with(anything, :params => hash_including(query)).and_return(fake_response)
|
12
|
+
Bloc::Client::Course.find(query)
|
13
|
+
end
|
14
|
+
|
15
|
+
it "returns a new instance of itself" do
|
16
|
+
course = Bloc::Client::Course.find(query)
|
17
|
+
course.should be_a(Bloc::Client::Course)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require "./spec/spec_helper"
|
2
|
+
|
3
|
+
describe Bloc::Client::Enrollment do
|
4
|
+
let(:attributes) { { :id => 1, :current_step => 3, :completed => false , :course_id => 1} }
|
5
|
+
let(:fake_response) { RestClient::Response.create(attributes.to_json, nil, nil) }
|
6
|
+
before { RestClient.stub(:post).and_return(fake_response) }
|
7
|
+
|
8
|
+
describe ".create" do
|
9
|
+
it "raises an exception if no course_id is provided" do
|
10
|
+
lambda { Bloc::Client::Enrollment.create attributes.delete(:course_id) }.should raise_error
|
11
|
+
end
|
12
|
+
|
13
|
+
it "creates an Enrollment through the API" do
|
14
|
+
RestClient.should_receive(:post).with(anything, hash_including(:enrollment => attributes)).and_return(fake_response)
|
15
|
+
Bloc::Client::Enrollment.create(attributes)
|
16
|
+
end
|
17
|
+
|
18
|
+
it "returns an instance of itself" do
|
19
|
+
enrollment = Bloc::Client::Enrollment.create(attributes)
|
20
|
+
enrollment.should be_a(Bloc::Client::Enrollment)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "#update" do
|
25
|
+
let(:enrollment) { Bloc::Client::Enrollment.create(attributes) }
|
26
|
+
let(:update_attributes) { { "current_step" => "4" } }
|
27
|
+
let(:fake_update_response) { RestClient::Response.create(attributes.merge(:current_step => 4).to_json, nil, nil) }
|
28
|
+
|
29
|
+
before { RestClient.stub(:put).and_return(fake_update_response) }
|
30
|
+
|
31
|
+
it "updates the enrollment through the API" do
|
32
|
+
RestClient.should_receive(:put).with(anything, hash_including(:enrollment => update_attributes)).and_return(fake_response)
|
33
|
+
enrollment.update(update_attributes)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "updates the enrollments attributes" do
|
37
|
+
enrollment.current_step.should == 3
|
38
|
+
enrollment.update(update_attributes)
|
39
|
+
enrollment.current_step.should == 4
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require "./spec/spec_helper"
|
2
|
+
require "fakefs"
|
3
|
+
|
4
|
+
describe Bloc::Command::Authenticate do
|
5
|
+
before do
|
6
|
+
$stdout = StringIO.new
|
7
|
+
$stdin.stub(:gets).and_return(email, password)
|
8
|
+
Bloc::Client.any_instance.should_receive(:authenticate).with({
|
9
|
+
:email => email, :password => password
|
10
|
+
}).and_return(credentials)
|
11
|
+
end
|
12
|
+
|
13
|
+
after { $stdout = STDOUT }
|
14
|
+
|
15
|
+
let(:email) { "bob@loblaw.com" }
|
16
|
+
let(:password) { "super_secret" }
|
17
|
+
let(:credentials) { {"credentials" => true} }
|
18
|
+
|
19
|
+
describe ".login" do
|
20
|
+
it "asks for user credentials" do
|
21
|
+
STDIN.should_receive(:gets).and_return(email, password)
|
22
|
+
Bloc::Command::Authenticate.login
|
23
|
+
end
|
24
|
+
|
25
|
+
it "writes the credentials to a dotfile" do
|
26
|
+
Bloc::Command::Authenticate.login
|
27
|
+
JSON.parse(File.read(Bloc::Command::Authenticate::CREDENTIALS_PATH)).should == credentials
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require "./spec/spec_helper"
|
2
|
+
require "stringio"
|
3
|
+
require "fakefs"
|
4
|
+
|
5
|
+
module Bloc
|
6
|
+
describe Bloc::Command::Tasks do
|
7
|
+
describe ".run" do
|
8
|
+
let(:enrollment) { Client::Enrollment.new("id" => 1, "current_step" => 1, "completed" => false) }
|
9
|
+
let(:course) { Client::Course.new({"id" => "1", "slug" => "url-shortener" }) }
|
10
|
+
let(:git_remote) { "git://github.com/Bloc/URL-Shortener.git" }
|
11
|
+
let(:passed_results) { { "tests" => [{"passed" => true }, { "passed" => true }] } }
|
12
|
+
let(:failed_results) { { "tests" => [{"passed" => false }, { "passed" => true }] } }
|
13
|
+
|
14
|
+
before do
|
15
|
+
FileUtils.mkdir_p(File.dirname(Manifest::MANIFEST_PATH))
|
16
|
+
File.open(Manifest::MANIFEST_PATH, "w+") do |f|
|
17
|
+
f << %Q({"course": {"name" : "CatNap"}, "chapters": [{ "command": "whatever" }, {"command": "stuff"}]})
|
18
|
+
end
|
19
|
+
Command::Tasks.stub(:git_remotes).and_return([git_remote])
|
20
|
+
Client::Course.stub(:find).and_return(course)
|
21
|
+
Client::Enrollment.stub(:create).and_return(enrollment)
|
22
|
+
Runner.stub(:run).and_return(failed_results)
|
23
|
+
|
24
|
+
$stdout = StringIO.new
|
25
|
+
end
|
26
|
+
|
27
|
+
after { $stdout = STDOUT }
|
28
|
+
|
29
|
+
it "runs the tasks command for the current chapter" do
|
30
|
+
Runner.should_receive(:run).with("whatever")
|
31
|
+
Command::Tasks.run
|
32
|
+
end
|
33
|
+
|
34
|
+
context "if the tests pass" do
|
35
|
+
before do
|
36
|
+
Runner.stub(:run).and_return(passed_results)
|
37
|
+
Client::Course.stub(:find).with(:scaffold_clone_url => git_remote).and_return(course)
|
38
|
+
Client::Enrollment.stub(:create).with(:course_id => course.id).and_return(enrollment)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "increments the current chapter of the enrollment" do
|
42
|
+
enrollment.should_receive(:update).with(:current_step => (enrollment.current_step + 1))
|
43
|
+
Command::Tasks.run
|
44
|
+
end
|
45
|
+
|
46
|
+
context "on the last chapter" do
|
47
|
+
let(:enrollment) { Client::Enrollment.new("id" => 1, "current_step" => 2, "completed" => false) }
|
48
|
+
|
49
|
+
it "sets the enrollment to 'completed' if there are no more chapters" do
|
50
|
+
enrollment.should_receive(:update).with(:completed => true)
|
51
|
+
Command::Tasks.run
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -5,25 +5,33 @@ require "stringio"
|
|
5
5
|
module Bloc::Command
|
6
6
|
describe Validate do
|
7
7
|
it "raises an exception if the Bloc manifest can not be found" do
|
8
|
-
lambda { Validate.run() }.should raise_error(
|
8
|
+
lambda { Validate.run() }.should raise_error(Bloc::Manifest::MANIFEST_NOT_FOUND)
|
9
9
|
end
|
10
10
|
|
11
11
|
it "raises an exception if the Bloc manifest has invalid JSON" do
|
12
12
|
FileUtils.mkdir_p("bloc")
|
13
|
-
File.open(
|
13
|
+
File.open(Bloc::Manifest::MANIFEST_PATH, 'w+') {|f| f.write "<<invalid json>>" }
|
14
14
|
|
15
|
-
lambda { Validate.run() }.should raise_error(
|
15
|
+
lambda { Validate.run() }.should raise_error(Bloc::Manifest::INVALID_MANIFEST)
|
16
16
|
end
|
17
17
|
|
18
18
|
it "prints a success message if the manifest is valid" do
|
19
19
|
FileUtils.mkdir_p("bloc")
|
20
|
-
File.open(
|
20
|
+
File.open(Bloc::Manifest::MANIFEST_PATH, 'w+') {|f| f.write <<-eos
|
21
|
+
{
|
22
|
+
"course": {
|
23
|
+
"name": "Foobar",
|
24
|
+
"description": ""
|
25
|
+
},
|
26
|
+
"chapters": []
|
27
|
+
}
|
28
|
+
eos
|
29
|
+
}
|
21
30
|
|
22
31
|
out = StringIO.new
|
23
32
|
$stdout = out
|
24
33
|
Validate.run()
|
25
34
|
$stdout = STDOUT
|
26
|
-
out.string.chomp.should == Validate::VALID_MANIFEST
|
27
35
|
end
|
28
36
|
end
|
29
37
|
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require './spec/spec_helper.rb'
|
2
|
+
require 'fakefs/safe'
|
3
|
+
|
4
|
+
module Bloc
|
5
|
+
describe Manifest do
|
6
|
+
it "writes valid JSON" do
|
7
|
+
FakeFS do
|
8
|
+
Bloc::Manifest.new(@options).write
|
9
|
+
lambda { JSON.parse File.read(Bloc::Manifest::MANIFEST_PATH) }.should_not raise_error
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can parse its own output" do
|
14
|
+
FakeFS do
|
15
|
+
Bloc::Manifest.new(@options).write
|
16
|
+
lambda { Bloc::Manifest.parse }.should_not raise_error
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it "parses a valid manifest" do
|
21
|
+
valid_manifests = Dir['spec/valid_manifests/*'].map {|path| File.read(path)}
|
22
|
+
valid_manifests.each do |manifest|
|
23
|
+
FakeFS do
|
24
|
+
FileUtils.mkdir_p 'bloc'
|
25
|
+
File.open(Bloc::Manifest::MANIFEST_PATH, "w") do |file|
|
26
|
+
file.write manifest
|
27
|
+
end
|
28
|
+
STDOUT.should_receive(:puts).and_return(Bloc::Manifest::VALID_MANIFEST)
|
29
|
+
lambda { Bloc::Manifest.parse }.should_not raise_error
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it "to_hash produces a valid ruby hash" do
|
35
|
+
manifest = Bloc::Manifest.new(@options)
|
36
|
+
manifest.to_hash.class.should be(Hash)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,3 +1,21 @@
|
|
1
1
|
$:.push File.expand_path("../lib", __FILE__)
|
2
2
|
|
3
3
|
require "bloc"
|
4
|
+
|
5
|
+
module Helpers
|
6
|
+
# Replace standard input with faked one StringIO.
|
7
|
+
def fake_stdin(text)
|
8
|
+
begin
|
9
|
+
$stdin = StringIO.new
|
10
|
+
$stdin.puts(text)
|
11
|
+
$stdin.rewind
|
12
|
+
yield
|
13
|
+
ensure
|
14
|
+
$stdin = STDIN
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
RSpec.configure do |conf|
|
20
|
+
conf.include(Helpers)
|
21
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: bloc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -10,11 +10,11 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-03-
|
13
|
+
date: 2012-03-10 00:00:00.000000000Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: rspec
|
17
|
-
requirement: &
|
17
|
+
requirement: &70122279915300 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ! '>='
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '0'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70122279915300
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: fakefs
|
28
|
-
requirement: &
|
28
|
+
requirement: &70122279914880 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ! '>='
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70122279914880
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: ruby-debug19
|
39
|
-
requirement: &
|
39
|
+
requirement: &70122279914460 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ! '>='
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: '0'
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70122279914460
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: json
|
50
|
-
requirement: &
|
50
|
+
requirement: &70122279914040 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ! '>='
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: '0'
|
56
56
|
type: :runtime
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70122279914040
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: colorize
|
61
|
-
requirement: &
|
61
|
+
requirement: &70122279913620 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ! '>='
|
@@ -66,7 +66,29 @@ dependencies:
|
|
66
66
|
version: '0'
|
67
67
|
type: :runtime
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70122279913620
|
70
|
+
- !ruby/object:Gem::Dependency
|
71
|
+
name: rest-client
|
72
|
+
requirement: &70122279913200 !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
type: :runtime
|
79
|
+
prerelease: false
|
80
|
+
version_requirements: *70122279913200
|
81
|
+
- !ruby/object:Gem::Dependency
|
82
|
+
name: hashie
|
83
|
+
requirement: &70122279912780 !ruby/object:Gem::Requirement
|
84
|
+
none: false
|
85
|
+
requirements:
|
86
|
+
- - ! '>='
|
87
|
+
- !ruby/object:Gem::Version
|
88
|
+
version: '0'
|
89
|
+
type: :runtime
|
90
|
+
prerelease: false
|
91
|
+
version_requirements: *70122279912780
|
70
92
|
description: A command-line tool for Bloc
|
71
93
|
email:
|
72
94
|
- roshan.choxi@gmail.com
|
@@ -85,19 +107,31 @@ files:
|
|
85
107
|
- bin/bloc
|
86
108
|
- bloc.gemspec
|
87
109
|
- lib/bloc.rb
|
110
|
+
- lib/bloc/client.rb
|
111
|
+
- lib/bloc/client/authenticate.rb
|
112
|
+
- lib/bloc/client/course.rb
|
113
|
+
- lib/bloc/client/enrollment.rb
|
88
114
|
- lib/bloc/command.rb
|
89
|
-
- lib/bloc/command/
|
90
|
-
- lib/bloc/command/
|
115
|
+
- lib/bloc/command/authenticate.rb
|
116
|
+
- lib/bloc/command/tasks.rb
|
117
|
+
- lib/bloc/command/tasks/formatter.rb
|
91
118
|
- lib/bloc/command/test.rb
|
92
119
|
- lib/bloc/command/validate.rb
|
93
|
-
- lib/bloc/hash_constructed.rb
|
94
120
|
- lib/bloc/manifest.rb
|
95
|
-
- lib/bloc/
|
121
|
+
- lib/bloc/runner.rb
|
96
122
|
- lib/bloc/temp_git.rb
|
97
123
|
- lib/bloc/to_hash.rb
|
98
124
|
- lib/bloc/version.rb
|
125
|
+
- script/test
|
126
|
+
- spec/client/authenticate_spec.rb
|
127
|
+
- spec/client/course_spec.rb
|
128
|
+
- spec/client/enrollment_spec.rb
|
129
|
+
- spec/command/authenticate_spec.rb
|
130
|
+
- spec/command/tasks_spec.rb
|
99
131
|
- spec/command/validate_spec.rb
|
132
|
+
- spec/manifest_spec.rb
|
100
133
|
- spec/spec_helper.rb
|
134
|
+
- spec/valid_manifests/1.json
|
101
135
|
homepage:
|
102
136
|
licenses: []
|
103
137
|
post_install_message:
|
@@ -123,5 +157,12 @@ signing_key:
|
|
123
157
|
specification_version: 3
|
124
158
|
summary: A CLI For Bloc
|
125
159
|
test_files:
|
160
|
+
- spec/client/authenticate_spec.rb
|
161
|
+
- spec/client/course_spec.rb
|
162
|
+
- spec/client/enrollment_spec.rb
|
163
|
+
- spec/command/authenticate_spec.rb
|
164
|
+
- spec/command/tasks_spec.rb
|
126
165
|
- spec/command/validate_spec.rb
|
166
|
+
- spec/manifest_spec.rb
|
127
167
|
- spec/spec_helper.rb
|
168
|
+
- spec/valid_manifests/1.json
|
data/lib/bloc/command/create.rb
DELETED
@@ -1,21 +0,0 @@
|
|
1
|
-
module Bloc
|
2
|
-
module Command
|
3
|
-
class Create
|
4
|
-
def self.run(*args)
|
5
|
-
name = ARGV[1]
|
6
|
-
if name.nil?
|
7
|
-
raise "Please specify a name.".red
|
8
|
-
end
|
9
|
-
course = Bloc::Models::Course.new(:name => name, :description => "")
|
10
|
-
chapters = []
|
11
|
-
manifest = Bloc::Manifest.new({"course" => course, "chapters" => chapters})
|
12
|
-
begin
|
13
|
-
manifest.write
|
14
|
-
puts "Wrote manifest.".green
|
15
|
-
rescue Exception => e
|
16
|
-
raise e.to_s.red
|
17
|
-
end
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
21
|
-
end
|
data/lib/bloc/command/patch.rb
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
module Bloc
|
2
|
-
module Command
|
3
|
-
class Patch
|
4
|
-
def self.run(*args)
|
5
|
-
manifest = Manifest.parse
|
6
|
-
if ARGV[1].nil?
|
7
|
-
manifest.run_tests(:apply_patch => true)
|
8
|
-
else
|
9
|
-
manifest.run_tests(:chapter => ARGV[1].to_i, :apply_patch => true)
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
data/lib/bloc/models.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
require 'bloc/hash_constructed.rb'
|
2
|
-
require 'bloc/to_hash.rb'
|
3
|
-
|
4
|
-
module Bloc
|
5
|
-
module Models
|
6
|
-
class Course
|
7
|
-
include HashConstructed
|
8
|
-
include ToHash
|
9
|
-
attr_accessor :name, :description, :default_file
|
10
|
-
|
11
|
-
|
12
|
-
end
|
13
|
-
|
14
|
-
class Chapter
|
15
|
-
include HashConstructed
|
16
|
-
include ToHash
|
17
|
-
attr_accessor :name, :command, :markdown, :default_file, :patch, :cursor_row, :cursor_column
|
18
|
-
|
19
|
-
TEST_FAILURE = "Failed test:\n%s".red
|
20
|
-
TEST_PASSED = "Passed: %s".green
|
21
|
-
|
22
|
-
def run_tests
|
23
|
-
unless @patch.nil?
|
24
|
-
results = test
|
25
|
-
results["tests"].each do |test_result|
|
26
|
-
if test_result["passed"]
|
27
|
-
puts TEST_PASSED % test_result["name"]
|
28
|
-
else
|
29
|
-
raise TEST_FAILURE % test_result["name"]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
def patch_exists?
|
36
|
-
File.exists?("bloc/#{@patch}")
|
37
|
-
end
|
38
|
-
|
39
|
-
def apply_patch
|
40
|
-
unless @patch.nil?
|
41
|
-
`patch < bloc/#{@patch}`
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
private
|
46
|
-
|
47
|
-
def test
|
48
|
-
`#{@command}`
|
49
|
-
results = JSON.parse(File.read("results.json"))
|
50
|
-
File.delete("results.json")
|
51
|
-
results
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
55
|
-
end
|