coursegen 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +66 -16
- data/lib/coursegen/cli.rb +7 -1
- data/lib/coursegen/course/helpers/content_helpers.rb +4 -4
- data/lib/coursegen/course/lib/citem.rb +6 -4
- data/lib/coursegen/course/lib/lectures.rb +2 -2
- data/lib/coursegen/course/lib/section.rb +7 -2
- data/lib/coursegen/course/lib/toc.rb +2 -2
- data/lib/coursegen/version.rb +1 -1
- data/spec/citem_spec.rb +56 -0
- data/spec/lectures_spec.rb +52 -0
- data/spec/scheduler_spec.rb +81 -0
- data/spec/section_spec.rb +40 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/toc_spec.rb +86 -0
- data/templates/cg_config.rb +2 -0
- data/templates/cg_config.rb_sample +1 -4
- metadata +15 -9
- data/test1/Rules +0 -40
- data/test1/content/index.html +0 -14
- data/test1/content/stylesheet.css +0 -101
- data/test1/layouts/default.html +0 -29
- data/test1/lib/default.rb +0 -2
- data/test1/nanoc.yaml +0 -62
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4cbd7ecb6ccd900a51488c819eeaad47d4bdd378
|
4
|
+
data.tar.gz: f1762832554e141c230e461ac2556d4d3a2b4cd9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2e09e848c82fcc3ccc3066adfa9d884398e53bcd29ea78ca64c7275a2aaf7bc567b97e549d5789494da39427f54b8e07f483c3382cfead3cdb66b003bf47811c
|
7
|
+
data.tar.gz: aafafadf2180a753d78d6da10aeef0f9dc2c8a09a1a18e1bf836a95a67f1e98fb3e2cd8f78eb7b1d14077e8b4b9f8be54c1943a11a9d8a24e558919f28e6366e
|
data/README.md
CHANGED
@@ -1,29 +1,79 @@
|
|
1
1
|
# Coursegen
|
2
2
|
|
3
|
-
|
3
|
+
Generates course curriculum web sites with support for sections, lectures, code examples, homeworks, course calendar and lots more.
|
4
4
|
|
5
|
-
|
5
|
+
# Work in progress
|
6
6
|
|
7
|
-
|
7
|
+
This gem is still under development. In the current form it can already be used but I will be adding more convenience features in the near future.
|
8
8
|
|
9
|
-
|
9
|
+
# Steps to building a new course
|
10
10
|
|
11
|
-
|
11
|
+
### Install
|
12
|
+
* install the gem, `gem install coursegen`
|
12
13
|
|
13
|
-
|
14
|
+
### Generate Blank Course
|
15
|
+
* generate a blank course site `gem new MYCOURSE`. This will create a local directory called MYCOURSE containing the skeleton
|
16
|
+
* `cd MYCOURSE`
|
17
|
+
* Load up the template and basics into the newly created directory `cg prepare`
|
18
|
+
* Modify the configuration of the course by editing the file `cg_config.rb`. You can look at a complicated example to see some of the things that are possible: `cg_config.rb_sample`
|
14
19
|
|
15
|
-
|
20
|
+
### Add it to github (optional)
|
21
|
+
* Create a new github repo with the course name
|
22
|
+
* `cd MYCOURSE; git init`
|
23
|
+
* Follow Github instructions to push the first commit up to github
|
16
24
|
|
17
|
-
|
25
|
+
### Steps to develop the course
|
26
|
+
* Add your course content as new files into the content/ directory
|
27
|
+
* Rebuild the course static html: `cg compile`
|
28
|
+
* Start a local server for testing: `cg serve`
|
29
|
+
* Open your web browser to look at the course: `cg view`
|
30
|
+
* Rinse, dry and repeat
|
18
31
|
|
19
|
-
|
32
|
+
### Steps for deploying the course
|
33
|
+
* The /output directory is a complete self-contained static html site. You can deploy it anywhere.
|
34
|
+
* I recommend Amazon S3 for a free site
|
20
35
|
|
21
|
-
|
36
|
+
### Create an S3 instance to host the site (optional)
|
37
|
+
* Log into aws.amazon.com and look for the management console for S3
|
38
|
+
* Create a new bucket. Call it whatever you want.
|
39
|
+
* Look in the properties and choose "Static Website Hosting". Enable it and set index.html as your index document
|
40
|
+
* Make a note of the "endpoint url"
|
41
|
+
* Click on permissions/add more permissions
|
42
|
+
* Choose Grantee: Everyone; And "list" and no others
|
43
|
+
* Click "Edit Bucket Policy". Paste this into your bucket policy and save. Notice you have to edit "yourbucketname" to be your bucket name
|
22
44
|
|
23
|
-
|
45
|
+
{
|
46
|
+
"Version": "2008-10-17",
|
47
|
+
"Id": "Policy1",
|
48
|
+
"Statement": [
|
49
|
+
{
|
50
|
+
"Sid": "Stmt1",
|
51
|
+
"Effect": "Allow",
|
52
|
+
"Principal": {
|
53
|
+
"AWS": "*"
|
54
|
+
},
|
55
|
+
"Action": "s3:GetObject",
|
56
|
+
"Resource": "arn:aws:s3:::yourbucketname/*"
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
|
61
|
+
### Deploy to S3 (optional)
|
62
|
+
* I've used `s3cmd` with great success. You can find it at s3tools.org. Follow their instructions to install it.
|
63
|
+
* Configure your course to point to the bucket you just created. Notice, we are using the bucket name not the url: `AWS_BUCKET = "cosi165-2014"`
|
64
|
+
|
65
|
+
### Examples
|
66
|
+
* A simple example: https://github.com/pitosalas/jbscosi2014
|
67
|
+
* A complex example (but based on a previous version): https://github.com/pitosalas/cosi236b
|
68
|
+
* The resultant web site: bit.ly/cosi236b
|
69
|
+
|
70
|
+
## Open Source Catalog of Topics
|
71
|
+
CourseGen comes with a large, shared catalog of topics. Depending on what you teach you may or may not find useful content there. Over time we hope to broaden this catalog. You can review the current set of topics here: https://github.com/pitosalas/coursegen-topics
|
72
|
+
|
73
|
+
### Step by step
|
74
|
+
* First clone the topic directory to your local drive: `git clone git@github.com:pitosalas/coursegen-topics.git`
|
75
|
+
* Add a symbolic link between your course and the newly cloned directory. For example you might do this:
|
76
|
+
cd MYCOURSE/content
|
77
|
+
ln -s TOPICDIR .
|
78
|
+
|
24
79
|
|
25
|
-
1. Fork it ( https://github.com/[my-github-username]/coursegen/fork )
|
26
|
-
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
-
3. Commit your changes (`git commit -am 'Add some feature'`)
|
28
|
-
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
5. Create a new Pull Request
|
data/lib/coursegen/cli.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'thor'
|
2
|
-
require
|
2
|
+
require 'coursegen/templates'
|
3
|
+
require './cg_config.rb'
|
3
4
|
|
4
5
|
module CourseGen
|
5
6
|
class CLI < Thor
|
@@ -38,6 +39,11 @@ module CourseGen
|
|
38
39
|
run "open http://0.0.0.0:3000"
|
39
40
|
end
|
40
41
|
|
42
|
+
desc "deploy", "Deploy course to S3"
|
43
|
+
def deploy
|
44
|
+
run "s3cmd sync --delete-removed output/ s3://#{AWS_BUCKET}/"
|
45
|
+
end
|
46
|
+
|
41
47
|
no_commands do
|
42
48
|
def check_valid_directory
|
43
49
|
CourseGen::Templates.new.valid_cg_directory? ?
|
@@ -111,9 +111,9 @@ module ContentHelpers
|
|
111
111
|
end
|
112
112
|
|
113
113
|
def include_ruby name
|
114
|
-
|
115
|
-
|
116
|
-
|
114
|
+
filename = Dir.pwd+"/content/content/topics/scripts/"+name.to_s+".rb"
|
115
|
+
filecontents = File.new(filename).read
|
116
|
+
ruby_string filecontents
|
117
117
|
end
|
118
118
|
|
119
119
|
def code_begin
|
@@ -129,7 +129,7 @@ module ContentHelpers
|
|
129
129
|
end
|
130
130
|
|
131
131
|
def include_code name
|
132
|
-
filename = "content/content/scripts
|
132
|
+
filename = Dir.pwd+"/content/content/topics/scripts/"+name
|
133
133
|
filecontents = File.new(filename).read
|
134
134
|
code_string filecontents
|
135
135
|
end
|
@@ -1,12 +1,12 @@
|
|
1
1
|
require 'active_support/inflector'
|
2
2
|
|
3
3
|
#
|
4
|
-
#
|
4
|
+
# Coursegen Item decorates Nanoc Item
|
5
5
|
#
|
6
6
|
class CItem
|
7
7
|
|
8
8
|
attr_reader :order, :section, :subsection, :subsection_citem, :title,
|
9
|
-
:type, :identifier, :short_name, :status, :nitem, :css_class
|
9
|
+
:type, :identifier, :short_name, :status, :nitem, :css_class
|
10
10
|
attr_accessor :lecture_number, :lecture_date
|
11
11
|
|
12
12
|
# Callable with nitem=nil to create a mock
|
@@ -25,7 +25,6 @@ class CItem
|
|
25
25
|
@order = order
|
26
26
|
@title = ident
|
27
27
|
@style = nil
|
28
|
-
@collapsed = nil
|
29
28
|
parse_identifier ident
|
30
29
|
end
|
31
30
|
end
|
@@ -46,6 +45,10 @@ class CItem
|
|
46
45
|
!@lecture_date.nil?
|
47
46
|
end
|
48
47
|
|
48
|
+
def collapsed?
|
49
|
+
@collapsed = @nitem[:collapsed].nil? ? true : @nitem[:collapsed]
|
50
|
+
end
|
51
|
+
|
49
52
|
def lecture_date_s
|
50
53
|
@lecture_date.strftime('%a %b %-d') unless @lecture_date.nil?
|
51
54
|
end
|
@@ -67,7 +70,6 @@ class CItem
|
|
67
70
|
@section = @nitem[:section]
|
68
71
|
@title = @nitem[:title]
|
69
72
|
@css_class = @nitem[:css_class]
|
70
|
-
@collapsed = @nitem[:collapsed].nil? ? true : @nitem[:collapsed]
|
71
73
|
end
|
72
74
|
|
73
75
|
def parse_identifier ident
|
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'tree'
|
2
2
|
|
3
3
|
class Lectures < Section
|
4
|
-
def initialize
|
5
|
-
super sect, citems
|
4
|
+
def initialize(sect, citems, schedule=nil, collapsed=false)
|
5
|
+
super sect, citems, collapsed
|
6
6
|
@schedule = schedule || ::Scheduler.new
|
7
7
|
@citems = sort_items
|
8
8
|
build_tree
|
@@ -6,8 +6,9 @@ class Section
|
|
6
6
|
attr_reader :section
|
7
7
|
def_delegators :@citems, :[], :count, :each, :sort!, :reduce
|
8
8
|
|
9
|
-
def initialize sect, citems
|
9
|
+
def initialize (sect, citems, collapsed)
|
10
10
|
@section = sect
|
11
|
+
@collapsed = collapsed
|
11
12
|
@citems = section_filter(citems)
|
12
13
|
sort_pages
|
13
14
|
end
|
@@ -50,6 +51,10 @@ class Section
|
|
50
51
|
false
|
51
52
|
end
|
52
53
|
|
54
|
+
def collapsed?
|
55
|
+
@collapsed
|
56
|
+
end
|
57
|
+
|
53
58
|
protected
|
54
59
|
|
55
60
|
def lookup_citem_by_identifier identifier
|
@@ -59,7 +64,7 @@ class Section
|
|
59
64
|
res[0]
|
60
65
|
end
|
61
66
|
|
62
|
-
|
67
|
+
# Remove citems that don't belong in this section, or are hidden
|
63
68
|
def section_filter citems
|
64
69
|
filtered_citems = citems.map do
|
65
70
|
|citem|
|
@@ -21,9 +21,9 @@ class Toc
|
|
21
21
|
if sect.options[:type] == :lecture
|
22
22
|
schedule = Scheduler.new
|
23
23
|
schedule.setup_from_schedule_def(sect.options[:schedule])
|
24
|
-
@sections[selector] = Lectures.new(selector, items, schedule)
|
24
|
+
@sections[selector] = Lectures.new(selector, items, schedule, sect.options[:collapsed])
|
25
25
|
elsif sect.options[:type] == :section
|
26
|
-
@sections[selector] = Section.new(selector, items)
|
26
|
+
@sections[selector] = Section.new(selector, items, sect.options[:collapsed])
|
27
27
|
else
|
28
28
|
raise ArgumentError.new("Invalid section option")
|
29
29
|
end
|
data/lib/coursegen/version.rb
CHANGED
data/spec/citem_spec.rb
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
require_relative "./spec_helper.rb"
|
2
|
+
require 'pry'
|
3
|
+
|
4
|
+
class NIMock
|
5
|
+
attr_reader :identifier
|
6
|
+
def initialize nm, order, section
|
7
|
+
@identifier = nm
|
8
|
+
@order = order
|
9
|
+
@section = section
|
10
|
+
end
|
11
|
+
|
12
|
+
def [](arg)
|
13
|
+
case arg
|
14
|
+
when :order
|
15
|
+
@order
|
16
|
+
when :section
|
17
|
+
@section
|
18
|
+
else
|
19
|
+
nil
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe "null Case" do
|
25
|
+
before(:each) do
|
26
|
+
@ni = NIMock.new('/content/lectures/iter4/db_design/', 7, "lectures")
|
27
|
+
@ci = CItem.new(@ni)
|
28
|
+
end
|
29
|
+
it "works as expected" do
|
30
|
+
expect(@ci.title).to eq("db_design")
|
31
|
+
expect(@ci.section).to eq("lectures")
|
32
|
+
expect(@ci.order).to eq(7)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "more complicated cases" do
|
37
|
+
|
38
|
+
CASES = [
|
39
|
+
['/content/intro/term_overview/', 7, "intro", "term_overview", "intro", 7, "/content/intro/"],
|
40
|
+
['/content/intro/001_term_overview/', nil, "intro", "term_overview", "intro", 1, "/content/intro/"],
|
41
|
+
['/content/lectures/iter2/02_db_design/', 7, "lectures", "db_design", "lectures", 7, "/content/lectures/iter2/"],
|
42
|
+
['/content/', 7, "lectures", "content", "lectures", 7, "//"],
|
43
|
+
]
|
44
|
+
|
45
|
+
context "table driven cases" do
|
46
|
+
CASES.each do |c|
|
47
|
+
ni = NIMock.new(c[0], c[1], c[2])
|
48
|
+
ci = CItem.new(ni)
|
49
|
+
|
50
|
+
it { expect(ci.title).to eq c[3]}
|
51
|
+
it { expect(ci.section).to eq c[4]}
|
52
|
+
it { expect(ci.order).to eq c[5]}
|
53
|
+
it { expect(ci.subsection).to eq c[6]}
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require_relative "./spec_helper.rb"
|
2
|
+
describe Lectures do
|
3
|
+
before do
|
4
|
+
mocks1_templates =
|
5
|
+
[["/lectures/intro/wonders/", "subsection", 0],
|
6
|
+
["/lectures/intro/wonders/page2/", "page", 1],
|
7
|
+
["/lectures/intro/wonders/page3/", "page", 2],
|
8
|
+
["/lectures/intro/wonder2/", "subsection", 1],
|
9
|
+
["/lectures/intro/wonder2/page22/", "page", 1],
|
10
|
+
["/lectures/intro/wonder2/page32/", "page",2 ]
|
11
|
+
]
|
12
|
+
@citems1 = mocks1_templates.map { |i, t, o| CItem.new(nil, i, t, o) }
|
13
|
+
@sect = Lectures.new "intro", @citems1
|
14
|
+
end
|
15
|
+
|
16
|
+
it "correctly counts subsections" do
|
17
|
+
expect(@sect.subsections.length).to eq 2
|
18
|
+
end
|
19
|
+
|
20
|
+
it "correctly handles next from page to page " do
|
21
|
+
expect(@sect.next_for(@citems1[1])).to eq @citems1[2]
|
22
|
+
end
|
23
|
+
|
24
|
+
it "correctly handles prev from page to page " do
|
25
|
+
expect(@sect.previous_for(@citems1[2])).to eq @citems1[1]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "correctly handles prev from first page in section " do
|
29
|
+
expect(@sect.previous_for(@citems1[1])).to eq @citems1[1]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "correctly handles next from last page in section " do
|
33
|
+
expect(@sect.next_for(@citems1[5])).to eq @citems1[5]
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
it "correctly handles next from subsection to subseciton" do
|
38
|
+
expect(@sect.next_for(@citems1[0])).to eq @citems1[3]
|
39
|
+
end
|
40
|
+
|
41
|
+
it "correctly handles next from last subsection" do
|
42
|
+
expect(@sect.next_for(@citems1[3])).to eq @citems1[3]
|
43
|
+
end
|
44
|
+
|
45
|
+
it { expect(@citems1[0].lecture_number).to eq 1 }
|
46
|
+
it { expect(@citems1[1].lecture_number).to eq 1 }
|
47
|
+
it { expect(@citems1[2].lecture_number).to eq 2 }
|
48
|
+
it { expect(@citems1[3].lecture_number).to eq 3 }
|
49
|
+
it { expect(@citems1[4].lecture_number).to eq 3 }
|
50
|
+
it { expect(@citems1[5].lecture_number).to eq 4 }
|
51
|
+
|
52
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'active_support/core_ext/date/conversions'
|
2
|
+
|
3
|
+
require_relative "./spec_helper.rb"
|
4
|
+
|
5
|
+
def s2d(string_date)
|
6
|
+
Date.strptime(string_date, "%b-%d-%Y") rescue binding.pry
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Scheduler do
|
10
|
+
let(:sched) {Scheduler.new}
|
11
|
+
let(:target_date) {"nov-25-2013"}
|
12
|
+
let(:target_date_bin) { s2d(target_date) }
|
13
|
+
|
14
|
+
describe "Single day scheduler" do
|
15
|
+
it "knows date for first event" do
|
16
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday], number: 1)
|
17
|
+
a_date = sched.event_date_by_index 0
|
18
|
+
expect(a_date).to eq target_date_bin
|
19
|
+
end
|
20
|
+
|
21
|
+
it "knows date for second event" do
|
22
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday] ,number: 2)
|
23
|
+
a_date = sched.event_date_by_index 1
|
24
|
+
expect(a_date).to eq target_date_bin + 7
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "Double day scheduler" do
|
29
|
+
it "knows date for first event" do
|
30
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :tuesday] ,number: 1)
|
31
|
+
a_date = sched.event_date_by_index 0
|
32
|
+
expect(a_date).to eq target_date_bin
|
33
|
+
end
|
34
|
+
|
35
|
+
it "knows date for second event" do
|
36
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :tuesday] ,number: 2)
|
37
|
+
a_date = sched.event_date_by_index 1
|
38
|
+
expect(a_date).to eq target_date_bin + 1
|
39
|
+
end
|
40
|
+
|
41
|
+
it "knows date for third event" do
|
42
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :tuesday] ,number: 3)
|
43
|
+
a_date = sched.event_date_by_index 2
|
44
|
+
expect(a_date).to eq target_date_bin + 7
|
45
|
+
end
|
46
|
+
|
47
|
+
it "knows date for fourth event" do
|
48
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :tuesday] ,number: 4)
|
49
|
+
a_date = sched.event_date_by_index 3
|
50
|
+
expect(a_date).to eq target_date_bin + 7 + 1
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "Tripple day scheduler" do
|
55
|
+
it "knows date for first event" do
|
56
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :wednesday, :friday], number: 1)
|
57
|
+
a_date = sched.event_date_by_index 0
|
58
|
+
expect(a_date).to eq target_date_bin
|
59
|
+
end
|
60
|
+
it "knows date for second event" do
|
61
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :wednesday, :friday], number: 2)
|
62
|
+
a_date = sched.event_date_by_index 1
|
63
|
+
expect(a_date).to eq target_date_bin + 2
|
64
|
+
end
|
65
|
+
|
66
|
+
it "knows date for third event" do
|
67
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :wednesday, :friday], number: 3)
|
68
|
+
a_date = sched.event_date_by_index 2
|
69
|
+
expect(a_date).to eq target_date_bin + 4
|
70
|
+
end
|
71
|
+
it "knows date for fourth event" do
|
72
|
+
sched.setup_from_args(start: target_date, weekdays: [:monday, :wednesday, :friday], number: 4)
|
73
|
+
a_date = sched.event_date_by_index 3
|
74
|
+
expect(a_date).to eq target_date_bin + 7
|
75
|
+
end
|
76
|
+
|
77
|
+
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require_relative "./spec_helper.rb"
|
2
|
+
|
3
|
+
describe Section do
|
4
|
+
before do
|
5
|
+
@mock_citems =
|
6
|
+
[["welcome", :intro, false, 1],
|
7
|
+
["Day 1", :intro, false, 2],
|
8
|
+
["Day 1a", :intro, true,3 ]
|
9
|
+
].map {|t, s, h, o| double("CItem", title: t, section: s, hidden?: h, order: o) }
|
10
|
+
@sect = Section.new :intro, @mock_citems, false
|
11
|
+
end
|
12
|
+
|
13
|
+
it "can find the index" do
|
14
|
+
expect( @sect.find_index(@mock_citems[1] )).to eq 1
|
15
|
+
end
|
16
|
+
|
17
|
+
it "can look up by index" do
|
18
|
+
expect(@sect[1]).to eq @mock_citems[1]
|
19
|
+
end
|
20
|
+
|
21
|
+
it "can find next entry" do
|
22
|
+
expect( @sect.next_for(@mock_citems[0])).to eq @mock_citems[1]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "can find prev entry" do
|
26
|
+
expect( @sect.previous_for(@mock_citems[1])).to eq @mock_citems[0]
|
27
|
+
end
|
28
|
+
|
29
|
+
it "can deal with first" do
|
30
|
+
expect( @sect.previous_for(@mock_citems[0])).to eq @mock_citems[0]
|
31
|
+
end
|
32
|
+
|
33
|
+
it "can deal with last" do
|
34
|
+
expect( @sect.next_for(@mock_citems[1])).to eq @mock_citems[1]
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
end
|
39
|
+
|
40
|
+
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'pry'
|
3
|
+
require './lib/coursegen/course/lib/section'
|
4
|
+
require './lib/coursegen/course/lib/toc'
|
5
|
+
require './lib/coursegen/course/lib/lectures'
|
6
|
+
require './lib/coursegen/course/lib/citem'
|
7
|
+
require './lib/coursegen/course/lib/scheduler'
|
8
|
+
require './lib/coursegen/course/lib/section_def'
|
9
|
+
|
10
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
11
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
12
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
13
|
+
# loaded once.
|
14
|
+
#
|
15
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
16
|
+
RSpec.configure do |config|
|
17
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
18
|
+
#config.run_all_when_everything_filtered = true
|
19
|
+
#config.filter_run :focus
|
20
|
+
|
21
|
+
# Run specs in random order to surface order dependencies. If you find an
|
22
|
+
# order dependency and want to debug it, you can fix the order by providing
|
23
|
+
# the seed, which is printed after each run.
|
24
|
+
# --seed 1234
|
25
|
+
config.order = 'random'
|
26
|
+
end
|
data/spec/toc_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require_relative "./spec_helper.rb"
|
2
|
+
|
3
|
+
class MockItem
|
4
|
+
attr_accessor :identifier, :attributes
|
5
|
+
def initialize name, att = {}
|
6
|
+
@attributes = { :section => "lectures" }.merge(att)
|
7
|
+
@identifier = name
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](index)
|
11
|
+
@attributes[index]
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.gen (names)
|
15
|
+
items = []
|
16
|
+
order = [5,2,12,4,8,3,1,6]
|
17
|
+
names.each { |nm| items << MockItem.new(nm, {order: order.pop}) }
|
18
|
+
items
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe Toc do
|
23
|
+
context "simple sections" do
|
24
|
+
let(:items) {
|
25
|
+
MockItem.gen(
|
26
|
+
%w(/content/lectures/hypotheses/
|
27
|
+
/content/lectures/ideation/
|
28
|
+
/content/lectures/lean_immersion/welcome/
|
29
|
+
/content/lectures/lean_startup/) ) }
|
30
|
+
|
31
|
+
it "initializes" do
|
32
|
+
toc = Toc.instance
|
33
|
+
toc.reset
|
34
|
+
toc.prepare items, [SectionDef.new("Lectures", "lectures", type: :section)]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context "a little more complicated" do
|
39
|
+
|
40
|
+
describe "next and previous methods" do
|
41
|
+
let(:items) {
|
42
|
+
MockItem.gen(%w(/content/lectures/a/
|
43
|
+
/content/lectures/b/
|
44
|
+
/content/lectures/c/
|
45
|
+
/content/lectures/d/) )
|
46
|
+
}
|
47
|
+
|
48
|
+
before (:each) do
|
49
|
+
@toc = Toc.instance
|
50
|
+
@toc.reset
|
51
|
+
@toc.prepare items, [SectionDef.new("Lectures", "lectures", type: :section)]
|
52
|
+
end
|
53
|
+
|
54
|
+
it "next" do
|
55
|
+
sect = @toc.section("lectures")
|
56
|
+
cprev = sect[1]
|
57
|
+
c_plus_one = @toc.find_next_for(cprev)
|
58
|
+
expect(c_plus_one.title).to eq sect[2].title
|
59
|
+
end
|
60
|
+
|
61
|
+
it "prev" do
|
62
|
+
sect = @toc.section("lectures")
|
63
|
+
cprev = sect[2]
|
64
|
+
c_plus_one = @toc.find_previous_for(cprev)
|
65
|
+
expect(c_plus_one.title).to eq sect[1].title
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
context "lookup and access methods" do
|
71
|
+
let(:items) {
|
72
|
+
MockItem.gen(%w(/content/lectures/a/
|
73
|
+
/content/lectures/b/
|
74
|
+
/content/lectures/c/
|
75
|
+
/content/lectures/d/) )
|
76
|
+
}
|
77
|
+
|
78
|
+
before (:each) do
|
79
|
+
@toc = Toc.instance
|
80
|
+
@toc.reset
|
81
|
+
@toc.prepare items, [SectionDef.new("Lectures", "lectures", type: :section)]
|
82
|
+
end
|
83
|
+
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
data/templates/cg_config.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: coursegen
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Pito Salas
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-05-
|
11
|
+
date: 2014-05-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -113,6 +113,12 @@ files:
|
|
113
113
|
- lib/coursegen/course/lib/toc.rb
|
114
114
|
- lib/coursegen/templates.rb
|
115
115
|
- lib/coursegen/version.rb
|
116
|
+
- spec/citem_spec.rb
|
117
|
+
- spec/lectures_spec.rb
|
118
|
+
- spec/scheduler_spec.rb
|
119
|
+
- spec/section_spec.rb
|
120
|
+
- spec/spec_helper.rb
|
121
|
+
- spec/toc_spec.rb
|
116
122
|
- templates/Rules
|
117
123
|
- templates/cg_config.rb
|
118
124
|
- templates/cg_config.rb_sample
|
@@ -125,12 +131,6 @@ files:
|
|
125
131
|
- templates/layouts/course.html
|
126
132
|
- templates/layouts/main_navbar.html.erb
|
127
133
|
- templates/lib/default.rb
|
128
|
-
- test1/Rules
|
129
|
-
- test1/content/index.html
|
130
|
-
- test1/content/stylesheet.css
|
131
|
-
- test1/layouts/default.html
|
132
|
-
- test1/lib/default.rb
|
133
|
-
- test1/nanoc.yaml
|
134
134
|
homepage: ''
|
135
135
|
licenses:
|
136
136
|
- MIT
|
@@ -155,5 +155,11 @@ rubygems_version: 2.2.2
|
|
155
155
|
signing_key:
|
156
156
|
specification_version: 4
|
157
157
|
summary: Use Nanoc to build courses and deploy them to S3
|
158
|
-
test_files:
|
158
|
+
test_files:
|
159
|
+
- spec/citem_spec.rb
|
160
|
+
- spec/lectures_spec.rb
|
161
|
+
- spec/scheduler_spec.rb
|
162
|
+
- spec/section_spec.rb
|
163
|
+
- spec/spec_helper.rb
|
164
|
+
- spec/toc_spec.rb
|
159
165
|
has_rdoc:
|
data/test1/Rules
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
# A few helpful tips about the Rules file:
|
4
|
-
#
|
5
|
-
# * The string given to #compile and #route are matching patterns for
|
6
|
-
# identifiers--not for paths. Therefore, you can’t match on extension.
|
7
|
-
#
|
8
|
-
# * The order of rules is important: for each item, only the first matching
|
9
|
-
# rule is applied.
|
10
|
-
#
|
11
|
-
# * Item identifiers start and end with a slash (e.g. “/about/” for the file
|
12
|
-
# “content/about.html”). To select all children, grandchildren, … of an
|
13
|
-
# item, use the pattern “/about/*/”; “/about/*” will also select the parent,
|
14
|
-
# because “*” matches zero or more characters.
|
15
|
-
|
16
|
-
compile '*' do
|
17
|
-
if item[:extension] == 'css'
|
18
|
-
# don’t filter stylesheets
|
19
|
-
elsif item.binary?
|
20
|
-
# don’t filter binary items
|
21
|
-
else
|
22
|
-
filter :erb
|
23
|
-
layout 'default'
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
route '*' do
|
28
|
-
if item[:extension] == 'css'
|
29
|
-
# Write item with identifier /foo/ to /foo.css
|
30
|
-
item.identifier.chop + '.css'
|
31
|
-
elsif item.binary?
|
32
|
-
# Write item with identifier /foo/ to /foo.ext
|
33
|
-
item.identifier.chop + '.' + item[:extension]
|
34
|
-
else
|
35
|
-
# Write item with identifier /foo/ to /foo/index.html
|
36
|
-
item.identifier + 'index.html'
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
layout '*', :erb
|
data/test1/content/index.html
DELETED
@@ -1,14 +0,0 @@
|
|
1
|
-
---
|
2
|
-
title: Home
|
3
|
-
---
|
4
|
-
|
5
|
-
<h1>A Brand New nanoc Site</h1>
|
6
|
-
|
7
|
-
<p>You’ve just created a new nanoc site. The page you are looking at right now is the home page for your site. To get started, consider replacing this default homepage with your own customized homepage. Some pointers on how to do so:</p>
|
8
|
-
|
9
|
-
<ul>
|
10
|
-
<li><p><strong>Change this page’s content</strong> by editing the “index.html” file in the “content” directory. This is the actual page content, and therefore doesn’t include the header, sidebar or style information (those are part of the layout).</p></li>
|
11
|
-
<li><p><strong>Change the layout</strong>, which is the “default.html” file in the “layouts” directory, and create something unique (and hopefully less bland).</p></li>
|
12
|
-
</ul>
|
13
|
-
|
14
|
-
<p>If you need any help with customizing your nanoc web site, be sure to check out the documentation (see sidebar), and be sure to subscribe to the discussion group (also see sidebar). Enjoy!</p>
|
@@ -1,101 +0,0 @@
|
|
1
|
-
* {
|
2
|
-
margin: 0;
|
3
|
-
padding: 0;
|
4
|
-
|
5
|
-
font-family: Georgia, Palatino, serif;
|
6
|
-
}
|
7
|
-
|
8
|
-
body {
|
9
|
-
background: #fff;
|
10
|
-
}
|
11
|
-
|
12
|
-
a {
|
13
|
-
text-decoration: none;
|
14
|
-
}
|
15
|
-
|
16
|
-
a:link,
|
17
|
-
a:visited {
|
18
|
-
color: #f30;
|
19
|
-
}
|
20
|
-
|
21
|
-
a:hover {
|
22
|
-
color: #f90;
|
23
|
-
}
|
24
|
-
|
25
|
-
#main {
|
26
|
-
position: absolute;
|
27
|
-
|
28
|
-
top: 40px;
|
29
|
-
left: 280px;
|
30
|
-
|
31
|
-
width: 500px;
|
32
|
-
}
|
33
|
-
|
34
|
-
#main h1 {
|
35
|
-
font-size: 40px;
|
36
|
-
font-weight: normal;
|
37
|
-
|
38
|
-
line-height: 40px;
|
39
|
-
|
40
|
-
letter-spacing: -1px;
|
41
|
-
}
|
42
|
-
|
43
|
-
#main p {
|
44
|
-
margin: 20px 0;
|
45
|
-
|
46
|
-
font-size: 15px;
|
47
|
-
|
48
|
-
line-height: 20px;
|
49
|
-
}
|
50
|
-
|
51
|
-
#main ul, #main ol {
|
52
|
-
margin: 20px;
|
53
|
-
}
|
54
|
-
|
55
|
-
#main li {
|
56
|
-
font-size: 15px;
|
57
|
-
|
58
|
-
line-height: 20px;
|
59
|
-
}
|
60
|
-
|
61
|
-
#main ul li {
|
62
|
-
list-style-type: square;
|
63
|
-
}
|
64
|
-
|
65
|
-
#sidebar {
|
66
|
-
position: absolute;
|
67
|
-
|
68
|
-
top: 40px;
|
69
|
-
left: 20px;
|
70
|
-
width: 200px;
|
71
|
-
|
72
|
-
padding: 20px 20px 0 0;
|
73
|
-
|
74
|
-
border-right: 1px solid #ccc;
|
75
|
-
|
76
|
-
text-align: right;
|
77
|
-
}
|
78
|
-
|
79
|
-
#sidebar h2 {
|
80
|
-
text-transform: uppercase;
|
81
|
-
|
82
|
-
font-size: 13px;
|
83
|
-
|
84
|
-
color: #333;
|
85
|
-
|
86
|
-
letter-spacing: 1px;
|
87
|
-
|
88
|
-
line-height: 20px;
|
89
|
-
}
|
90
|
-
|
91
|
-
#sidebar ul {
|
92
|
-
list-style-type: none;
|
93
|
-
|
94
|
-
margin: 20px 0;
|
95
|
-
}
|
96
|
-
|
97
|
-
#sidebar li {
|
98
|
-
font-size: 14px;
|
99
|
-
|
100
|
-
line-height: 20px;
|
101
|
-
}
|
data/test1/layouts/default.html
DELETED
@@ -1,29 +0,0 @@
|
|
1
|
-
<!DOCTYPE HTML>
|
2
|
-
<html lang="en">
|
3
|
-
<head>
|
4
|
-
<meta charset="utf-8">
|
5
|
-
<title>A Brand New nanoc Site - <%= @item[:title] %></title>
|
6
|
-
<link rel="stylesheet" href="<%= @items['/stylesheet/'].path %>">
|
7
|
-
|
8
|
-
<!-- you don't need to keep this, but it's cool for stats! -->
|
9
|
-
<meta name="generator" content="nanoc <%= Nanoc::VERSION %>">
|
10
|
-
</head>
|
11
|
-
<body>
|
12
|
-
<div id="main">
|
13
|
-
<%= yield %>
|
14
|
-
</div>
|
15
|
-
<div id="sidebar">
|
16
|
-
<h2>Documentation</h2>
|
17
|
-
<ul>
|
18
|
-
<li><a href="http://nanoc.ws/docs/">Documentation</a></li>
|
19
|
-
<li><a href="http://nanoc.ws/docs/tutorial/">Getting Started</a></li>
|
20
|
-
</ul>
|
21
|
-
<h2>Community</h2>
|
22
|
-
<ul>
|
23
|
-
<li><a href="http://groups.google.com/group/nanoc/">Discussion Group</a></li>
|
24
|
-
<li><a href="irc://chat.freenode.net/#nanoc">IRC Channel</a></li>
|
25
|
-
<li><a href="http://github.com/nanoc/nanoc/wiki/">Wiki</a></li>
|
26
|
-
</ul>
|
27
|
-
</div>
|
28
|
-
</body>
|
29
|
-
</html>
|
data/test1/lib/default.rb
DELETED
data/test1/nanoc.yaml
DELETED
@@ -1,62 +0,0 @@
|
|
1
|
-
# A list of file extensions that nanoc will consider to be textual rather than
|
2
|
-
# binary. If an item with an extension not in this list is found, the file
|
3
|
-
# will be considered as binary.
|
4
|
-
text_extensions: [ 'coffee', 'css', 'erb', 'haml', 'handlebars', 'hb', 'htm', 'html', 'js', 'less', 'markdown', 'md', 'ms', 'mustache', 'php', 'rb', 'sass', 'scss', 'txt', 'xhtml', 'xml' ]
|
5
|
-
|
6
|
-
# The path to the directory where all generated files will be written to. This
|
7
|
-
# can be an absolute path starting with a slash, but it can also be path
|
8
|
-
# relative to the site directory.
|
9
|
-
output_dir: output
|
10
|
-
|
11
|
-
# A list of index filenames, i.e. names of files that will be served by a web
|
12
|
-
# server when a directory is requested. Usually, index files are named
|
13
|
-
# “index.html”, but depending on the web server, this may be something else,
|
14
|
-
# such as “default.htm”. This list is used by nanoc to generate pretty URLs.
|
15
|
-
index_filenames: [ 'index.html' ]
|
16
|
-
|
17
|
-
# Whether or not to generate a diff of the compiled content when compiling a
|
18
|
-
# site. The diff will contain the differences between the compiled content
|
19
|
-
# before and after the last site compilation.
|
20
|
-
enable_output_diff: false
|
21
|
-
|
22
|
-
prune:
|
23
|
-
# Whether to automatically remove files not managed by nanoc from the output
|
24
|
-
# directory. For safety reasons, this is turned off by default.
|
25
|
-
auto_prune: false
|
26
|
-
|
27
|
-
# Which files and directories you want to exclude from pruning. If you version
|
28
|
-
# your output directory, you should probably exclude VCS directories such as
|
29
|
-
# .git, .svn etc.
|
30
|
-
exclude: [ '.git', '.hg', '.svn', 'CVS' ]
|
31
|
-
|
32
|
-
# The data sources where nanoc loads its data from. This is an array of
|
33
|
-
# hashes; each array element represents a single data source. By default,
|
34
|
-
# there is only a single data source that reads data from the “content/” and
|
35
|
-
# “layout/” directories in the site directory.
|
36
|
-
data_sources:
|
37
|
-
-
|
38
|
-
# The type is the identifier of the data source. By default, this will be
|
39
|
-
# `filesystem_unified`.
|
40
|
-
type: filesystem_unified
|
41
|
-
|
42
|
-
# The path where items should be mounted (comparable to mount points in
|
43
|
-
# Unix-like systems). This is “/” by default, meaning that items will have
|
44
|
-
# “/” prefixed to their identifiers. If the items root were “/en/”
|
45
|
-
# instead, an item at content/about.html would have an identifier of
|
46
|
-
# “/en/about/” instead of just “/about/”.
|
47
|
-
items_root: /
|
48
|
-
|
49
|
-
# The path where layouts should be mounted. The layouts root behaves the
|
50
|
-
# same as the items root, but applies to layouts rather than items.
|
51
|
-
layouts_root: /
|
52
|
-
|
53
|
-
# Whether to allow periods in identifiers. When turned off, everything
|
54
|
-
# past the first period is considered to be the extension, and when
|
55
|
-
# turned on, only the characters past the last period are considered to
|
56
|
-
# be the extension. For example, a file named “content/about.html.erb”
|
57
|
-
# will have the identifier “/about/” when turned off, but when turned on
|
58
|
-
# it will become “/about.html/” instead.
|
59
|
-
allow_periods_in_identifiers: false
|
60
|
-
|
61
|
-
# The default encoding for all files in `content/` and `layouts/`.
|
62
|
-
encoding: utf-8
|