amiba 0.0.2

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,199 @@
1
+ require 'rexml/document'
2
+ include REXML
3
+
4
+ # reverse markdown for ruby
5
+ # author: JO
6
+ # e-mail: xijo@gmx.de
7
+ # date: 14.7.2009
8
+ # version: 0.1
9
+ # license: GPL
10
+
11
+ # TODO
12
+ # - ol numbering is buggy, in fact doesn't matter for markdown code
13
+ # -
14
+
15
+ module Amiba
16
+ class ReverseMarkdown
17
+
18
+ # set basic variables:
19
+ # - @li_counter: numbering list item (li) tags in an ordered list (ol)
20
+ # - @links: hold the links for adding them to the bottom of the @output
21
+ # this means 'reference style', please take a look at http://daringfireball.net/projects/markdown/syntax#link
22
+ # - @outout: fancy markdown code in here!
23
+ # - @indent: control indention level for nested lists
24
+ # - @errors: appearing errors, like unknown tags, go into this array
25
+ def initialize()
26
+ @li_counter = 0
27
+ @links = []
28
+ @output = ""
29
+ @indent = 0
30
+ @errors = []
31
+ end
32
+
33
+ # Invokes the HTML parsing by using a string. Returns the markdown code in @output.
34
+ # To garantuee well-formed xml for REXML a <root> element will be added, but has no effect.
35
+ # After parsing all elements, the 'reference style'-links will be inserted.
36
+ def parse_string(string)
37
+ doc = Document.new("<root>\n"+string+"\n</root>")
38
+ root = doc.root
39
+ root.elements.each do |element|
40
+ parse_element(element, :root)
41
+ end
42
+ insert_links()
43
+ @output
44
+ end
45
+
46
+ # Parsing an element and its children (recursive) and writing its markdown code to @output
47
+ # 1. do indent for nested list items
48
+ # 2. add the markdown opening tag for this element
49
+ # 3a. if element only contains text, handle it like a text node
50
+ # 3b. if element is a container handle its children, which may be text- or element nodes
51
+ # 4. finally add the markdown ending tag for this element
52
+ def parse_element(element, parent)
53
+ name = element.name.to_sym
54
+ # 1.
55
+ @output << indent() if name.eql?(:li)
56
+ # 2.
57
+ @output << opening(element, parent)
58
+
59
+ # 3a.
60
+ if (element.has_text? and element.children.size < 2)
61
+ @output << text_node(element, parent)
62
+ end
63
+
64
+ # 3b.
65
+ if element.has_elements?
66
+ element.children.each do |child|
67
+ # increase indent if nested list
68
+ @indent += 1 if element.name=~/(ul|ol)/ and parent.eql?(:li)
69
+
70
+ if child.node_type.eql?(:element)
71
+ parse_element(child, element.name.to_sym)
72
+ else
73
+ if parent.eql?(:blockquote)
74
+ @output << child.to_s.gsub("\n ", "\n>")
75
+ else
76
+ @output << child.to_s
77
+ end
78
+ end
79
+
80
+ # decrease indent if end of nested list
81
+ @indent -= 1 if element.name=~/(ul|ol)/ and parent.eql?(:li)
82
+ end
83
+ end
84
+
85
+ # 4.
86
+ @output << ending(element, parent)
87
+ end
88
+
89
+ # Returns opening markdown tag for the element. Its parent matters sometimes!
90
+ def opening(type, parent)
91
+ case type.name.to_sym
92
+ when :h1
93
+ "# "
94
+ when :li
95
+ parent.eql?(:ul) ? " - " : " "+(@li_counter+=1).to_s+". "
96
+ when :ol
97
+ @li_counter = 0
98
+ ""
99
+ when :ul
100
+ ""
101
+ when :h2
102
+ "## "
103
+ when :em
104
+ "*"
105
+ when :strong
106
+ "**"
107
+ when :blockquote
108
+ # remove leading newline
109
+ type.children.first.value = ""
110
+ "> "
111
+ when :code
112
+ parent.eql?(:pre) ? " " : "`"
113
+ when :a
114
+ "["
115
+ when :img
116
+ "!["
117
+ when :hr
118
+ "----------\n\n"
119
+ else
120
+ @errors << "unknown start tag: "+type.name.to_s
121
+ ""
122
+ end
123
+ end
124
+
125
+ # Returns the closing markdown tag, like opening()
126
+ def ending(type, parent)
127
+ case type.name.to_sym
128
+ when :h1
129
+ " #\n\n"
130
+ when :h2
131
+ " ##\n\n"
132
+ when :p
133
+ parent.eql?(:root) ? "\n\n" : "\n"
134
+ when :ol
135
+ parent.eql?(:li) ? "" : "\n"
136
+ when :ul
137
+ parent.eql?(:li) ? "" : "\n"
138
+ when :em
139
+ "*"
140
+ when :strong
141
+ "**"
142
+ when :li
143
+ ""
144
+ when :blockquote
145
+ ""
146
+ when :code
147
+ parent.eql?(:pre) ? "" : "`"
148
+ when :a
149
+ @links << type.attribute('href').to_s
150
+ "][" + @links.size.to_s + "] "
151
+ when :img
152
+ @links << type.attribute('src').to_s
153
+ "" + type.attribute('alt').to_s + "][" + @links.size.to_s + "] "
154
+ "#{type.attribute('alt')}][#{@links.size}] "
155
+ else
156
+ @errors << " unknown end tag: "+type.name.to_s
157
+ ""
158
+ end
159
+ end
160
+
161
+ # Perform indent: two space, @indent times - quite simple! :)
162
+ def indent
163
+ str = ""
164
+ @indent.times do
165
+ str << " "
166
+ end
167
+ str
168
+ end
169
+
170
+ # Return the content of element, which should be just text.
171
+ # If its a code block to indent of 4 spaces.
172
+ # For block quotation add a leading '>'
173
+ def text_node(element, parent)
174
+ if element.name.to_sym.eql?(:code) and parent.eql?(:pre)
175
+ element.text.gsub("\n","\n ") << "\n"
176
+ elsif parent.eql?(:blockquote)
177
+ element.text.gsub!("\n ","\n>")
178
+ else
179
+ element.text
180
+ end
181
+ end
182
+
183
+ # Insert the mentioned reference style links.
184
+ def insert_links
185
+ @output << "\n"
186
+ @links.each_index do |index|
187
+ @output << " [#{index+1}]: #{@links[index]}\n"
188
+ end
189
+ end
190
+
191
+ # Print out all errors, that occured and have been written to @errors.
192
+ def print_errors
193
+ @errors.each do |error|
194
+ puts error
195
+ end
196
+ end
197
+
198
+ end
199
+ end
@@ -0,0 +1,49 @@
1
+ module Amiba
2
+ class Scope
3
+
4
+ attr_reader :page
5
+
6
+ def initialize(page)
7
+ @page = page
8
+ end
9
+
10
+ def title
11
+ page.title
12
+ end
13
+
14
+ def description
15
+ page.description
16
+ end
17
+
18
+ def content
19
+ page_renderer.render(self)
20
+ end
21
+
22
+ def entries
23
+ Amiba::Source::Entry
24
+ end
25
+
26
+ def partial(path, locals={})
27
+ p = Amiba::Source::Partial.new path
28
+ Tilt.new(p.filename).render(Amiba::Scope.new(p), locals)
29
+ end
30
+
31
+ def site_name
32
+ Amiba::Configuration.site_name.nil? ? "" : "http://#{Amiba::Configuration.site_name}/"
33
+ end
34
+
35
+ def full_url(frag)
36
+ if site_name.empty?
37
+ frag
38
+ else
39
+ URI.join(site_name, frag).to_s
40
+ end
41
+ end
42
+
43
+ protected
44
+
45
+ def page_renderer
46
+ Tilt.new page.staged_filename
47
+ end
48
+ end
49
+ end
data/lib/amiba/site.rb ADDED
@@ -0,0 +1,166 @@
1
+ module Amiba
2
+ module Site
3
+
4
+ class S3Upload < Thor::Group
5
+ include Amiba::Generator
6
+
7
+ namespace :"site:upload:s3"
8
+
9
+ class_option :credentials, :default => :default
10
+
11
+ def init_s3
12
+ Fog.credential = options[:credentials].to_sym
13
+ @s3 ||= Fog::Storage.new(:provider=>'AWS')
14
+ end
15
+
16
+ def create
17
+ invoke Amiba::Site::Generate
18
+ end
19
+
20
+ def configure_s3
21
+ if ! @s3.directories.get bucket
22
+ @bucket = @s3.directories.create(:key=>bucket, :public=>true, :location=>location)
23
+ say_status "Created", @bucket.key, :green
24
+ @s3.put_bucket_website(bucket,"index.html")
25
+ say_status "Configured", @bucket.key, :green
26
+ else
27
+ @bucket = @s3.directories.get bucket
28
+ end
29
+ end
30
+
31
+ def upload_files
32
+ Dir[File.join(Amiba::Configuration.site_dir, "public", "**/*")].each do |ent|
33
+ next if File.directory? ent
34
+ path = File.expand_path ent
35
+ name = File.relpath(path, File.join(Amiba::Configuration.site_dir, "public"))
36
+ data = File.open path
37
+ file = @bucket.files.create(:key=>name, :body=>data, :public=>true)
38
+ say_status "Uploaded", name, :green
39
+ end
40
+ end
41
+
42
+ def complete
43
+ host = "http://#{bucket}.s3-website-#{Fog.credentials[:region]}.amazonaws.com/"
44
+ say_status "Available at", host, :green
45
+ end
46
+
47
+ private
48
+ def bucket
49
+ Amiba::Configuration.site_name
50
+ end
51
+
52
+ def location
53
+ Amiba::Configuration.s3_location || "EU"
54
+ end
55
+
56
+ end
57
+
58
+ class Generate < Thor::Group
59
+ include Amiba::Generator
60
+
61
+ namespace :"site:generate"
62
+
63
+ def self.source_root
64
+ Dir.pwd
65
+ end
66
+
67
+ def cleardown
68
+ remove_dir Amiba::Configuration.site_dir
69
+ remove_dir Amiba::Configuration.staged_dir
70
+ end
71
+
72
+ def create_site_structure
73
+ empty_directory Amiba::Configuration.site_dir
74
+ end
75
+
76
+ def copy_favicon
77
+ if File.exists? "public/images/favicon.ico"
78
+ copy_file "public/images/favicon.ico", File.join(Amiba::Configuration.site_dir, "public/favicon.ico")
79
+ end
80
+ end
81
+
82
+ def copy_xdomain
83
+ if File.exists? "public/crossdomain.xml"
84
+ copy_file "public/crossdomain.xml", File.join(Amiba::Configuration.site_dir, "public/crossdomain.xml")
85
+ end
86
+ end
87
+
88
+ def copy_javascript
89
+ directory "public/js", File.join(Amiba::Configuration.site_dir, "public/js")
90
+ end
91
+
92
+ def copy_images
93
+ directory "public/images", File.join(Amiba::Configuration.site_dir, "public/images")
94
+ end
95
+
96
+ def copy_css
97
+ Dir.glob('public/css/*.css').each do |css_file|
98
+ copy_file css_file, File.join(Amiba::Configuration.site_dir, "public/css/", File.basename(css_file))
99
+ end
100
+ end
101
+
102
+ def process_and_copy_sass
103
+ Dir.glob('public/css/[^_]*.scss').each do |scss_file|
104
+ create_file File.join(Amiba::Configuration.site_dir,"public/css/", File.basename(scss_file).gsub('scss', 'css')) do
105
+ Tilt.new(scss_file).render
106
+ end
107
+ end
108
+ end
109
+
110
+ def build_pages
111
+ Dir.glob('pages/**/[^_]*').each do |page_file|
112
+ next if File.directory? page_file
113
+ page = Amiba::Source::Page.new(File.relpath(page_file, "pages"))
114
+ next unless page.state == "published"
115
+ build_page page
116
+ end
117
+ end
118
+
119
+ def build_entries
120
+ Amiba::Source::Entry.all.each do |entry|
121
+ build_page entry
122
+ end
123
+ end
124
+
125
+ def build_json
126
+ Dir.glob('entries/*').each do |cat|
127
+ c = File.basename cat
128
+ create_file(File.join(Amiba::Configuration.site_dir, "public", c, "latest.json")) do
129
+ Amiba::Source::Entry.send(c.to_sym.pluralize).limit(20).each.inject([]) do |acc, ent|
130
+ a = ent.metadata
131
+ a["content"] = ent.render
132
+ acc << a
133
+ end.to_json
134
+ end
135
+ end
136
+ end
137
+
138
+ def build_feeds
139
+ Dir.glob('feeds/*.builder').each do |feed_file|
140
+ feed = Amiba::Source::Feed.new(feed_file)
141
+ create_file(feed.output_filename) do
142
+ Tilt.new(feed.filename).render(Amiba::Scope.new(feed), :xml => Builder::XmlMarkup.new)
143
+ end
144
+ end
145
+ end
146
+
147
+ private
148
+
149
+ def build_layout(page)
150
+ layout = Amiba::Source::Layout.new(page.layout)
151
+ return layout if File.exists? layout.staged_filename
152
+ create_file(layout.staged_filename) do layout.content end
153
+ layout
154
+ end
155
+
156
+ def build_page(page)
157
+ layout = build_layout(page)
158
+ create_file(page.staged_filename) do page.content end
159
+ create_file(page.output_filename) do
160
+ Tilt.new(layout.staged_filename).render(Amiba::Scope.new(page))
161
+ end
162
+ end
163
+
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,42 @@
1
+ module Amiba
2
+ module Source
3
+ class Entry
4
+ extend Amiba::Source::EntryFinder
5
+ include Amiba::Source
6
+
7
+ attr_accessor :category
8
+ metadata_fields :title, :slug, :state, :layout
9
+
10
+ validates_presence_of :title, :state, :layout
11
+
12
+ def initialize(category, name, format, metadata = nil, content = nil)
13
+ self.category = category
14
+ self.name = name
15
+ self.format = format
16
+ self.metadata = metadata
17
+ self.content = content
18
+ end
19
+
20
+ def filename
21
+ File.join("entries", category.to_s.downcase.pluralize, name + ".#{format.to_s}")
22
+ end
23
+
24
+ def staged_filename
25
+ File.join(Amiba::Configuration.staged_dir, filename)
26
+ end
27
+
28
+ def output_filename
29
+ File.join(Amiba::Configuration.site_dir, 'public', category.to_s.downcase.pluralize, "#{name}.html")
30
+ end
31
+
32
+ def link
33
+ URI.escape( ["", category.to_s.downcase.pluralize, "#{name}.html"].join("/") )
34
+ end
35
+
36
+ def render
37
+ Tilt.new(self.staged_filename).render(Amiba::Scope.new(self))
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,136 @@
1
+ module Amiba
2
+ module Source
3
+ module EntryFinder
4
+
5
+ def method_missing(method_name, *args, &block)
6
+ proxy = FinderProxy.new
7
+ proxy.send(method_name, *args, &block)
8
+ rescue
9
+ raise ArgumentError.new("#{method_name} does not exist")
10
+ end
11
+
12
+ end
13
+
14
+ class FinderProxy
15
+ include Amiba::Repo
16
+ include Enumerable
17
+
18
+ def each(&blk)
19
+ entries.each(&blk)
20
+ end
21
+
22
+ def first
23
+ entries.first
24
+ end
25
+
26
+ def last
27
+ entries.last
28
+ end
29
+
30
+ def count
31
+ entries.count
32
+ end
33
+
34
+ def [](index)
35
+ entries[index]
36
+ end
37
+
38
+ def entries
39
+ result = (scopes[:category] || CategoryScope.new).apply
40
+ result = (scopes[:state] || StateScope.new).apply(result)
41
+ # reverse sorting is a more natural approach
42
+ result.sort! do |a, b|
43
+ last_commit_date(b.filename) <=> last_commit_date(a.filename)
44
+ end
45
+ result = scopes[:offset].apply(result) if scopes[:offset]
46
+ result = scopes[:limit].apply(result) if scopes[:limit]
47
+ result
48
+ end
49
+
50
+ [:draft, :published, :any].each do |state|
51
+ define_method state do
52
+ self[:state] = StateScope.new(state)
53
+ self
54
+ end
55
+ end
56
+
57
+ def method_missing(method_name, *args, &block)
58
+ entry_types = (Dir.glob('entries/*') << "all").map {|c| File.basename(c).to_sym}
59
+ if entry_types.include?(method_name)
60
+ self[:category] = CategoryScope.new(method_name)
61
+ else
62
+ raise ArgumentError
63
+ end
64
+ self
65
+ end
66
+
67
+ def offset(index)
68
+ self[:offset] = OffsetScope.new(index)
69
+ self
70
+ end
71
+
72
+ def limit(count)
73
+ self[:limit] = LimitScope.new(count)
74
+ self
75
+ end
76
+
77
+ protected
78
+
79
+ def []=(key, val)
80
+ scopes[key] = val
81
+ self
82
+ end
83
+
84
+ def scopes
85
+ @scopes ||= {}
86
+ end
87
+
88
+ end
89
+
90
+ class CategoryScope
91
+ include Amiba::Repo
92
+ def initialize(category = :all)
93
+ @category = category
94
+ end
95
+ def apply
96
+ entry_files.map do |ef|
97
+ _, category, filename = ef.split('/')
98
+ name, format = filename.split('.')
99
+ Amiba::Source::Entry.new(category, name, format)
100
+ end
101
+ end
102
+ def entry_files
103
+ globstring = "entries/#{@category == :all ? '*' : @category.to_s}/*"
104
+ Dir.glob(globstring).select {|e| !repo.log(e).empty?}
105
+ end
106
+ end
107
+
108
+ class StateScope
109
+ def initialize(state = :published)
110
+ @state = state
111
+ end
112
+ def apply(entries)
113
+ return entries if @state == :any
114
+ entries.select {|e| e.state.to_sym == @state}
115
+ end
116
+ end
117
+
118
+ class OffsetScope
119
+ def initialize(offset = 0)
120
+ @offset = offset
121
+ end
122
+ def apply(entries)
123
+ entries[@offset..-1]
124
+ end
125
+ end
126
+
127
+ class LimitScope
128
+ def initialize(limit = -1)
129
+ @limit = (limit == -1 ? -1 : limit - 1)
130
+ end
131
+ def apply(entries)
132
+ entries[0..@limit]
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,34 @@
1
+
2
+ module Amiba
3
+ module Source
4
+ class Feed
5
+ include Amiba::Source
6
+
7
+ attr_accessor :type, :name
8
+ def initialize(fn)
9
+ self.name, self.type = File.basename(fn, ".builder").split(".")
10
+ end
11
+
12
+ def filename
13
+ @filename ||= File.join("feeds", "#{@name}.#{@type}.builder")
14
+ end
15
+
16
+ def content=(c)
17
+ @content ||= self.new? ? c : File.read(filename)
18
+ end
19
+
20
+ def staged_filename
21
+ File.join(Amiba::Configuration.staged_dir, filename)
22
+ end
23
+
24
+ def output_filename
25
+ File.join(Amiba::Configuration.site_dir, "public/#{name}.#{type}")
26
+ end
27
+
28
+ def link
29
+ URI.escape "/#{name}.#{type}"
30
+ end
31
+
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,22 @@
1
+ module Amiba
2
+ module Source
3
+ class Partial
4
+ include Amiba::Source
5
+
6
+ attr_accessor :dir, :name
7
+ def initialize(path)
8
+ self.dir, self.name = File.split path
9
+ end
10
+
11
+ def filename
12
+ @filename ||= File.join("pages", @dir, "_#{@name}.haml")
13
+ end
14
+
15
+ def staged_filename
16
+ File.join(Amiba::Configuration.staged_dir, filename)
17
+ end
18
+ alias_method :output_filename, :staged_filename
19
+
20
+ end
21
+ end
22
+ end