bloc 0.0.10 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|