zine 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,11 +1,23 @@
1
1
  <footer>
2
- <div class="contact">
2
+ <div class="column">
3
+ <h3>Contact me</h3>
3
4
  <p>Twitter:&nbsp;<a href="https://twitter.com/<%= page[:twitter_name] %>">@<%= page[:twitter_name] %></a></p>
4
5
  </div>
5
- <div class="contact">
6
- <p>&copy; 2017<span> <%= page[:site_author] %></span>
7
- </p>
6
+ <div class="column">
7
+ <h3>Me elsewhere</h3>
8
+ <ul>
9
+ <% for @link in page[:links_array] %>
10
+ <li><a href="<%= @link['uri'] %>"><%= @link['name'] %></a></li>
11
+ <% end %>
12
+ </ul>
8
13
  </div>
14
+ <div class="column">
15
+ <h3>The fine print</h3>
16
+ <ul>
17
+ <li>Built with <a href="https://github.com/mikekreuzer/zine">Zine</a></li>
18
+ <li>&copy; 2017<span> <%= page[:site_author] %></span></li>
19
+ </ul>
20
+ <div>
9
21
  </footer>
10
22
  </body>
11
23
  </html>
@@ -2,7 +2,7 @@
2
2
  <main>
3
3
  <section>
4
4
  <% for @post in data %>
5
- <h2><%= @post[:page][:title] %></h2>
5
+ <h2><a href="<%= @post[:uri] %>"><%= @post[:page][:title] %></a></h2>
6
6
  <p class="date"><%= @post[:page][:date_us] %></p>
7
7
  <%= @post[:html] %>
8
8
  <div class="tags">Tags:
@@ -13,6 +13,8 @@
13
13
  </ul>
14
14
  </div>
15
15
  <% end %>
16
+ <h2>Previously</h2>
17
+ <p><a href="/articles.html">Earlier posts...</a></p>
16
18
  </section>
17
19
  </main>
18
20
  <%= footer_partial %>
@@ -1,28 +1,41 @@
1
1
  ---
2
2
  directories:
3
- blog: blog
4
- build: build
5
- assets: assets
6
- posts: posts
7
- source: source
8
- styles: styles
9
- templates: templates
10
- templates:
11
- articles: articles
12
- default: default
13
- home: home
14
- new_post: new_post.erb
15
- post: post
16
- rss: rss
17
- tag: tag
18
- tag_index: tag_index
3
+ assets: assets
4
+ blog: blog
5
+ build: build
6
+ posts: posts
7
+ source: source
8
+ styles: styles
9
+ templates: templates
10
+ links:
11
+ -
12
+ name: GitHub
13
+ uri: https://github.com/mikekreuzer
14
+ -
15
+ name: Ripley
16
+ uri: https://mikekreuzer.github.io/Ripley/
19
17
  options:
20
- css_preprocessor: less
21
- github_name: mikekreuzer
22
- number_items_in_RSS: 7
23
- num_items_on_home: 7
24
- site_author: Mike Kreuzer
25
- site_description: The scribblings of a once and future code monkey
26
- site_name: Mike Kreuzer
27
- site_URL: https://mikekreuzer.com
28
- twitter_name: mikekreuzer
18
+ css_preprocessor: less
19
+ github_name: mikekreuzer
20
+ number_items_in_RSS: 7
21
+ num_items_on_home: 7
22
+ site_author: Mike Kreuzer
23
+ site_description: The scribblings of a once and future code monkey
24
+ site_name: Mike Kreuzer
25
+ site_URL: https://mikekreuzer.com
26
+ twitter_name: mikekreuzer
27
+ templates:
28
+ articles: articles
29
+ default: default
30
+ home: home
31
+ new_post: new_post.erb
32
+ post: post
33
+ rss: rss
34
+ tag: tag
35
+ tag_index: tag_index
36
+ upload:
37
+ credentials: /local/absolute/path/to/yaml/file/with/username/and/password
38
+ host: 127.0.0.1
39
+ method: none|sftp
40
+ path: /remote/absolute/path/to/html
41
+ verbose: true
data/lib/zine/style.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'sassc'
2
+
3
+ module Zine
4
+ # Render sass into CSS in the source directory, to be copied later
5
+ class Style
6
+ # Source & destination files
7
+ def initialize(directories)
8
+ @style_file = File.join directories['styles'], 'screen.scss'
9
+ @css_file = File.join directories['source'], 'screen.css'
10
+ end
11
+
12
+ # Write the CSS file
13
+ def process
14
+ sass = File.open(@style_file, 'r').read
15
+ css = SassC::Engine.new(sass, style: :compressed).render
16
+ File.write @css_file, css
17
+ end
18
+ end
19
+ end
data/lib/zine/tag.rb CHANGED
@@ -8,6 +8,7 @@ module Zine
8
8
  @posts_by_tag = sort_tags tags_by_post
9
9
  @templates = { tag: tag_templates, tag_index: tag_index_templates }
10
10
  @tag_dir = File.join @options['directories']['build'], 'tags'
11
+ FileUtils.remove_dir @tag_dir, force: true
11
12
  FileUtils.mkdir_p @tag_dir
12
13
  end
13
14
 
@@ -0,0 +1,161 @@
1
+ require 'net/ssh'
2
+ require 'net/sftp'
3
+ require 'rainbow'
4
+ require 'set'
5
+
6
+ module Zine
7
+ # Deploy changes to a remote host
8
+ # TODO: add GitHub deploys as well...
9
+ class Upload
10
+ # a folder in a path
11
+ Node = Struct.new(:name, :path_string)
12
+
13
+ def initialize(build_dir, options, delete_file_array, upload_file_array)
14
+ return unless options['method'] == 'sftp'
15
+
16
+ @build_dir = build_dir
17
+ @host = options['host']
18
+ @path = options['path']
19
+ @verbose = options['verbose']
20
+
21
+ cred_file = options['credentials']
22
+ @credentials = parse_yaml(File.open(cred_file, 'r'), cred_file)
23
+
24
+ @upload_file_array = Set.new(upload_file_array).to_a
25
+ @delete_file_array = Set.new(delete_file_array).to_a - @upload_file_array
26
+ end
27
+
28
+ def delete
29
+ Net::SFTP.start(@host, @credentials['username'],
30
+ password: @credentials['password']) do |sftp|
31
+ @delete_file_array.each do |rel_file_path|
32
+ sftp.remove(File.join(@path, rel_file_path)).wait
33
+ puts "Deleted #{rel_file_path}" if @verbose
34
+ end
35
+ end
36
+ end
37
+
38
+ def deploy
39
+ Net::SFTP.start(@host, @credentials['username'],
40
+ password: @credentials['password']) do |sftp|
41
+ deploy_directories sftp
42
+ deploy_files sftp
43
+ end
44
+ end
45
+
46
+ private
47
+
48
+ def break_strings_into_arrays(paths_array)
49
+ paths_array.map do |dir|
50
+ dir.to_s.split File::SEPARATOR
51
+ end
52
+ end
53
+
54
+ # make directories
55
+ def deploy_directories(sftp)
56
+ node_array = make_sparse_node_array @upload_file_array
57
+ node_array.each do |level|
58
+ level.each do |node|
59
+ next if node.nil?
60
+ path_string = node.path_string
61
+ mkdir_p(sftp, path_string)
62
+ puts "mkdir_p #{path_string}" if @verbose
63
+ end
64
+ end
65
+ end
66
+
67
+ # upload files
68
+ def deploy_files(sftp)
69
+ @upload_file_array.each do |rel_file_path|
70
+ sftp.upload(File.join(@build_dir, rel_file_path),
71
+ File.join(@path, rel_file_path),
72
+ permissions: 0o644).wait # -rw-r--r--
73
+ puts "Uploaded #{rel_file_path}" if @verbose
74
+ end
75
+ end
76
+
77
+ def mkdir_p(sftp, path)
78
+ sftp.mkdir!(File.join(@path, path), permissions: 0o755) # drwxr-xr-x
79
+ rescue Net::SFTP::StatusException => error
80
+ raise if error.code != 4 && error.code != 11 # folder already exists
81
+ end
82
+
83
+ # make a sparse matrix as a directory tree, to make mkdir calls efficiently
84
+ def make_sparse_node_array(file_array)
85
+ paths_array = remove_filenames file_array # remove file names
86
+ paths_array = Set.new(paths_array).to_a # remove duplicates
87
+ paths_array = break_strings_into_arrays paths_array # to array of arrays
88
+ paths_array = strings_to_nodes paths_array # ...of nodes
89
+ level_array = transpose paths_array # arrayed by level
90
+ remove_duplicates level_array # make it sparse
91
+ end
92
+
93
+ def parse_yaml(text, cred_file)
94
+ YAML.safe_load text
95
+ rescue Psych::Exception
96
+ puts Rainbow("Could not parse YAML in: #{cred_file}").red
97
+ { 'username' => '', 'password' => '' }
98
+ end
99
+
100
+ # for each level, if a node has the same name & same path it's a duplicate
101
+ def remove_duplicates(level_array)
102
+ level_array.map do |level|
103
+ length = level.length
104
+ level_copy = level
105
+ level.each_with_index.map do |node, index|
106
+ next if node.nil?
107
+ if index.zero?
108
+ node
109
+ elsif !level_copy[0..(index - 1)]
110
+ .index { |item| same(item, node) }.nil? ||
111
+ !level_copy[(index + 1)..(length - 1)]
112
+ .index { |item| same(item, node) }.nil?
113
+ level_copy[index] = nil
114
+ nil
115
+ else
116
+ node
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ # array of file paths to array of directory paths
123
+ def remove_filenames(file_array)
124
+ file_array.map do |file|
125
+ path = File.dirname file
126
+ if path == '.'
127
+ nil
128
+ else
129
+ path
130
+ end
131
+ end
132
+ end
133
+
134
+ # equality for a node
135
+ def same(first, second)
136
+ return false if first.nil? || second.nil?
137
+ first.name == second.name && first.path_string == second.path_string
138
+ end
139
+
140
+ # convert directory names to nodes with knowledge of their parentage
141
+ def strings_to_nodes(paths_array)
142
+ paths_array.map do |path|
143
+ path_string_array = path
144
+ path.each_with_index.map do |elem, index|
145
+ Node.new(elem, path_string_array[0..index].join(File::SEPARATOR))
146
+ end
147
+ end
148
+ end
149
+
150
+ # flip it, to get directories by level in a path, rather than by path
151
+ def transpose(input)
152
+ result = []
153
+ max_size = input.max_by(&:size).size
154
+ max_size.times do |i|
155
+ result[i] = Array.new(input.first.size)
156
+ input.each_with_index { |r, j| result[i][j] = r[i] }
157
+ end
158
+ result
159
+ end
160
+ end
161
+ end
data/lib/zine/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Zine
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
@@ -0,0 +1,88 @@
1
+ require 'listen'
2
+ require 'pathname'
3
+
4
+ module Zine
5
+ # Watch files for changes
6
+ class Watcher
7
+ attr_reader :upload_array, :delete_array
8
+
9
+ def initialize(posts_and_headlines, build_directory, source_directory)
10
+ @posts_and_headlines = posts_and_headlines
11
+ @build_directory = File.join Dir.pwd, build_directory
12
+ @source_directory = File.join Dir.pwd, source_directory
13
+ @upload_array = []
14
+ @delete_array = []
15
+ end
16
+
17
+ # Build a delete list & an upload list for SSH from changes in build,
18
+ # & rebuild & reload on changes in source
19
+ def start
20
+ watch_build_dir
21
+ watch_source_dir
22
+ end
23
+
24
+ private
25
+
26
+ def on_build_change(path_string_array)
27
+ path_string_array.each do |str|
28
+ rel_path = rel_path_from_build_dir str
29
+ @upload_array << rel_path
30
+ end
31
+ end
32
+
33
+ def on_build_delete(path_string_array)
34
+ path_string_array.each do |str|
35
+ rel_path = rel_path_from_build_dir str
36
+ @delete_array << rel_path
37
+ # @upload_array.delete rel_path
38
+ end
39
+ end
40
+
41
+ def rel_path_from_build_dir(path)
42
+ full = Pathname(path)
43
+ full.relative_path_from(Pathname(@build_directory))
44
+ end
45
+
46
+ # rebuild the file, and the headline files & tags
47
+ # TODO: moves within the watched directory won't delete the old location
48
+ def on_source_change(path)
49
+ path.each do |file|
50
+ if !file.nil? && (file =~ /^.+\.md$/).nil?
51
+ @posts_and_headlines.preview_straight_copy file
52
+ else
53
+ @posts_and_headlines.preview_rebuild file
54
+ end
55
+ end
56
+ end
57
+
58
+ # delete build file from posts, then posts entry
59
+ # rebuild the headline files... and the tags...
60
+ def on_source_delete(path)
61
+ path.each do |file|
62
+ if !file.nil? && (file =~ /^.+\.md$/).nil?
63
+ @posts_and_headlines.preview_straight_delete file
64
+ else
65
+ @posts_and_headlines.preview_delete file
66
+ end
67
+ end
68
+ end
69
+
70
+ def watch_build_dir
71
+ listener = Listen.to(@build_directory) do |modified, added, removed|
72
+ on_build_change modified unless modified.empty?
73
+ on_build_change added unless added.empty?
74
+ on_build_delete removed unless removed.empty?
75
+ end
76
+ listener.start
77
+ end
78
+
79
+ def watch_source_dir
80
+ listener = Listen.to(@source_directory) do |modified, added, removed|
81
+ on_source_change modified unless modified.empty?
82
+ on_source_change added unless added.empty?
83
+ on_source_delete removed unless removed.empty?
84
+ end
85
+ listener.start
86
+ end
87
+ end
88
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: zine
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
  - Mike Kreuzer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-02-09 00:00:00.000000000 Z
11
+ date: 2017-02-19 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,6 +66,26 @@ dependencies:
66
66
  - - "~>"
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0.12'
69
+ - !ruby/object:Gem::Dependency
70
+ name: highline
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.7'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 1.7.8
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '1.7'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 1.7.8
69
89
  - !ruby/object:Gem::Dependency
70
90
  name: htmlcompressor
71
91
  requirement: !ruby/object:Gem::Requirement
@@ -73,6 +93,9 @@ dependencies:
73
93
  - - "~>"
74
94
  - !ruby/object:Gem::Version
75
95
  version: '0.3'
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ version: 0.3.1
76
99
  type: :runtime
77
100
  prerelease: false
78
101
  version_requirements: !ruby/object:Gem::Requirement
@@ -80,6 +103,9 @@ dependencies:
80
103
  - - "~>"
81
104
  - !ruby/object:Gem::Version
82
105
  version: '0.3'
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: 0.3.1
83
109
  - !ruby/object:Gem::Dependency
84
110
  name: kramdown
85
111
  requirement: !ruby/object:Gem::Requirement
@@ -87,6 +113,9 @@ dependencies:
87
113
  - - "~>"
88
114
  - !ruby/object:Gem::Version
89
115
  version: '1.13'
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: 1.13.2
90
119
  type: :runtime
91
120
  prerelease: false
92
121
  version_requirements: !ruby/object:Gem::Requirement
@@ -94,6 +123,49 @@ dependencies:
94
123
  - - "~>"
95
124
  - !ruby/object:Gem::Version
96
125
  version: '1.13'
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: 1.13.2
129
+ - !ruby/object:Gem::Dependency
130
+ name: listen
131
+ requirement: !ruby/object:Gem::Requirement
132
+ requirements:
133
+ - - "~>"
134
+ - !ruby/object:Gem::Version
135
+ version: '3.0'
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 3.1.5
139
+ type: :runtime
140
+ prerelease: false
141
+ version_requirements: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '3.0'
146
+ - - ">="
147
+ - !ruby/object:Gem::Version
148
+ version: 3.1.5
149
+ - !ruby/object:Gem::Dependency
150
+ name: net-sftp
151
+ requirement: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: '2.1'
156
+ - - ">="
157
+ - !ruby/object:Gem::Version
158
+ version: 2.1.2
159
+ type: :runtime
160
+ prerelease: false
161
+ version_requirements: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '2.1'
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: 2.1.2
97
169
  - !ruby/object:Gem::Dependency
98
170
  name: rainbow
99
171
  requirement: !ruby/object:Gem::Requirement
@@ -101,6 +173,9 @@ dependencies:
101
173
  - - "~>"
102
174
  - !ruby/object:Gem::Version
103
175
  version: '2.2'
176
+ - - ">="
177
+ - !ruby/object:Gem::Version
178
+ version: 2.2.1
104
179
  type: :runtime
105
180
  prerelease: false
106
181
  version_requirements: !ruby/object:Gem::Requirement
@@ -108,6 +183,29 @@ dependencies:
108
183
  - - "~>"
109
184
  - !ruby/object:Gem::Version
110
185
  version: '2.2'
186
+ - - ">="
187
+ - !ruby/object:Gem::Version
188
+ version: 2.2.1
189
+ - !ruby/object:Gem::Dependency
190
+ name: sassc
191
+ requirement: !ruby/object:Gem::Requirement
192
+ requirements:
193
+ - - "~>"
194
+ - !ruby/object:Gem::Version
195
+ version: '1.11'
196
+ - - ">="
197
+ - !ruby/object:Gem::Version
198
+ version: 1.11.2
199
+ type: :runtime
200
+ prerelease: false
201
+ version_requirements: !ruby/object:Gem::Requirement
202
+ requirements:
203
+ - - "~>"
204
+ - !ruby/object:Gem::Version
205
+ version: '1.11'
206
+ - - ">="
207
+ - !ruby/object:Gem::Version
208
+ version: 1.11.2
111
209
  - !ruby/object:Gem::Dependency
112
210
  name: thin
113
211
  requirement: !ruby/object:Gem::Requirement
@@ -129,6 +227,9 @@ dependencies:
129
227
  - - "~>"
130
228
  - !ruby/object:Gem::Version
131
229
  version: '0.19'
230
+ - - ">="
231
+ - !ruby/object:Gem::Version
232
+ version: 0.19.4
132
233
  type: :runtime
133
234
  prerelease: false
134
235
  version_requirements: !ruby/object:Gem::Requirement
@@ -136,6 +237,9 @@ dependencies:
136
237
  - - "~>"
137
238
  - !ruby/object:Gem::Version
138
239
  version: '0.19'
240
+ - - ">="
241
+ - !ruby/object:Gem::Version
242
+ version: 0.19.4
139
243
  description: |-
140
244
  Yet another blog aware static site generator.
141
245
  These are the very early days of zine, expect breaking changes.
@@ -157,10 +261,16 @@ files:
157
261
  - lib/zine/data_page.rb
158
262
  - lib/zine/page.rb
159
263
  - lib/zine/post.rb
264
+ - lib/zine/posts_and_headlines.rb
160
265
  - lib/zine/server.rb
161
266
  - lib/zine/skeleton/source/about.md
267
+ - lib/zine/skeleton/source/assets/webicon-rss-m.png
268
+ - lib/zine/skeleton/source/assets/webicon-rss.svg
269
+ - lib/zine/skeleton/source/assets/webicon-twitter-m.png
270
+ - lib/zine/skeleton/source/assets/webicon-twitter.svg
162
271
  - lib/zine/skeleton/source/posts/2017-01-25-my-new-blog.md
163
272
  - lib/zine/skeleton/source/screen.css
273
+ - lib/zine/skeleton/source/styles/screen.scss
164
274
  - lib/zine/skeleton/source/templates/articles.erb
165
275
  - lib/zine/skeleton/source/templates/default.erb
166
276
  - lib/zine/skeleton/source/templates/footer_partial.erb
@@ -172,9 +282,12 @@ files:
172
282
  - lib/zine/skeleton/source/templates/tag.erb
173
283
  - lib/zine/skeleton/source/templates/tag_index.erb
174
284
  - lib/zine/skeleton/zine.yaml
285
+ - lib/zine/style.rb
175
286
  - lib/zine/tag.rb
176
287
  - lib/zine/templates.rb
288
+ - lib/zine/upload.rb
177
289
  - lib/zine/version.rb
290
+ - lib/zine/watcher.rb
178
291
  homepage: https://github.com/mikekreuzer/zine
179
292
  licenses:
180
293
  - MIT