gidget 0.2.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,29 +7,17 @@ Gidget is a minimalist blog engine designed to run on Heroku with a Git-based wo
7
7
  == Basic Structure
8
8
 
9
9
  * Sinatra based server
10
- * A singleton array containing post information (the body of a post is lazily-loaded)
10
+ * A File Mapper to help map requests to pages, posts, and tags
11
+ * A class to handle page information
11
12
  * A class to handle post information
12
13
 
13
- == Routing
14
+ == Templates
14
15
 
15
- * / - an index template with access to the full array of posts
16
- * /2010/11/19 - an archive template with access to all posts from the specified year/month/day as well as what year, month, and day it is for (month and day are optional)
17
- * /2010/11/19/first-post - a post template with access to the full array of posts and the current post index
18
- * /page/1 - a paging template with access to posts for the specified day as well as the current page number and total number of pages
19
- * /some-special-page - a custom template with access to the full array of posts
16
+ <This section is being rewritten>
20
17
 
21
- == Post Creation
18
+ == Page/Post Creation
22
19
 
23
- Posts are simply .txt files located under a folder named posts located off the root of the web app. Text files contain two sections of data. The first is metadata including at least a title and date as such:
24
-
25
- title: My great post title
26
- date: 2010-11-19
27
-
28
- _Hello World!_ This is my first blog post!
29
-
30
- The second section is the body of the post and uses markdown as it's markup language. The two sections should be separated by an empty line.
31
-
32
- File name and structure (other than the .txt extension) don't matter. Gidget will determine request paths based on the date and title metadata and sort them accordingly.
20
+ <This section is being rewritten>
33
21
 
34
22
  == Settings
35
23
 
@@ -38,7 +26,6 @@ Gidget uses Sinatra's built-in setting support. Settings can be set in your con
38
26
  gidget = Gidget::Server.new do
39
27
  set :title, "My Awesome Blog"
40
28
  set :author, "Your Name Goes Here"
41
- set :summary_size, 100
42
29
  set :page_size, 10
43
30
  end
44
31
 
@@ -79,10 +66,11 @@ In order to deploy an app to Heroku you must have an account with them. Once yo
79
66
  git push heroku master
80
67
  heroku open
81
68
 
82
- Congrats! You now have your own blog running on the internets!
69
+ Congrats! You now have your own blog running on the world wide web!
83
70
 
84
71
  == TO BE DONE
85
72
 
73
+ * Cleanup stub contents
86
74
  * Come up with a decent base theme
87
75
 
88
76
  == Contributing to gidget
@@ -0,0 +1,3 @@
1
+ title: About
2
+
3
+ This is an about page with whatever information you want to be here.
@@ -7,4 +7,5 @@
7
7
  %h1 My Blog
8
8
  %article
9
9
  %h1= posts[0].title
10
- = posts[0].body
10
+ = posts[0].body
11
+ = DateTime.now.to_s
@@ -6,4 +6,5 @@
6
6
  %header
7
7
  %h1 My Blog
8
8
  %article
9
- %h1= "I found #{posts.size} posts!"
9
+ %h1= page.title
10
+ = page.body
@@ -0,0 +1,9 @@
1
+ !!! 5
2
+ %html
3
+ %head
4
+ %title= settings.title
5
+ %body
6
+ %header
7
+ %h1 My Blog
8
+ %article
9
+ %h1= "I found #{posts.size} posts!"
data/bin/gidget CHANGED
@@ -24,6 +24,7 @@ class App
24
24
 
25
25
  print " Copying stub ... "
26
26
  FileUtils.cp_r(source + "/.", destination)
27
+ FileUtils.remove_file(File.join(destination, "config.dev.ru"))
27
28
  puts "done."
28
29
 
29
30
  puts "Done."
@@ -4,11 +4,6 @@ class String
4
4
  end
5
5
 
6
6
 
7
- def humanize
8
- self.capitalize.gsub(/[-_]+/, ' ')
9
- end
10
-
11
-
12
7
  def starts_with?(prefix)
13
8
  prefix = prefix.to_s
14
9
  self[0, prefix.length] == prefix
@@ -0,0 +1,68 @@
1
+ require 'singleton'
2
+ require 'gidget/page'
3
+ require 'gidget/post'
4
+
5
+
6
+ module Gidget
7
+ class FileMapper
8
+ include Singleton
9
+
10
+ attr_reader :pages
11
+ attr_reader :posts
12
+ attr_reader :tags
13
+
14
+
15
+ def initialize
16
+ @pages = Hash.new
17
+ @posts = Array.new
18
+ @tags = Hash.new
19
+ end
20
+
21
+
22
+ def load
23
+ page_paths = Dir.glob("pages/**/*.txt")
24
+
25
+ # load the page index
26
+ page_paths.each { |file_path|
27
+ page = Page.new(file_path)
28
+ @pages[page.request_path] = page
29
+ }
30
+
31
+ puts "Page Index created, size = " + @pages.keys.size.to_s
32
+
33
+
34
+ post_paths = Dir.glob("posts/**/*.txt")
35
+
36
+ # load the post index
37
+ post_paths.each { |file_path|
38
+ post = Post.new(file_path)
39
+ @posts << post
40
+ }
41
+
42
+ # sort the post index by date descending (newest will be first)
43
+ @posts.replace @posts.sort_by { |p| Time.parse(p.date.to_s).to_i }.reverse!
44
+
45
+ puts "Post Index created, size = " + @posts.size.to_s
46
+
47
+
48
+ # load the tag index
49
+ @posts.each { |post|
50
+ if (post.meta_data.has_key? :tags)
51
+ tags = post.meta_data[:tags].split(',')
52
+
53
+ tags.each { |tag|
54
+ tag.strip!
55
+
56
+ if (!@tags.has_key? tag)
57
+ @tags[tag] = []
58
+ end
59
+
60
+ @tags[tag] << post
61
+ }
62
+ end
63
+ }
64
+
65
+ puts "Tag Index created, size = " + @tags.keys.size.to_s
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,55 @@
1
+ require 'rdiscount'
2
+ require 'yaml'
3
+ require 'gidget/ext'
4
+
5
+
6
+ module Gidget
7
+ class Page
8
+ attr_reader :file_path
9
+ attr_reader :meta_data
10
+
11
+
12
+ def initialize(file_path)
13
+ @file_path = file_path
14
+
15
+ begin
16
+ file = File.open(@file_path, "r")
17
+
18
+ # read the first paragraph and load it into a Hash with symbols as keys
19
+ @meta_data = YAML.load(file.gets("")).inject({}) { |h, (k,v)| h.merge(k.to_sym => v) }
20
+ ensure
21
+ file.close()
22
+ end
23
+ end
24
+
25
+
26
+ def request_path
27
+ @meta_data[:permalink] || "/" + self.title.slugize
28
+ end
29
+
30
+
31
+ def title
32
+ @meta_data[:title]
33
+ end
34
+
35
+
36
+ def body
37
+ begin
38
+ file = File.open(@file_path, "r")
39
+
40
+ # ignore the first paragraph
41
+ file.gets("")
42
+
43
+ # read the rest of the file and process it's markdown
44
+ RDiscount.new(file.gets(nil)).to_html
45
+ ensure
46
+ file.close()
47
+ end
48
+ end
49
+
50
+
51
+ def method_missing(m, *args, &block)
52
+ @meta_data[m] || super
53
+ end
54
+ end
55
+ end
@@ -1,52 +1,20 @@
1
- require 'yaml'
2
- require 'rdiscount'
3
- require 'gidget/ext'
1
+ require 'gidget/page'
4
2
 
5
3
 
6
4
  module Gidget
7
- class Post
8
- attr_reader :file_path
9
- attr_reader :request_path
10
- attr_reader :meta_data
11
-
12
-
13
- def initialize(file_path)
14
- @file_path = file_path
15
-
16
- begin
17
- file = File.open(@file_path, "r")
18
-
19
- # read the first paragraph and load it into a Hash with symbols as keys
20
- @meta_data = YAML.load(file.gets("")).inject({}) { |h, (k,v)| h.merge(k.to_sym => v) }
21
- ensure
22
- file.close()
23
- end
24
-
25
- @request_path = @meta_data[:date].strftime("/%Y/%m/%d/") + @meta_data[:title].slugize
5
+ class Post < Page
6
+ def request_path
7
+ @meta_data[:permalink] || self.date.strftime("/%Y/%m/") + self.title.slugize
26
8
  end
27
9
 
28
10
 
29
- def body
30
- begin
31
- file = File.open(@file_path, "r")
32
-
33
- # ignore the first paragraph
34
- file.gets("")
35
-
36
- # read the rest of the file and process it's markdown
37
- RDiscount.new(file.gets(nil)).to_html
38
- ensure
39
- file.close()
40
- end
11
+ def date
12
+ @meta_data[:date]
41
13
  end
42
14
 
43
15
 
44
- def method_missing(m, *args, &block)
45
- if (@meta_data.has_key?(m))
46
- return @meta_data[m]
47
- else
48
- super
49
- end
16
+ def tags
17
+ @meta_data[:tags] || ""
50
18
  end
51
19
  end
52
20
  end
@@ -1,6 +1,7 @@
1
1
  require 'sinatra/base'
2
+ require 'benchmark'
2
3
  require 'haml'
3
- require 'gidget/post_index'
4
+ require 'gidget/file_mapper'
4
5
 
5
6
 
6
7
  module Gidget
@@ -14,82 +15,97 @@ module Gidget
14
15
  super(app, &b=nil)
15
16
  Server.class_exec(&block) if block_given?
16
17
 
17
- PostIndex.instance.load
18
+ # load the file mapper
19
+ puts Benchmark.measure { FileMapper.instance.load }
18
20
  end
19
21
 
20
22
 
23
+ # route for root
21
24
  get '/' do
22
- render_view(:index, { :posts => PostIndex.instance })
25
+ haml :index, :locals => { :posts => FileMapper.instance.posts }
23
26
  end
24
27
 
25
28
 
26
- get %r{^\/\d{4}\/\d{2}\/\d{2}\/[\w,-]+$} do
29
+ # route for a specific page
30
+ get %r{^\/[\w,\-,\/]+$} do
31
+ page = FileMapper.instance.pages[request.path]
32
+
33
+ if (page != nil)
34
+ haml :page, :locals => { :page => page }
35
+ else
36
+ pass
37
+ end
38
+ end
39
+
40
+
41
+ # route for a specific post
42
+ get %r{^\/[\w,\-,\/]+$} do
27
43
  index = nil
28
-
29
- PostIndex.instance.each_with_index { |p, i|
44
+
45
+ FileMapper.instance.posts.each_with_index { |p, i|
30
46
  if (p.request_path == request.path)
31
47
  index = i
32
48
  break
33
49
  end
34
50
  }
35
-
51
+
36
52
  if (index != nil)
37
- render_view(:post, { :posts => PostIndex.instance, :index => index })
53
+ haml :post, :locals => { :posts => FileMapper.instance.posts, :index => index }
54
+ else
55
+ pass
38
56
  end
39
57
  end
40
-
41
-
42
- get %r{^\/(\d{4})(\/(\d{2})(\/(\d{2}))?)?$} do
43
- year = params[:captures][0]
44
- month = params[:captures][2]
45
- day = params[:captures][4]
46
-
47
- posts = PostIndex.instance.select { |p|
48
- p.request_path.starts_with? request.path
49
- }
50
-
51
- render_view(:archive, { :posts => posts, :year => year, :month => month, :day => day })
52
- end
53
58
 
54
59
 
55
- get %r{^\/page\/(\d+$)} do
60
+ # route for an archive listing in the format /archive/yyyy/mm (year and month are optional)
61
+ get %r{^\/archive(\/(\d{4})(\/(\d{2}))?)?$} do
62
+ if (params[:captures] == nil)
63
+ posts = FileMapper.instance.posts
64
+ else
65
+ year = params[:captures][1]
66
+ month = params[:captures][3]
67
+
68
+ prefix = "#{year}#{month}"
69
+
70
+ posts = FileMapper.instance.posts.select { |p|
71
+ p.date.strftime("%Y%m").starts_with? prefix
72
+ }
73
+ end
74
+
75
+ haml :archive, :locals => { :posts => posts, :year => year, :month => month }
76
+ end
77
+
78
+
79
+ # route for post paging
80
+ get %r{^\/page\/(\d+)$} do
56
81
  current_page = params[:captures][0].to_i
57
- total_pages = (PostIndex.instance.size + settings.page_size - 1) / settings.page_size
58
-
82
+ total_pages = (FileMapper.instance.posts.size + settings.page_size - 1) / settings.page_size
83
+
59
84
  if (current_page <= total_pages)
60
- posts = PostIndex.instance[(current_page - 1) * settings.page_size, settings.page_size]
61
-
62
- render_view(:page, { :posts => posts, :current_page => current_page, :total_pages => total_pages })
85
+ posts = FileMapper.instance.posts[(current_page - 1) * settings.page_size, settings.page_size]
86
+
87
+ haml :post_paging, :locals => { :posts => posts, :current_page => current_page, :total_pages => total_pages }
63
88
  else
64
89
  pass
65
90
  end
66
91
  end
67
-
68
-
69
- get %r{^\/tag\/([\w,-]+$)} do
70
- posts = TagIndex.instance[params[:captures][0]]
71
-
92
+
93
+
94
+ # route for list of posts tagged with a particular tag
95
+ get %r{^\/tag\/([\w,\-]+)$} do
96
+ posts = FileMapper.instance.tags[params[:captures][0]]
97
+
72
98
  if (posts != nil)
73
- render_view(:tag, { :posts => posts })
99
+ haml :tag, :locals => { :posts => posts }
74
100
  else
75
101
  pass
76
102
  end
77
103
  end
78
104
 
79
105
 
80
- get %r{^\/[\w,-]+$} do
81
- begin
82
- render_view(request.path.to_sym, { :posts => PostIndex.instance })
83
- rescue
84
- pass
85
- end
86
- end
87
-
88
-
89
- def render_view(view, locals)
106
+ # make all requests cached for a day
107
+ after do
90
108
  expires(86400, :public)
91
-
92
- haml view, :locals => locals
93
109
  end
94
110
  end
95
111
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gidget
3
3
  version: !ruby/object:Gem::Version
4
- hash: 21
4
+ hash: 19
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 2
9
- - 1
10
- version: 0.2.1
8
+ - 3
9
+ - 0
10
+ version: 0.3.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Forrest Robertson
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-09 00:00:00 -07:00
18
+ date: 2011-02-08 00:00:00 -07:00
19
19
  default_executable: gidget
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -139,19 +139,21 @@ files:
139
139
  - _stub_/Gemfile
140
140
  - _stub_/config.dev.ru
141
141
  - _stub_/config.ru
142
+ - _stub_/pages/about.txt
142
143
  - _stub_/posts/first-post.txt
143
144
  - _stub_/views/about.haml
144
145
  - _stub_/views/archive.haml
145
146
  - _stub_/views/index.haml
146
147
  - _stub_/views/page.haml
147
148
  - _stub_/views/post.haml
149
+ - _stub_/views/post_paging.haml
148
150
  - _stub_/views/tag.haml
149
151
  - lib/gidget.rb
150
152
  - lib/gidget/ext.rb
153
+ - lib/gidget/file_mapper.rb
154
+ - lib/gidget/page.rb
151
155
  - lib/gidget/post.rb
152
- - lib/gidget/post_index.rb
153
156
  - lib/gidget/server.rb
154
- - lib/gidget/tag_index.rb
155
157
  - LICENSE.txt
156
158
  - README.rdoc
157
159
  - bin/gidget
@@ -1,28 +0,0 @@
1
- require 'singleton'
2
- require 'gidget/post'
3
- require 'gidget/tag_index'
4
-
5
-
6
- module Gidget
7
- class PostIndex < Array
8
- include Singleton
9
-
10
-
11
- def load
12
- paths = Dir.glob("posts/**/*.txt")
13
-
14
- # add all the posts to the array
15
- paths.each { |file_path|
16
- self << Post.new(file_path)
17
- }
18
-
19
- # sort the array by the request path, descending (newest by date will be first)
20
- self.replace self.sort_by { |p| p.request_path }.reverse!
21
-
22
- puts "Post Index created, size = " + self.size.to_s
23
-
24
- # load the tag index
25
- TagIndex.instance.load self
26
- end
27
- end
28
- end
@@ -1,30 +0,0 @@
1
- require 'singleton'
2
- require 'gidget/post'
3
-
4
-
5
- module Gidget
6
- class TagIndex < Hash
7
- include Singleton
8
-
9
-
10
- def load posts
11
- posts.each { |post|
12
- tags = nil
13
-
14
- if (post.meta_data.has_key? :tags)
15
- tags = post.meta_data[:tags].split(',')
16
-
17
- tags.each { |tag|
18
- tag.strip!
19
-
20
- if (!self.has_key? tag)
21
- self[tag] = []
22
- end
23
-
24
- self[tag] << post
25
- }
26
- end
27
- }
28
- end
29
- end
30
- end