contraption 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d1c34da43377fb2b0cec9e0c8cf821867a18a85b
4
- data.tar.gz: 8623c83b4113c1878756257ea4f43f02d8fa8ab4
3
+ metadata.gz: db47cc8b0f80c9b56c5ce15af3e3d02201d2a3d2
4
+ data.tar.gz: 04dd17291329b373f04780fb7dccebde4abb1064
5
5
  SHA512:
6
- metadata.gz: 873f12c0aa72c64145879a0b09564419aef292d60fd3eee8d00553c9639279ec353b21a0c5bc77e3558ad6dc8ab7223f34b186b98263daff0c847aa1c20e4d4a
7
- data.tar.gz: 78c1b4f321c8327e687f542dfc508a0f4bf8df61ed8e71a4f463fd1a5e2bdced4b9b5bf82987d5e642c5bd5f7b721e38612a2b6f2c248eceb18e7e001f195fb9
6
+ metadata.gz: 5da3efa5065cd2a501f2d67f40af417db884f49549a3acd4319f9fd5fe27903a73debd3507b7e76a0fd753d1155d3ebe92d53a006c2798c493c1a6ad3252253a
7
+ data.tar.gz: 63e2c8eee732f05dc0d6b1a73f87a2495cc6a9085f8fe79dc6f02c6685582b3e80fdb987d5e6b9f6719d9d6bac84c47a3f7785015e721c0b4db21e093fdadaf0
data/.gitignore CHANGED
@@ -7,7 +7,6 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
- doc/
11
10
  lib/bundler/man
12
11
  pkg
13
12
  rdoc
data/README.md CHANGED
@@ -1,24 +1,31 @@
1
1
  # Contraption
2
2
 
3
- TODO: Write a gem description
3
+ __Contraption__ - *Informal, often facetious or derogatory, a device or contrivance, esp one considered strange, unnecessarily intricate, or improvised*
4
+
5
+ Contraption is a engine for managing static web content specifically targeted at blogging.
4
6
 
5
7
  ## Installation
6
8
 
7
- Add this line to your application's Gemfile:
9
+ $ gem install contraption
8
10
 
9
- gem 'contraption'
11
+ ## Usage
10
12
 
11
- And then execute:
13
+ __Step 1__: Generate new content directory.
12
14
 
13
- $ bundle
15
+ bin/contraption generate -d my_content
14
16
 
15
- Or install it yourself as:
17
+ __Step 2__: Write a new draft.
16
18
 
17
- $ gem install contraption
19
+ vim drafts/why_contraption_is_awesome.md
18
20
 
19
- ## Usage
21
+ __Step 3__: Preview site.
22
+
23
+ bin/contraption preview -s my_content
24
+
25
+ __Step 4__: Build website.
26
+
27
+ bin/contraption -s my_content -d public_html
20
28
 
21
- TODO: Write usage instructions here
22
29
 
23
30
  ## Development
24
31
  [![Code Climate](https://codeclimate.com/github/rampantmonkey/contraption.png)](https://codeclimate.com/github/rampantmonkey/contraption)
@@ -31,3 +38,8 @@ TODO: Write usage instructions here
31
38
  3. Commit your changes (`git commit -am 'Add some feature'`)
32
39
  4. Push to the branch (`git push origin my-new-feature`)
33
40
  5. Create new Pull Request
41
+
42
+ If you are looking for ideas check out the [issue tracker](https://github.com/rampantmonkey/contraption/issues).
43
+
44
+ ## License
45
+ Contraption is licensed under [The MIT License](http://opensource.org/licenses/MIT).
data/Rakefile CHANGED
@@ -8,5 +8,9 @@ Cucumber::Rake::Task.new(:features) do |t|
8
8
  t.cucumber_opts = "features --format pretty"
9
9
  end
10
10
 
11
+ task :doc do
12
+ `dot -Tpng -o doc/classes.png doc/classes.dot`
13
+ end
14
+
11
15
  task :default => [:spec, :features]
12
16
 
@@ -0,0 +1,8 @@
1
+ require 'benchmark'
2
+ require_relative 'lib/contraption/command_selector'
3
+
4
+ elapsed_time = Benchmark.realtime do
5
+ Contraption::CommandSelector.go ARGV
6
+ end
7
+
8
+ puts "Time: #{elapsed_time}s"
@@ -1,7 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+ require 'contraption/command_selector'
2
4
 
3
- require 'contraption/options'
4
- require 'contraption/runner'
5
-
6
- options = Contraption::Options.new ARGV
7
- Contraption::Runner.new(options).run!
5
+ Contraption::CommandSelector.go ARGV
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <!-- This is an example generated by Contraption
3
+ 'landing_page.erb' represents the first page visitors will see when visiting your website.
4
+ Currently this is just a copy of 'page.rb', but it provides an opportunity for customization.
5
+ -->
6
+
7
+ <body>
8
+ <nav>
9
+ <ul>
10
+ <li><a href="">Home</a></li>
11
+ <li><a href="recent.html">Posts</a></li>
12
+ <li><a href="about.html">About Me</a></li>
13
+ </ul>
14
+ </nav>
15
+ <div class="content">
16
+ <!-- the 'context' tag below is the outlet which tells contraption to "insert text here". -->
17
+ <%= context %>
18
+ </div>
19
+ <footer>
20
+ <p>&copy; Your Name Goes Here <%= Time.new.year %></p>
21
+ </footer>
22
+ </body>
@@ -0,0 +1,22 @@
1
+ <!DOCTYPE html>
2
+ <!-- This is an example generated by Contraption
3
+ 'page.erb' represents the outermost layer of every page in the website.
4
+ You should put the structure of your page along with common elements (navagation, stylesheets, footer, etc.)
5
+ -->
6
+
7
+ <body>
8
+ <nav>
9
+ <ul>
10
+ <li><a href="">Home</a></li>
11
+ <li><a href="recent.html">Posts</a></li>
12
+ <li><a href="about.html">About Me</a></li>
13
+ </ul>
14
+ </nav>
15
+ <div class="content">
16
+ <!-- the 'context' tag below is the outlet which tells contraption to "insert text here". -->
17
+ <%= context %>
18
+ </div>
19
+ <footer>
20
+ <p>&copy; Your Name Goes Here <%= Time.new.year %></p>
21
+ </footer>
22
+ </body>
@@ -0,0 +1,2 @@
1
+ <h1>Tag Cloud</h1>
2
+ <%= context[:content] %>
@@ -0,0 +1,118 @@
1
+ digraph G {
2
+ rankdir = "BT"
3
+ fontname = "Menlo-Regular"
4
+ fontsize = 8
5
+
6
+ node [
7
+ fontname = "Menlo-Regular"
8
+ fontsize = 8
9
+ shape = "record"
10
+ ]
11
+
12
+ edge [
13
+ fontname = "Menlo-Regular"
14
+ fontsize = 8
15
+ ]
16
+
17
+ // Classes
18
+ AwsS3 [ label="AWS::S3" ]
19
+ Catalog [ label = "{Catalog|+ initialize(items)\l+ \<\<(new_item)\l+ by_year()\l+ by_month()\l+ by_day()\l+ by_tag()\l+ each()\l+ most_recent(n=1)}" ]
20
+ CommandSelector [ label="{CommandSelector|+ go(args)}" ]
21
+ Day [ label="{Day}" ]
22
+ Diagnoser [ label="Diagnoser}" ]
23
+ Enumerable [ label = "{«module»\nEnumerable}" ]
24
+ Erb [ label = "{ERB}" ]
25
+ Formatter [ label = "{Formatter|+ initialize(location)\l+ formats()\l+ format(context,method=nil)}" ]
26
+ Generator [ label="{Generator}" ]
27
+ Header [ label = "{Header|+ from(string)|+ initialize(opts=\{\})\l+ filename(ext)\l+ new?\l+ update(new_opts=\{\})}" ]
28
+ HttpHandler [ label="{HttpHandler}" ]
29
+ Location [ label="{Location|+ initialize(path='.')\l+ list(ext=/.*/)\l+ path()\l+ cd(dir)\l+ create!()\l+ remove(file_name)\l+ ==(other)\l+ exist?\l+ executable?\l+ readable?\l+ read(file_name)\l+ writable?\l+ write(file_name, content)}" ]
30
+ Month [ label="{Month}" ]
31
+ Options [ label="{Options|+ initialize(args)}" ]
32
+ Optparse [ label="{OptionParser}" ]
33
+ Orchestrator [ label="{Orchestrator}" ]
34
+ Post [ label="{Post|+ build(data='')\l+ translate_markdown content|+ body()\l+ metadata\l+ publish(handlers=[])}" ]
35
+ Previewer [ label="{Previewer}" ]
36
+ RedcarpetMarkdown [ label="{Redcarpet::Markdown}" ]
37
+ RedcarpetRender [ label="{Redcarpet::Render}" ]
38
+ Repository [ label="{Repository|+ initialize(path)\l+ formatter()\l+ validate()\l+ valid_layout?\l+ construct_locations()\l+ drafts()\l+ posts()\l+ static_files()\l+ completed_drafts()\l+ finalize_completed_drafts(link_handlers=[])}" ]
39
+ RssBuilder [ label="{RSSBuilder|+ to_rss()\l+ initialize(posts)}" ]
40
+ RssMaker [ label="{RSS::Maker}" ]
41
+ Runner [ label="{Runner}" ]
42
+ S3Uploader [ label="{S3Uploader|+ initialize(connection_args=\{\},\r target_bucket,\r path)}" ]
43
+ Site [ label="{Site|+ initialize(location, posts, formats)\l+ build_all_individuals()\+ build_individual(i)\l+ build_month_pages()\l+ build_year_pages()\l+ build_tag_pages()\l+ build_recent()\l+ build_landing()\l+ build_tag_cloud()\l+ build_rss()\l+ root()}" ]
44
+ Tag [ label="{Tag|+ initialize(text)\l+ to_s()\l+ to_url()\l+ to_sym()}" ]
45
+ TagCloud [ label="{TagCloud|+ initialize(posts)\l+ to_s()}" ]
46
+ Yaml [ label="{YAML}" ]
47
+ Year [ label="{Year}" ]
48
+
49
+ // Protocols
50
+ edge [
51
+ arrowhead = "onormal"
52
+ style = "dashed"
53
+ ]
54
+
55
+ ControlProtocol [ label="{«protocol»|+ run!\l+ initialize(options)}" ]
56
+
57
+ OptionProtocol [ label="{«protocol»|+ source\l+ destination}" ]
58
+ Options -> OptionProtocol
59
+
60
+ LinkHandler [ label="{«protocol»\nLinkHandler|+ protocol\l+ handle(link)}" ]
61
+ HttpHandler -> LinkHandler
62
+ S3Uploader -> LinkHandler
63
+
64
+ S3Credentials [ label="{«protocol»\nS3Credentials|+ s3_target_bucket\l+ s3_access_key_id\l+ s3_secret_access_key}" ]
65
+ Options -> S3Credentials
66
+
67
+ // Dependencies
68
+ edge [
69
+ arrowhead = "vee"
70
+ style = "dashed"
71
+ ]
72
+
73
+ Catalog -> Day
74
+ Catalog -> Month
75
+ Catalog -> Year
76
+ CommandSelector -> Diagnoser
77
+ CommandSelector -> Generator
78
+ CommandSelector -> Options
79
+ CommandSelector -> Previewer
80
+ CommandSelector -> Runner
81
+ Formatter -> Erb
82
+ Options -> Location
83
+ Options -> Optparse
84
+ Options -> Yaml
85
+ Orchestrator -> HttpHandler
86
+ Orchestrator -> OptionProtocol
87
+ Orchestrator -> Repository
88
+ Orchestrator -> Site
89
+ Post -> Header
90
+ Post -> LinkHandler
91
+ Post -> RedcarpetMarkdown
92
+ Post -> RedcarpetRender
93
+ Repository -> Catalog
94
+ Repository -> Formatter
95
+ Repository -> Post
96
+ RssBuilder -> RssMaker
97
+ Runner -> S3Credentials
98
+ Runner -> S3Uploader
99
+ S3Uploader -> AwsS3
100
+ S3Uploader -> S3Credentials
101
+ Site -> RssBuilder
102
+ Site -> Tag
103
+ Site -> TagCloud
104
+ TagCloud -> Tag
105
+
106
+ // Inheritance
107
+ edge [
108
+ arrowhead = "onormal"
109
+ style = "solid"
110
+ ]
111
+
112
+ Catalog -> Enumerable
113
+ Diagnoser -> ControlProtocol
114
+ Generator -> ControlProtocol
115
+ Orchestrator -> ControlProtocol
116
+ Previewer -> Orchestrator
117
+ Runner -> Orchestrator
118
+ }
Binary file
@@ -0,0 +1,12 @@
1
+ Feature: Repository is generated
2
+
3
+ As a new user
4
+ I want to create a valid directory structure
5
+ That will serve as input to Contraption
6
+ And include example template to help get started
7
+
8
+ Scenario: generate repository
9
+ Given I have a writeable directory
10
+ When I run contraption generate
11
+ Then I should have a valid project
12
+ And contraption should accept the generated repository as input
@@ -0,0 +1,22 @@
1
+ require_relative '../../lib/contraption/location'
2
+ require_relative '../../lib/contraption/repository'
3
+ require_relative '../../lib/contraption/command_selector'
4
+
5
+ Given(/^I have a writeable directory$/) do
6
+ @target_dir = 'new_repository'
7
+ FileUtils.rm_r @target_dir if File.exist? @target_dir
8
+ expect(Dir.mkdir(@target_dir)).to eq 0
9
+ end
10
+
11
+ When(/^I run contraption generate$/) do
12
+ Contraption::CommandSelector.go ['generate', '-d', @target_dir]
13
+ end
14
+
15
+ Then(/^I should have a valid project$/) do
16
+ path = Contraption::Location.new @target_dir
17
+ expect{Contraption::Repository.new path}.not_to raise_error
18
+ end
19
+
20
+ Then(/^contraption should accept the generated repository as input$/) do
21
+ expect{Contraption::CommandSelector.go ['-s', @target_dir, '-d', @target_dir + 'output']}.not_to raise_error
22
+ end
@@ -1,3 +1,3 @@
1
1
  After do
2
- `rm -rf tmp`
2
+ `rm -rf tmp #{@target_dir} #{@target_dir ? @target_dir+'output' : ''}`
3
3
  end
@@ -3,6 +3,8 @@ require "contraption/post"
3
3
  require "contraption/header"
4
4
  require "contraption/catalog"
5
5
  require "contraption/location"
6
+ require "contraption/orchestrator"
7
+ require "contraption/previewer"
6
8
  require "contraption/runner"
7
9
  require "contraption/tag"
8
10
  require "contraption/tag_cloud"
@@ -27,10 +27,9 @@ module Contraption
27
27
  end
28
28
 
29
29
  def by_tag
30
- all(:tags).each_with_object(Hash.new {|h,k| h[k] = []}) do |tag, result|
31
- items.each do |item|
32
- result[tag.to_sym] << item if item.tags.include? tag
33
- end
30
+ tags = all(:tags).uniq{|t| t.to_url}
31
+ items.each_with_object(Hash.new {|h,k| h[k] = []}) do |item, result|
32
+ item.tags.each{|t| result[t.to_url] << item}
34
33
  end
35
34
  end
36
35
 
@@ -0,0 +1,27 @@
1
+ require_relative 'diagnoser'
2
+ require_relative 'generator'
3
+ require_relative 'options'
4
+ require_relative 'runner'
5
+ require_relative 'previewer'
6
+
7
+ module Contraption
8
+ class CommandSelector
9
+ class << self
10
+ def go args
11
+ case args.first
12
+ when 'diagnose'
13
+ args.shift
14
+ Diagnoser
15
+ when 'generate'
16
+ args.shift
17
+ Generator
18
+ when 'preview'
19
+ args.shift
20
+ Previewer
21
+ else
22
+ Runner
23
+ end.send("new", Options.new(args)).run!
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,12 @@
1
+ module Contraption
2
+ class Diagnoser
3
+ def initialize args
4
+ @options = args.values
5
+ end
6
+
7
+ def run!
8
+ key_width = @options.keys.map(&:length).max + 1
9
+ @options.each_pair{|k,v| puts "#{(k.to_s << ':').ljust key_width} #{v}"}
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,35 @@
1
+ module Contraption
2
+ class Generator
3
+ def initialize args
4
+ @options = args
5
+ end
6
+
7
+ def run!
8
+ directories = ['drafts', 'posts']
9
+ directories.each do |d|
10
+ destination.cd d
11
+ end
12
+
13
+ init_git
14
+ copy_example_files
15
+ make_initial_commit
16
+ end
17
+
18
+ private
19
+ def destination
20
+ @options.destination
21
+ end
22
+
23
+ def init_git
24
+ `git init #{destination.path}`
25
+ end
26
+
27
+ def copy_example_files
28
+ `cp -r data/* #{destination.path}`
29
+ end
30
+
31
+ def make_initial_commit
32
+ `cd #{destination.path}; git add --all .; git commit -m 'Generated input for Contraption static site generator'`
33
+ end
34
+ end
35
+ end
@@ -51,6 +51,7 @@ module Contraption
51
51
 
52
52
  def initialize opts={}
53
53
  @defaults = { title: "",
54
+ external: nil,
54
55
  publication_date: "",
55
56
  summary: "",
56
57
  type: "",
@@ -16,10 +16,18 @@ module Contraption
16
16
  .map{ |f| f.basename.to_s }
17
17
  end
18
18
 
19
+ def entries
20
+ pn.entries.map{|e| e.expand_path(pn) }
21
+ end
22
+
19
23
  def path
20
24
  pn.to_s
21
25
  end
22
26
 
27
+ def to_s
28
+ pn.expand_path.to_s
29
+ end
30
+
23
31
  def read file_name
24
32
  complete_name = pn+file_name
25
33
  return :file_does_not_exist unless complete_name.exist?
@@ -29,6 +37,10 @@ module Contraption
29
37
  complete_name.open("r") { |f| yield f }
30
38
  end
31
39
 
40
+ def destroy
41
+ FileUtils.rm_rf pn.expand_path
42
+ end
43
+
32
44
  def cd directory
33
45
  l = Location.new(pn+directory)
34
46
  l.create! unless l.pn.directory?
@@ -36,6 +36,10 @@ module Contraption
36
36
  opts.on("-d", "--destination path", "Path to destination") do |path|
37
37
  @values[:destination] = Location.new path
38
38
  end
39
+ opts.on("-h", "--help") do
40
+ puts opts
41
+ exit
42
+ end
39
43
  opts.on("-s", "--source path", "Path to source") do |path|
40
44
  @values[:source] = Location.new path
41
45
  end
@@ -0,0 +1,54 @@
1
+ require_relative 'repository'
2
+ require_relative 'site'
3
+ require_relative 'http_handler'
4
+
5
+ module Contraption
6
+ class Orchestrator
7
+ def initialize options
8
+ @options = options
9
+ end
10
+
11
+ def run!
12
+ source.finalize_completed_drafts handlers
13
+ site.build_all_individuals
14
+ site.build_month_pages
15
+ site.build_year_pages
16
+ site.build_tag_pages
17
+ site.build_tag_cloud
18
+ site.build_recent
19
+ site.build_landing
20
+ site.build_rss
21
+ site.build_archive_navigation
22
+ copy_static_files
23
+
24
+ post_run
25
+ end
26
+
27
+ private
28
+ def site
29
+ @site ||= Site.new @options.destination, posts, formatter
30
+ end
31
+
32
+ def source
33
+ raise "Not implemented"
34
+ end
35
+
36
+ def post_run; end
37
+
38
+ def formatter
39
+ @formatter ||= source.formatter
40
+ end
41
+
42
+ def posts
43
+ @posts ||= source.posts
44
+ end
45
+
46
+ def copy_static_files
47
+ `cp -r #{source.static_file_path}/* #{site.root}` unless (Dir[source.static_file_path.to_s + "/*"]).length == 0
48
+ end
49
+
50
+ def handlers
51
+ [ HttpHandler.new ]
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'orchestrator'
2
+
3
+ module Contraption
4
+ class Previewer < Orchestrator
5
+ PORT = 9009
6
+
7
+ def post_run
8
+ trap("INT") { cleanup }
9
+ spawn_server
10
+ end
11
+
12
+ private
13
+ def source
14
+ @source ||= Repository.new(@options.source).clone
15
+ end
16
+
17
+ def cleanup
18
+ puts "\nremoving temporary files and shutting down server"
19
+ source.destroy
20
+ site.location.destroy
21
+ end
22
+
23
+ def spawn_server
24
+ puts "Previewing site on http://localhost:#{PORT}"
25
+ `ruby -run -e httpd #{site.root} -p #{PORT} &> /dev/null`
26
+ end
27
+ end
28
+ end
@@ -13,6 +13,16 @@ module Contraption
13
13
  @formatter = Formatter.new @formats_source
14
14
  end
15
15
 
16
+ def clone
17
+ new_path = @path.cd "../.tmp_#{Time.now.to_i}"
18
+ `cp -r #{@path.path}/* #{new_path.path}`
19
+ Repository.new new_path
20
+ end
21
+
22
+ def destroy
23
+ @path.destroy
24
+ end
25
+
16
26
  def validate
17
27
  raise "Invalid source directory structure in #{@path}" unless valid_layout?
18
28
  end
@@ -26,13 +36,24 @@ module Contraption
26
36
  end
27
37
 
28
38
  def posts
29
- Catalog.new( @posts_source.list.map{|p| Post.build(@posts_source.read p)} )
39
+ Catalog.new( @posts_source.list.map do |p|
40
+ begin
41
+ Post.build(@posts_source.read p)
42
+ rescue ArgumentError => e
43
+ STDERR.puts "Invalid post #{p}"
44
+ end
45
+ end.select{|p| p.title != ''}
46
+ )
30
47
  end
31
48
 
32
- def static_files
49
+ def static_file_path
33
50
  @static_source.path
34
51
  end
35
52
 
53
+ def static_files
54
+ @static_source.entries
55
+ end
56
+
36
57
  def completed_drafts
37
58
  drafts.map{|draft| [draft, @drafts_source.read(draft)]}
38
59
  .each_with_object([]) do |draft, result|
@@ -1,60 +1,27 @@
1
- require_relative 'repository'
2
- require_relative 'site'
1
+ require_relative 'orchestrator'
3
2
  require_relative 's3_uploader'
4
- require_relative 'http_handler'
5
3
 
6
4
  module Contraption
7
- class Runner
5
+ class Runner < Orchestrator
8
6
  def initialize options
9
7
  @options = options
10
8
  end
11
9
 
12
- def run!
13
- source.finalize_completed_drafts handlers
14
- site.build_all_individuals
15
- site.build_month_pages
16
- site.build_year_pages
17
- site.build_tag_pages
18
- site.build_tag_cloud
19
- site.build_recent
20
- site.build_landing
21
- site.build_rss
22
- copy_static_files
23
- end
24
-
25
10
  private
26
- def formatter
27
- @formatter ||= source.formatter
28
- end
29
-
30
- def posts
31
- @posts ||= source.posts
32
- end
33
-
34
- def site
35
- @site ||= Site.new @options.destination, posts, formatter
36
- end
37
-
38
11
  def source
39
12
  @source ||= Repository.new @options.source
40
13
  end
41
14
 
42
- def copy_static_files
43
- `cp -r #{source.static_files}/* #{site.root}` unless (Dir[source.static_files.to_s + "/*"]).length == 0
44
- end
45
-
46
15
  def handlers
16
+ h = super
47
17
  if @options.s3_access_key_id
48
- [
49
- HttpHandler.new,
18
+ h <<
50
19
  S3Uploader.new({access_key_id: @options.s3_access_key_id,
51
20
  secret_access_key: @options.s3_secret_access_key},
52
21
  @options.s3_target_bucket,
53
22
  @options.source)
54
- ]
55
- else
56
- [ HttpHandler.new]
57
23
  end
24
+ h
58
25
  end
59
26
  end
60
27
  end
@@ -16,14 +16,16 @@ module Contraption
16
16
  def handle request
17
17
  local_path = Pathname.new(@path.path) + request.last
18
18
  if local_path.exist?
19
- upload local_path, request.last
20
- ["http:", "#{@target_bucket}/#{request.last}"]
19
+ remote_path = "#{Time.now.year}" + "/#{"%02d" % Time.now.mon}/" + request.last.split('/').last
20
+ upload local_path, remote_path
21
+ ["http:", "#{@target_bucket}/#{remote_path}"]
21
22
  else
22
- raise "#{request.last} does not exist"
23
+ raise "#{local_path.expand_path} does not exist"
23
24
  end
24
25
  end
25
26
 
26
27
  def upload local_path, remote_path
28
+ puts "Uploading #{local_path} to #{remote_path}"
27
29
  AWS::S3::S3Object.store(remote_path, open(local_path), @target_bucket, :access => :public_read, 'Cache-Control' => 'max-age=3136000')
28
30
  end
29
31
  end
@@ -55,10 +55,23 @@ module Contraption
55
55
  @location.write 'rss', feed
56
56
  end
57
57
 
58
+ def build_archive_navigation
59
+ content = @posts.by_month
60
+ .keys
61
+ .sort_by{|m| m.year*12 + m.month}
62
+ .map{|m| %Q{<a class="indivisible" href="#{m.year}/#{"%02d" % m.month}">#{Date::ABBR_MONTHNAMES[m.month]} #{m.year}</a>}}
63
+ .join("\n")
64
+ write_as_page content, @location, 'archive.html'
65
+ end
66
+
58
67
  def root
59
68
  @location.path
60
69
  end
61
70
 
71
+ def location
72
+ @location
73
+ end
74
+
62
75
  private
63
76
  def build collection
64
77
  Array(collection).map {|p| @formats.format p}.join
@@ -15,7 +15,7 @@ module Contraption
15
15
  private
16
16
  def tag_link tag
17
17
  tag = Tag.new tag
18
- %Q{<a href="tags/#{tag.to_url}/index.html">#{tag.to_s}</a>}
18
+ %Q{<a class="indivisible" href="tags/#{tag.to_url}/index.html">#{tag.to_s}</a>}
19
19
  end
20
20
 
21
21
  def scale element
@@ -1,3 +1,3 @@
1
1
  module Contraption
2
- VERSION = "0.2.0"
2
+ VERSION = '0.3.0'
3
3
  end
@@ -69,35 +69,6 @@ module Contraption
69
69
  end
70
70
  end
71
71
 
72
- context "tag grouping" do
73
- let(:item_1) { stub(tags: %w[a b c]) }
74
- let(:item_2) { stub(tags: [])}
75
- let(:item_3) { stub(tags: %w[a d]) }
76
- let(:item_4) { stub(tags: %w[b c d]) }
77
- let(:item_5) { stub(tags: %w[c e]) }
78
- let(:catalog) { Catalog.new [item_1, item_2, item_3, item_4, item_5] }
79
-
80
- it "groups all items tagged with 'a'" do
81
- catalog.by_tag[:a].should eq [item_1, item_3]
82
- end
83
-
84
- it "groups all items tagged with 'b'" do
85
- catalog.by_tag[:b].should eq [item_1, item_4]
86
- end
87
-
88
- it "groups all items tagged with 'c'" do
89
- catalog.by_tag[:c].should eq [item_1, item_4, item_5]
90
- end
91
-
92
- it "groups all items tagged with 'd'" do
93
- catalog.by_tag[:d].should eq [item_3, item_4]
94
- end
95
-
96
- it "groups all items tagged with 'e'" do
97
- catalog.by_tag[:e].should eq [item_5]
98
- end
99
- end
100
-
101
72
  it "includes enumerable" do
102
73
  Catalog.included_modules.should include Enumerable
103
74
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contraption
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Casey Robinson
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-11-03 00:00:00.000000000 Z
11
+ date: 2014-02-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -122,22 +122,35 @@ files:
122
122
  - LICENSE.txt
123
123
  - README.md
124
124
  - Rakefile
125
+ - benchmark.rb
125
126
  - bin/contraption
126
127
  - contraption.gemspec
128
+ - data/formats/landing_page.erb
129
+ - data/formats/page.erb
130
+ - data/formats/tag_cloud.erb
131
+ - doc/classes.dot
132
+ - doc/classes.png
133
+ - features/author_builds_site.feature
127
134
  - features/author_finalizes_draft.feature
128
- - features/author_generates_site.feature
135
+ - features/repository_is_generated.feature
129
136
  - features/step_definitions/author_steps.rb
137
+ - features/step_definitions/repository_generation_steps.rb
130
138
  - features/support/env.rb
131
139
  - features/support/example_inputs.rb
132
140
  - features/support/hooks.rb
133
141
  - lib/contraption.rb
134
142
  - lib/contraption/catalog.rb
143
+ - lib/contraption/command_selector.rb
144
+ - lib/contraption/diagnoser.rb
135
145
  - lib/contraption/formatter.rb
146
+ - lib/contraption/generator.rb
136
147
  - lib/contraption/header.rb
137
148
  - lib/contraption/http_handler.rb
138
149
  - lib/contraption/location.rb
139
150
  - lib/contraption/options.rb
151
+ - lib/contraption/orchestrator.rb
140
152
  - lib/contraption/post.rb
153
+ - lib/contraption/previewer.rb
141
154
  - lib/contraption/repository.rb
142
155
  - lib/contraption/rss_builder.rb
143
156
  - lib/contraption/runner.rb
@@ -182,9 +195,11 @@ signing_key:
182
195
  specification_version: 4
183
196
  summary: Static site generator
184
197
  test_files:
198
+ - features/author_builds_site.feature
185
199
  - features/author_finalizes_draft.feature
186
- - features/author_generates_site.feature
200
+ - features/repository_is_generated.feature
187
201
  - features/step_definitions/author_steps.rb
202
+ - features/step_definitions/repository_generation_steps.rb
188
203
  - features/support/env.rb
189
204
  - features/support/example_inputs.rb
190
205
  - features/support/hooks.rb