diary 0.1.5 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,37 @@
1
+ require 'optparse'
2
+
3
+ module Diary
4
+ module CLI
5
+ module Parser
6
+ def parse(args)
7
+ options = OpenStruct.new
8
+ options.force = false
9
+
10
+ opts = OptionParser.new do |opts|
11
+ opts.banner = "Usage: diary ACTION [-vf]"
12
+
13
+ opts.separator "\nSpecific options:"
14
+
15
+ opts.on("-f", "--[no-]force", "Force all items to compile") do |f|
16
+ options.force = f
17
+ end
18
+
19
+ opts.separator "\nCommon options:"
20
+
21
+ opts.on_tail("-h", "--help", "Show this message") do
22
+ $stdout.puts opts
23
+ exit
24
+ end
25
+
26
+ opts.on_tail("-v", "--version", "Show version") do
27
+ $stdout.puts "diary #{VERSION}"
28
+ exit
29
+ end
30
+ end
31
+
32
+ opts.parse!(args)
33
+ options
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,26 @@
1
+ module Diary
2
+ module Item
3
+ class Base
4
+ attr_reader :path
5
+
6
+ def initialize(path)
7
+ @path = path
8
+ end
9
+
10
+ def basename
11
+ File.basename(path, '.md')
12
+ end
13
+
14
+ def classname
15
+ self.class.name.downcase
16
+ end
17
+
18
+ def self.base_directory
19
+ self.name.downcase.pluralize
20
+ end
21
+
22
+ extend Creator
23
+ extend Finder
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,80 @@
1
+ # coding: utf-8
2
+ module Diary
3
+ module Item
4
+ module Creator
5
+ def self.extended(base)
6
+ base.send :include, Peristance
7
+ end
8
+
9
+ module Peristance
10
+ def save!(title = nil)
11
+ unless persisted?
12
+ self.class.create(title || basename)
13
+ else
14
+ self
15
+ end
16
+ end
17
+
18
+ def persisted?
19
+ File.exists?(path)
20
+ end
21
+ end
22
+
23
+ ACCENTS = {
24
+ ['á','à','â','ä','ã'] => 'a',
25
+ ['Ã','Ä','Â','À','�?'] => 'A',
26
+ ['é','è','ê','ë'] => 'e',
27
+ ['Ë','É','È','Ê'] => 'E',
28
+ ['í','ì','î','ï'] => 'i',
29
+ ['�?','Î','Ì','�?'] => 'I',
30
+ ['ó','ò','ô','ö','õ'] => 'o',
31
+ ['Õ','Ö','Ô','Ò','Ó'] => 'O',
32
+ ['ú','ù','û','ü'] => 'u',
33
+ ['Ú','Û','Ù','Ü'] => 'U',
34
+ ['ç'] => 'c', ['Ç'] => 'C',
35
+ ['ñ'] => 'n', ['Ñ'] => 'N'
36
+ }
37
+
38
+ def create(title)
39
+ slug = slugify(title)
40
+ path = resolve_path(slug)
41
+
42
+ ensure_directories_exists!(path)
43
+
44
+ unless File.exists?(path)
45
+ File.open(path, "w+") do |f|
46
+ f.puts "---\n"
47
+ f.puts "title: #{title}"
48
+ f.puts "\n---\n"
49
+ end
50
+
51
+ Diary.message :create, path, :green
52
+ else
53
+ Diary.message :exist, path, :yellow
54
+ end
55
+
56
+ self.new(path)
57
+ end
58
+
59
+ private
60
+
61
+ def slugify(str)
62
+ ACCENTS.each do |ac,rep|
63
+ ac.each do |s|
64
+ str = str.gsub(s, rep)
65
+ end
66
+ end
67
+
68
+ str.gsub(/[ ]+/, " ").gsub(/ /, "-").downcase
69
+ end
70
+
71
+ def resolve_path(slug)
72
+ File.join(base_directory, "#{slug}.md")
73
+ end
74
+
75
+ def ensure_directories_exists!(path)
76
+ FileUtils.mkpath File.dirname(path)
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,37 @@
1
+ module Diary
2
+ module Item
3
+ module Data
4
+ attr_reader :data
5
+
6
+ def initialize(*args)
7
+ super
8
+ load_data
9
+ end
10
+
11
+ def reload_data
12
+ @data = load_data
13
+ end
14
+
15
+ private
16
+
17
+ def load_data
18
+ if File.exists?(path)
19
+ @data ||= OpenStruct.new(YAML.load_file(path)).freeze
20
+ create_attr_readers_from_data_table!
21
+
22
+ @data
23
+ else
24
+ nil
25
+ end
26
+ end
27
+
28
+ def create_attr_readers_from_data_table!
29
+ data.instance_variable_get(:@table).tap do |h|
30
+ h.each_key do |k|
31
+ self.class.send(:define_method, k) { data.send k } unless self.class.method_defined?(k)
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,37 @@
1
+ module Diary
2
+ module Item
3
+ module Finder
4
+ def find(r)
5
+ file_list.each do |p|
6
+ return initialize_item(p) if p.match(r)
7
+ end
8
+
9
+ nil
10
+ end
11
+
12
+ def all
13
+ file_list.map do |p|
14
+ initialize_item(p)
15
+ end
16
+ end
17
+
18
+ def first
19
+ all.first
20
+ end
21
+
22
+ def last
23
+ all.last
24
+ end
25
+
26
+ private
27
+
28
+ def file_list
29
+ Dir[File.join(base_directory, "**", "*.md")]
30
+ end
31
+
32
+ def initialize_item(p)
33
+ self.new(p)
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,58 @@
1
+ module Diary
2
+ module Item
3
+ module Output
4
+ def output(force = false)
5
+ if changed? or force
6
+ FileUtils.mkpath output_directory
7
+ File.open(output_path, 'w+') { |f| f.puts render }
8
+ Diary.message (force ? :force : :update), output_path, (force ? :yellow : :green)
9
+
10
+ return true
11
+ else
12
+ Diary.message :identical, output_path, :yellow
13
+
14
+ return false
15
+ end
16
+ end
17
+
18
+ def content
19
+ return @content if @content
20
+
21
+ File.open(path, 'r') do |f|
22
+ @content ||= BlueCloth.new(f.read.split(/---\n/).slice(-1).strip).to_html
23
+ end
24
+ end
25
+
26
+ private
27
+
28
+ def render
29
+ File.open(template, 'r') do |t|
30
+ ERB.new(t.read).result(binding)
31
+ end
32
+ end
33
+
34
+ def changed?
35
+ file = File.open(path, 'r') if File.exists?(path)
36
+ output_file = File.open(output_path, 'r') if File.exists?(output_path)
37
+
38
+ if file and output_file
39
+ result = file.mtime > output_file.ctime
40
+ else
41
+ result = true
42
+ end
43
+
44
+ [file, output_file].each { |f| f.close if f and f.is_a?(File) }
45
+
46
+ result
47
+ end
48
+
49
+ def output_path(directory = "public")
50
+ path.gsub(self.class.base_directory, directory).gsub('.md', (path.match("index.md") ? '.html' : '/index.html'))
51
+ end
52
+
53
+ def output_directory
54
+ File.dirname(output_path)
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,9 @@
1
+ module Diary
2
+ module Item
3
+ module Snippet
4
+ def snippet(name)
5
+ Diary::Snippet.new(name.intern).content
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,38 @@
1
+ module Diary
2
+ module Item
3
+ module Template
4
+ # This method will go down the template lookup procedure and will
5
+ # return the first file path to match an existing file.
6
+ #
7
+ # The lookup order is as follow:
8
+ #
9
+ # 1 The template name supplied in the data section
10
+ # 2 A template with the same basename
11
+ # 3 A template with the name of the item class (Page, Post, etc…)
12
+ # 4 The index template file
13
+ def template
14
+ Diary.message :invoke, template_lookup, :cyan
15
+
16
+ template_lookup
17
+ end
18
+
19
+ private
20
+
21
+ def template_lookup
22
+ template_names.each do |name|
23
+ return template_path(name) if File.exists?(template_path(name))
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ def template_names
30
+ [(defined?(Data) ? data.template : nil), basename, classname, 'index'].compact
31
+ end
32
+
33
+ def template_path(name)
34
+ File.join("templates", "#{name}.html.erb")
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,9 @@
1
+ module Diary
2
+ module Item
3
+ module Url
4
+ def link(name)
5
+ "<a href=\"#{output_path.gsub("public/", "")}\">#{name}</a>"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,7 @@
1
+ class Page < Diary::Item::Base
2
+ include Diary::Item::Data
3
+ include Diary::Item::Url
4
+ include Diary::Item::Template
5
+ include Diary::Item::Snippet
6
+ include Diary::Item::Output
7
+ end
@@ -0,0 +1,10 @@
1
+ class Post < Diary::Item::Base
2
+ include Diary::Item::Data
3
+ include Diary::Item::Template
4
+ include Diary::Item::Output
5
+ include Diary::Item::Url
6
+
7
+ def output_path(directory = "public/blog")
8
+ super
9
+ end
10
+ end
@@ -1,23 +1,44 @@
1
1
  module Diary
2
- module Message
3
- # Red
4
- Error = " \e[1;31merror\e[0m"
5
- Skip = " \e[1;31mskip\e[0m"
6
-
7
- # Green
8
- Publish = " \e[1;32mpublish\e[0m"
9
- Create = " \e[1;32mcreate\e[0m"
10
- Update = " \e[1;32mupdate\e[0m"
11
-
12
- # Yellow
13
- Identical = " \e[1;33midentical\e[0m"
14
- Exist = " \e[1;33mexist\e[0m"
15
-
16
- # Cyan
17
- Invoke = " \e[1;36minvoke\e[0m"
18
-
19
- def say(const, message)
20
- puts "#{const} #{message}"
2
+ class Message
3
+ DEFAULT_PADDING = 12
4
+
5
+ # Embed in a String to clear all previous ANSI sequences.
6
+ CLEAR = "\e[0m"
7
+ # Embed in a String to add bold
8
+ BOLD = "\e[1m"
9
+ # Embed in a String to add color
10
+ BLACK = "\e[30m"
11
+ RED = "\e[31m"
12
+ GREEN = "\e[32m"
13
+ YELLOW = "\e[33m"
14
+ BLUE = "\e[34m"
15
+ MAGENTA = "\e[35m"
16
+ CYAN = "\e[36m"
17
+ WHITE = "\e[37m"
18
+
19
+ def initialize(keyword, message, color = false)
20
+ keyword = add_padding(keyword.to_s)
21
+ keyword = set_color(keyword, color) if color
22
+
23
+ $stdout.puts "#{keyword} #{message}"
24
+ end
25
+
26
+ private
27
+
28
+ def add_padding(str, padding = DEFAULT_PADDING)
29
+ spaces = " " * [0, (padding - str.size)].max
30
+ spaces + str
31
+ end
32
+
33
+ def set_color(str, color)
34
+ color = self.class.const_get(color.to_s.upcase)
35
+ color + str + CLEAR
36
+ end
37
+
38
+ module Shorthand
39
+ def message(*args)
40
+ ::Diary::Message.new(*args)
41
+ end
21
42
  end
22
43
  end
23
44
  end
@@ -0,0 +1,5 @@
1
+ require 'sinatra'
2
+
3
+ get '*/' do
4
+ File.read(File.join("public#{request.path_info}", 'index.html'))
5
+ end
@@ -0,0 +1,19 @@
1
+ module Diary
2
+ class Snippet
3
+ attr_reader :path
4
+
5
+ def initialize(name)
6
+ @path = File.join("snippets", "#{name}.html")
7
+ end
8
+
9
+ def content
10
+ unless @content
11
+ File.open(path, 'r') do |file|
12
+ @content ||= file.read
13
+ end
14
+ end
15
+
16
+ @content
17
+ end
18
+ end
19
+ end