blog-generator 0.0.1 → 0.0.2

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: 48df4a50c077eb56d0cb24ea7eef27a430b3ad16
4
- data.tar.gz: 1ac90bf92d9bb55f32e51c01b95f431a058935ce
3
+ metadata.gz: 6f1d0ff16391680e7a1feb4dd62f7249ab4030e0
4
+ data.tar.gz: 38bae8eb5d26ea606aeea6e0e82766da615b3159
5
5
  SHA512:
6
- metadata.gz: 34f40f9ab1eeed3994261cd5ed287883ab214825619de8d545ba657721a504588a901f098cb3ff62d64104bb5354e7dce02bc4b249094d3de819fddc469d33b2
7
- data.tar.gz: 37113b5c7f50b2ce0fbb4b7e4375343604788c618725dcf4f886f6cc181f2bc4044c38f00a3a0bf050cb05b9afab2298d160ffbc651f193ba5bc2656cdc673f3
6
+ metadata.gz: 754eafed7232e1c80bb8924e1e33dc3c7c9567231268adca87f501efc05d61b9008ee7fc8eca29d45a583c251c63f1dfbf0cf4fe2dec54ec52380b8586897c49
7
+ data.tar.gz: 82700d910962e9d63d877d70531788f7d0af2eda225dd043de1dd081e64a0040bae7c778d8ee41a0ead96983769c8ef0ab99c9be9fb61a715e2a3905ecc31128
data/README.md ADDED
@@ -0,0 +1,56 @@
1
+ # About
2
+
3
+ Few years back we used to use all the fancy static site generators for building a blog, so the blog could have layouts, tags, pagination and other features. With the arrival of frontend frameworks such as AngularJS, this is no longer necessary.
4
+
5
+ We can generate a static JSON API and let AngularJS handle the rest.
6
+
7
+ # Usage
8
+
9
+ gem install blog-generator
10
+ gem install redcarpet # If you are going to use markdown.
11
+ mkdir botanicus.me
12
+ cd botanicus.me
13
+ mkdir posts
14
+ blog-generator.rb posts api
15
+
16
+ # Post structure
17
+
18
+ ```html
19
+ title: 'Hello world!'
20
+ tags: ['Hello world', 'Test']
21
+ ---
22
+
23
+ <div id="excerpt">
24
+ Excerpt
25
+ </div>
26
+
27
+
28
+ <h1>Hello world!</h1>
29
+ <p>
30
+ Lorem ipsum dolor sit amet, consectetur adipisicing elit. Soluta quibusdam necessitatibus tempore ullam incidunt amet omnis, veritatis dicta quisquam accusamus at provident vel facere corporis sed fugiat cumque. Consequuntur, necessitatibus!
31
+ </p>
32
+ ```
33
+
34
+ ## Assumptions
35
+
36
+ - Metadata `title` and optionally `tags`.
37
+ - Metadata `draft: true` will exclude the post.
38
+ - Any other metadata can be added and will be accessible in the resulting JSON.
39
+ - Body has `#excerpt`.
40
+ - Format `posts/:published_on-:slug.:format`.
41
+
42
+ # Routes generated
43
+
44
+ - `slug` and `published_on` added to metadata.
45
+ - `/posts.json`
46
+ - `/posts/:slug.json`
47
+ - `/tags.json`
48
+ - `/tags/:slug.json`
49
+
50
+ # Status
51
+
52
+ It works, but it needs polishing. Integration specs are OK, unit tests are broken.
53
+
54
+ # TODO
55
+
56
+ - GH markdown including source code support.
@@ -0,0 +1,60 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # [Usage]
4
+ #
5
+ # blog-generator.rb [posts dir] [output base path]
6
+
7
+ require 'blog-generator'
8
+
9
+ POSTS_DIR, OUTPUT_BASE_PATH = ARGV
10
+
11
+ unless ARGV.length == 2
12
+ abort "Usage: #{$0} [posts dir] [output base path]"
13
+ end
14
+
15
+ unless File.directory?(POSTS_DIR)
16
+ abort "Posts directory #{POSTS_DIR} doesn't exist."
17
+ end
18
+
19
+ # Parse the posts.
20
+ generator = BlogGenerator::Generator.parse(POSTS_DIR)
21
+
22
+ unless File.directory?(OUTPUT_BASE_PATH)
23
+ puts "~ #{OUTPUT_BASE_PATH} doesn't exist, creating."
24
+ Dir.mkdir(OUTPUT_BASE_PATH)
25
+ end
26
+
27
+ # Generate.
28
+
29
+ Dir.chdir(OUTPUT_BASE_PATH) do
30
+ # GET /api/posts.json
31
+ File.open('posts.json', 'w') do |file|
32
+ # This calls PostList#to_json
33
+ file.puts(JSON.pretty_generate(generator.posts))
34
+ end
35
+
36
+ # GET /api/posts/hello-world.json
37
+ Dir.mkdir('posts') unless Dir.exist?('posts')
38
+ generator.posts.each do |post|
39
+ File.open("posts/#{post.slug}.json", 'w') do |file|
40
+ file.puts(JSON.pretty_generate(post))
41
+ end
42
+ end
43
+
44
+ # GET /api/tags.json
45
+ File.open('tags.json', 'w') do |file|
46
+ # [{title: x, slug: y}]
47
+ tags = generator.tags.map do |tag, _|
48
+ tag
49
+ end
50
+ file.puts(JSON.pretty_generate(tags))
51
+ end
52
+
53
+ # GET /api/tags/doxxu.json
54
+ Dir.mkdir('tags') unless Dir.exist?('tags')
55
+ generator.tags.each do |tag, posts|
56
+ File.open("tags/#{tag[:slug]}.json", 'w') do |file|
57
+ file.puts(JSON.pretty_generate(posts))
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ require 'json'
2
+
3
+ require 'blog-generator/post'
4
+ require 'blog-generator/post_list'
5
+
6
+ module BlogGenerator
7
+ class Generator
8
+ def self.parse(posts_dir)
9
+ posts = Dir.glob("#{posts_dir}/*.{html,md}").reduce(PostList.new) do |posts, path|
10
+ post = Post.new(path)
11
+ puts "~ Parsing #{post.inspect}"
12
+ posts.push(post)
13
+ end
14
+
15
+ self.new(posts)
16
+ end
17
+
18
+ attr_reader :posts
19
+ def initialize(posts)
20
+ @posts = posts
21
+ end
22
+
23
+ def tags
24
+ @posts.reduce(Hash.new) do |buffer, post|
25
+ puts; puts
26
+ p post
27
+ puts; puts
28
+ post.tags.each do |tag|
29
+ buffer[tag] ||= PostList.new
30
+ buffer[tag] << post
31
+ end
32
+
33
+ buffer
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,76 @@
1
+ require 'json'
2
+ require 'date'
3
+ require 'yaml'
4
+ require 'nokogiri'
5
+
6
+ module BlogGenerator
7
+ class Post
8
+ REGEXP = /^(\d{4}-\d{2}-\d{2})-(.+)\.(html|md)$/
9
+
10
+ attr_reader :metadata
11
+ def initialize(path)
12
+ @path = path
13
+
14
+ @metadata = YAML.load_file(path).reduce(Hash.new) do |buffer, (key, value)|
15
+ buffer.merge(key.to_sym => value)
16
+ end
17
+
18
+ published_on, slug, format = parse_path(path)
19
+
20
+ @body = convert_markdown(self.body) if format == :md
21
+ self.body # cache if it wasn't called yet
22
+
23
+ @metadata.merge!(slug: slug, published_on: published_on)
24
+ @metadata.merge!(excerpt: excerpt)
25
+
26
+ @metadata[:tags].map! do |tag|
27
+ {title: tag, slug: generate_slug(tag)}
28
+ end
29
+
30
+ document = Nokogiri::HTML(self.body)
31
+ document.css('#excerpt').remove
32
+ @body = document.css('body').inner_html.strip
33
+ end
34
+
35
+ def generate_slug(name)
36
+ name.downcase.tr(' /', '-').delete('!?')
37
+ end
38
+
39
+ # Maybe rename body -> raw_body and to_html -> body.
40
+ def body
41
+ @body ||= File.read(@path).match(/\n---\n(.+)$/m)[1].strip
42
+ end
43
+
44
+ # We're converting it to MD, apparently it's necessary even though we converted the whole text initially, but it seems like MD ignores whatever is in <div id="excerpt">...</div>.
45
+ def excerpt
46
+ @excerpt ||= Nokogiri::HTML(convert_markdown(Nokogiri::HTML(self.body).css('#excerpt').inner_html.strip)).css('p').inner_html
47
+ end
48
+
49
+ def as_json
50
+ @metadata.merge(body: body)
51
+ end
52
+
53
+ def to_json(*args)
54
+ self.as_json.to_json(*args)
55
+ end
56
+
57
+ private
58
+ def method_missing(method, *args, &block)
59
+ return super if (! args.empty?) || block
60
+ @metadata[method]
61
+ end
62
+
63
+ def parse_path(path)
64
+ match = File.basename(path).match(REGEXP)
65
+ [Date.parse(match[1]), match[2], match[3].to_sym]
66
+ end
67
+
68
+ def convert_markdown(markup)
69
+ require 'redcarpet'
70
+
71
+ renderer = Redcarpet::Render::HTML.new(no_links: true, hard_wrap: true)
72
+ markdown = Redcarpet::Markdown.new(renderer, extensions = {})
73
+ markdown.render(markup)
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,34 @@
1
+ require 'json'
2
+ require 'forwardable'
3
+
4
+ module BlogGenerator
5
+ class PostList
6
+ extend Forwardable
7
+
8
+ attr_reader :posts
9
+ def initialize
10
+ @posts = Array.new
11
+ end
12
+
13
+ def_delegators :@posts, :reduce, :each
14
+
15
+ def as_json
16
+ self.posts.map do |post|
17
+ post.as_json.tap do |metadata|
18
+ metadata.delete(:body)
19
+ end
20
+ end
21
+ end
22
+
23
+ def to_json(*args)
24
+ self.as_json.to_json(*args)
25
+ end
26
+
27
+ [:push, :<<].each do |method|
28
+ define_method(method) do |*args, &block|
29
+ @posts.send(method, *args, &block)
30
+ self # Important!
31
+ end
32
+ end
33
+ end
34
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: blog-generator
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - James C Russell
@@ -24,14 +24,21 @@ dependencies:
24
24
  - - ~>
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.6'
27
- description:
28
- email:
29
- executables: []
27
+ description: '...'
28
+ email: james@101ideas.cz
29
+ executables:
30
+ - blog-generator.rb
30
31
  extensions: []
31
32
  extra_rdoc_files: []
32
- files: []
33
- homepage:
34
- licenses: []
33
+ files:
34
+ - README.md
35
+ - bin/blog-generator.rb
36
+ - lib/blog-generator.rb
37
+ - lib/blog-generator/post.rb
38
+ - lib/blog-generator/post_list.rb
39
+ homepage: http://github.com/botanicus/blog-generator
40
+ licenses:
41
+ - MIT
35
42
  metadata: {}
36
43
  post_install_message:
37
44
  rdoc_options: []