postwave 0.1.2 → 0.1.4

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b4511f2e9288a14247515ff919efbcdfaf4aef9f7e6ae5d7de5c207795307d73
4
- data.tar.gz: 6997bdd3f519948e40fbf701d792bc761e0d563f23bd171b47cf35b6ba652fb8
3
+ metadata.gz: d0eb03ce8fdc3e97f2f54515cbfaa7d527e2f0df541ffd4d993966535b7a8483
4
+ data.tar.gz: c4b81108e5facc67d6e1799c8907f9d220f34ab9779e270b18bf9787408ca571
5
5
  SHA512:
6
- metadata.gz: 3248a5fdd235809dee5026f53ecf5dd637c96a3e69e6acff19d91617a6632ae61e49989a79dd16f91258b21de14d54ed748b31af5dbb2974d3e0c97d6f65accf
7
- data.tar.gz: 74bd0b0f980cb534b63c06a3374e4d1e807c68113cf18cc56f1bf6aa601adb4b4c2ae0c5bb55103ed2d2a496064b1b730435933dda937eb2aa446a92bf3279fc
6
+ metadata.gz: 84e334c164a2b1d02ff90b03657493bfd693985f52ef171311dd0319b915439ee04fa315982ab4b6052a295ea229ac0443421e9bbe08fe8ff9050023c4f26be9
7
+ data.tar.gz: 4f98af33627b143439e7aa75827705c5a74162791e2d066ba3024f99afa5de0df0b13437801da31f40ac00051bea9cca26f96b57c15484f83cab26a9594329ac
data/README.md CHANGED
@@ -4,12 +4,16 @@
4
4
 
5
5
  Write your posts statically. Interact with them dynamically.
6
6
 
7
+ [postwave.blog](https://postwave.blog/)
8
+
7
9
  ## What is Postwave?
8
10
 
9
11
  Postwave is an opinionated flat-file based based blog engine.
10
12
 
11
13
  It lets you write posts in Markdown and then display them on a dynamic site using the client functionality.
12
14
 
15
+ Read more about the philosophy behind [what Postwave is for](https://postwave.blog/posts/what-is-postwave-for).
16
+
13
17
  ## Installation
14
18
 
15
19
  ```
@@ -113,18 +117,20 @@ postwave_client = Postwave::Client.new("path/to/config/postwave.yaml", preload:
113
117
 
114
118
  ### Get a Single Post
115
119
 
116
- Pass in the stub (the filename without '.md') for the post.
120
+ Pass in the stub (the filename without the date and '.md') for the post.
117
121
  ```ruby
118
122
  post = postwave_client.post("my-great-post")
119
123
 
120
- # <Postwave::Post title="My Great Post", date=<Time ...>, tags=["tag1"], body="bla bla bla..">
124
+ # <Postwave::Post title="My Great Post", date=<Time ...>, tags=["tag1"], body="bla bla bla">
121
125
 
122
126
  puts post.title
123
127
  # "My Great Post"
124
128
  puts post.body
125
- # "bla bla bla..."
129
+ # "bla bla bla"
126
130
  puts post.body_html # uses Redcarpt to convert body Markdown to HTML
127
- # "<p>bla bla bla...</p>"
131
+ # "<p>bla bla bla</p>"
132
+ puts post.body_preview(8) # an HTML free preview of the post with optional `limit` and `ellipsis` params
133
+ # "bla bla..."
128
134
  ```
129
135
 
130
136
  ### Get a Collection of Posts
@@ -227,6 +233,10 @@ archive = postwave_client.archive(by: "month")
227
233
  # }
228
234
  ```
229
235
 
236
+ ## Example Site
237
+
238
+ The code for [postwave.blog](https://postwave.blog/) is available at [github.com/dorkrawk/postwave-site](https://github.com/dorkrawk/postwave-site) and serves as a good example of how to use Postwave in a real world project.
239
+
230
240
  ## Run Tests
231
241
 
232
242
  ```
@@ -32,7 +32,7 @@ module Postwave
32
32
 
33
33
  def archive(by: "year")
34
34
  working_index = @full_index || get_full_index
35
- post_hash = posts.group_by { |post| post.date.year }.transform_values { |posts| posts.sort_by(&:date) }
35
+ post_hash = working_index.group_by { |post| post.date.year }.transform_values { |posts| posts.sort_by(&:date) }
36
36
  if by == "month"
37
37
  post_hash.each do |key, value|
38
38
  post_hash[key] = value.group_by { |post| post.date.month }
@@ -119,7 +119,7 @@ module Postwave
119
119
 
120
120
  def get_summary
121
121
  summary_file_path = File.join(@blog_root, POSTS_DIR, META_DIR, SUMMARY_FILE_NAME)
122
- YAML.load_file(summary_file_path)
122
+ YAML.load_file(summary_file_path, permitted_classes: [Time, Symbol])
123
123
  end
124
124
  end
125
125
  end
data/lib/postwave/post.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require_relative "blog_utilities"
2
2
  require "redcarpet"
3
+ require "cgi"
3
4
 
4
5
  module Postwave
5
6
  class Post
@@ -7,7 +8,8 @@ module Postwave
7
8
 
8
9
  KNOWN_FIELDS = %w(title date tags title_slug body draft)
9
10
  REQUIRED_FIELDS = %w(title date)
10
- MEATADATA_DELIMTER = "---"
11
+ METADATA_DELIMITER = "---"
12
+ FILE_NAME_DATE_LEN = 11 # YYYY-MM-DD-
11
13
 
12
14
  attr_accessor :file_name
13
15
 
@@ -20,7 +22,7 @@ module Postwave
20
22
 
21
23
  File.readlines(path).each do |line|
22
24
  clean_line = line.strip
23
- if clean_line == MEATADATA_DELIMTER
25
+ if clean_line == METADATA_DELIMITER
24
26
  metadata_delimter_count += 1
25
27
  next
26
28
  end
@@ -65,11 +67,18 @@ module Postwave
65
67
  instance_variable_set("@#{field}", value) unless self.instance_variables.include?("@#{field}".to_sym)
66
68
  self.class.send(:attr_reader, field) unless self.public_methods.include?(field.to_sym)
67
69
  end
70
+
71
+ @slug = file_name_slug&.empty? ? title_slug : file_name_slug
68
72
  end
69
73
 
70
74
  def title_slug
71
75
  @title_slug ||= @title.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
72
76
  end
77
+
78
+ def file_name_slug
79
+ # YYYY-MM-DD-slug.md
80
+ File.basename(@file_name, ".md")[FILE_NAME_DATE_LEN..]
81
+ end
73
82
 
74
83
  def slug
75
84
  @slug ||= @title_slug
@@ -80,7 +89,34 @@ module Postwave
80
89
  end
81
90
 
82
91
  def body_html
83
- @@markdown.render(@body)
92
+ @body_html ||= @@markdown.render(@body)
93
+ end
94
+
95
+ def body_preview(limit = 100, ellipsis = "...")
96
+ text = body_html.to_s
97
+
98
+ # Turn common block boundaries into newlines first
99
+ text = text.gsub(/<(br|\/p|\/div|\/h\d|\/li)[^>]*>/i, "\n")
100
+ # Strip remaining tags
101
+ text = text.gsub(/<[^>]*>/, "")
102
+ # Decode entities
103
+ text = CGI.unescapeHTML(text)
104
+ # Collapse spaces but keep single newlines
105
+ text = text.gsub(/[ \t]+/, " ").gsub(/\n+/, "\n").strip
106
+
107
+ # Unicode-safe character counting (grapheme clusters)
108
+ graphemes = text.scan(/\X/)
109
+ return text if graphemes.length <= limit
110
+
111
+ candidate = graphemes.first(limit).join
112
+
113
+ # If we cut mid-word, trim back to the previous word boundary.
114
+ # Word characters = letters, numbers, marks, connector punctuation (e.g., underscore)
115
+ # This removes a trailing partial word if present.
116
+ cut = candidate.sub(/[\p{L}\p{N}\p{M}\p{Pc}]+\z/u, "").rstrip
117
+
118
+ # If the very first "word" exceeds the limit, we err under limit and just show an ellipsis.
119
+ cut.empty? ? ellipsis : "#{cut}#{ellipsis}"
84
120
  end
85
121
 
86
122
  def generated_file_name
@@ -15,16 +15,17 @@ module Postwave
15
15
  end
16
16
 
17
17
  def feed_content(posts)
18
- link = config_values[:url]
18
+ link = config_values[:url].chomp("/")
19
19
  updated = Time.now.iso8601.to_s
20
20
  title = config_values[:name]
21
21
  description = config_values[:description]
22
22
 
23
23
  markdown = Redcarpet::Markdown.new(Redcarpet::Render::HTML, fenced_code_blocks: true)
24
24
  feed_posts = posts.map do |post|
25
- post_link = "#{config_values[:url]}/#{config_values[:posts_path]}/#{post.slug}"
25
+ post_link = "#{link}/#{config_values[:posts_path]}/#{post.slug}"
26
26
  html_body = CGI.unescapeHTML(markdown.render(post.body))
27
- FeedPost.new(post.title, post_link, html_body, post.date.iso8601, post.tags)
27
+ post_title = CGI.escapeHTML(post.title)
28
+ FeedPost.new(post_title, post_link, html_body, post.date.iso8601, post.tags)
28
29
  end
29
30
 
30
31
  path = File.join(__dir__, "templates/feed.erb")
@@ -1,3 +1,3 @@
1
1
  module Postwave
2
- VERSION = "0.1.2"
2
+ VERSION = "0.1.4"
3
3
  end
data/postwave.gemspec CHANGED
@@ -19,7 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
  spec.add_dependency "redcarpet", '~> 3.6'
22
+ spec.add_dependency "csv"
23
+ spec.add_dependency "time"
24
+ spec.add_dependency "yaml"
25
+
22
26
 
23
27
  spec.add_development_dependency "bundler"
24
28
  spec.add_development_dependency "rake", "~> 12.3"
29
+ spec.add_development_dependency "rss"
25
30
  end
metadata CHANGED
@@ -1,14 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: postwave
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dave Schwantes
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2025-03-13 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
13
  name: redcarpet
@@ -24,6 +23,48 @@ dependencies:
24
23
  - - "~>"
25
24
  - !ruby/object:Gem::Version
26
25
  version: '3.6'
26
+ - !ruby/object:Gem::Dependency
27
+ name: csv
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: time
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ type: :runtime
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ - !ruby/object:Gem::Dependency
55
+ name: yaml
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - ">="
59
+ - !ruby/object:Gem::Version
60
+ version: '0'
61
+ type: :runtime
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
27
68
  - !ruby/object:Gem::Dependency
28
69
  name: bundler
29
70
  requirement: !ruby/object:Gem::Requirement
@@ -52,6 +93,20 @@ dependencies:
52
93
  - - "~>"
53
94
  - !ruby/object:Gem::Version
54
95
  version: '12.3'
96
+ - !ruby/object:Gem::Dependency
97
+ name: rss
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
55
110
  description: Write your posts statically. Interact with them dynamically.
56
111
  email:
57
112
  - dave.schwantes@gmail.com
@@ -83,7 +138,6 @@ homepage: https://github.com/dorkrawk/postwave
83
138
  licenses:
84
139
  - MIT
85
140
  metadata: {}
86
- post_install_message:
87
141
  rdoc_options: []
88
142
  require_paths:
89
143
  - lib
@@ -98,8 +152,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
98
152
  - !ruby/object:Gem::Version
99
153
  version: '0'
100
154
  requirements: []
101
- rubygems_version: 3.2.22
102
- signing_key:
155
+ rubygems_version: 3.7.1
103
156
  specification_version: 4
104
157
  summary: An opinionated flatfile based blog engine.
105
158
  test_files: []