postwave-client 0.0.1

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: dcf237beec10b8cff85f7ca70cb4f63a23b43555c70a0fa00d20fbbf01abc7d2
4
+ data.tar.gz: 6400cff762bb34f29084d3ca21de96c97e395239e79592bc53c476ac636b46cf
5
+ SHA512:
6
+ metadata.gz: 2c4611af08d26282c042f9695a7291d07ab1eb103fadf082e6a505f63297c20411848c9dba7669c9a63d7beeb15f5ef5ed0b52c83a0b6bbd72ce7a0cbd8fe5ba
7
+ data.tar.gz: 7314600e6fea02aac7cfb9359f8ac82d254efc54f5bf302a278e1dd6f5cf358d2d2a233371a455c45472501a6699a62b78ba67c66acb8d2a66884f4cc2113e0b
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.gem
data/README.md ADDED
@@ -0,0 +1,95 @@
1
+ # Postwave Ruby Client 🌊
2
+
3
+ A Ruby client for displaying [Postwave](https://github.com/dorkrawk/postwave) blogs.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ gem install postwave-client
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ### Create a Postwave Client
14
+ ```ruby
15
+ postwave_client = Postwave::Client.new("path/to/config/postwave.yaml")
16
+ ```
17
+
18
+ If you'd like to preload all the posts:
19
+ ```ruby
20
+ postwave_client = Postwave::Client.new("path/to/config/postwave.yaml", preload: true)
21
+ ```
22
+
23
+ ### Get a Single Post
24
+
25
+ Pass in the stub (the filename without '.md') for the post.
26
+ ```ruby
27
+ post = postwave_client.post("my-great-post")
28
+
29
+ # <Postwave::Post title="My Great Post", date=<Time ...>, tags=["tag1"], body="bla bla bla..">
30
+
31
+ puts post.title
32
+ # "My Great Post"
33
+ ```
34
+
35
+ ### Get a Collection of Posts
36
+
37
+ This will give you a list of posts for displaying on a page.
38
+
39
+ You can also filter by tags and specify offsets and limits (useful for pagination).
40
+
41
+ ```ruby
42
+ posts = postwave_client.posts
43
+
44
+ # [<Postwave::Post ...>, <Postwave::Post ...>, ...]
45
+
46
+ tagged_posts = postwave_client.posts(tag: "lizards")
47
+
48
+ page2_posts = postwave_client.posts(offset: 10, limit: 10)
49
+ ```
50
+ Posts will be in reverse chronological order.
51
+
52
+ ### Get an Index of Posts
53
+
54
+ This will give you a quick list of post summaries containing the title, date, and stub, useful for building an archive page or quick index of posts.
55
+
56
+ You can also specify offsets and limits (useful for pagination).
57
+ ```ruby
58
+ index = postwave_client(index)
59
+
60
+ # [<Postwave::PostStub title="My Great Post", date=<Time ...>, stub="my-great-post">, <Postwave::PostStub ...>, ...]
61
+
62
+ puts index.first.stub
63
+ # my-great-post
64
+
65
+ page2_index = postwave_client.index(offset: 10, limit: 10)
66
+ ```
67
+ Index will be in reverse chronological order.
68
+
69
+ ### Get Tags Used In the Blog
70
+
71
+ ```ruby
72
+ tags = postwave_client.tags
73
+
74
+ # ["tag1", "tag2", "another-tag"]
75
+ ```
76
+
77
+ ### Get Details For A Tag
78
+
79
+ ```ruby
80
+ tag = postwave_clinet.tag("tag1")
81
+
82
+ # <Postwave::Tag tile="tag1", count=1, post_slugs=["my-great-post"]>
83
+ ```
84
+
85
+ ## Want To Know More About Postwave?
86
+
87
+ Check out the [Postwave](https://github.com/dorkrawk/postwave) repo.
88
+
89
+ ## Development
90
+
91
+ ### Run the Tests!
92
+
93
+ ```
94
+ rake test
95
+ ```
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "rake/testtask"
2
+
3
+ Rake::TestTask.new do |t|
4
+ t.libs << "spec"
5
+ t.test_files = FileList["spec/**/*_spec.rb"]
6
+ t.verbose = true
7
+ end
8
+
9
+ # rake test
10
+ task :default => :test
@@ -0,0 +1,40 @@
1
+ module Postwave
2
+ module BlogUtilities
3
+ CONFIG_FILE_NAME = "postwave.yaml"
4
+ INDEX_FILE_NAME = "index.csv"
5
+ SUMMARY_FILE_NAME = "summary.yaml"
6
+ POSTS_DIR = "_posts"
7
+ META_DIR = "meta"
8
+ TAGS_DIR = "tags"
9
+
10
+ def is_set_up?(blog_root)
11
+ missing_paths = find_missing_paths(blog_root)
12
+ missing_paths.empty?
13
+ end
14
+
15
+ def file_paths(blog_root)
16
+ [
17
+ File.join(blog_root, CONFIG_FILE_NAME),
18
+ File.join(blog_root, POSTS_DIR, META_DIR, INDEX_FILE_NAME),
19
+ File.join(blog_root, POSTS_DIR, META_DIR, SUMMARY_FILE_NAME),
20
+ ]
21
+ end
22
+
23
+ def directory_paths(blog_root)
24
+ [
25
+ File.join(blog_root, POSTS_DIR),
26
+ File.join(blog_root, POSTS_DIR, META_DIR),
27
+ File.join(blog_root, POSTS_DIR, META_DIR, TAGS_DIR),
28
+ ]
29
+ end
30
+
31
+ def find_missing_paths(blog_root)
32
+ paths_to_check = directory_paths(blog_root) + file_paths(blog_root)
33
+ missing_paths = []
34
+ paths_to_check.each do |path|
35
+ missing_paths << path if !FileTest.exist?(path)
36
+ end
37
+ missing_paths
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,92 @@
1
+ require_relative "post"
2
+ require_relative "errors"
3
+ require_relative "blog_utilities"
4
+
5
+ require "csv"
6
+ require "time"
7
+ require "yaml"
8
+
9
+ module Postwave
10
+ class Client
11
+ include BlogUtilities
12
+
13
+ def initialize(config_path, preload: false)
14
+ raise MissingConfigError unless is_valid_config?(config_path)
15
+
16
+ @blog_root = File.dirname(config_path)
17
+ raise InvalidBlogError unless is_set_up?(@blog_root)
18
+
19
+ @all_posts = get_all_posts if preload
20
+ end
21
+
22
+ # returns: an array of PostStub Structs - [<struct PostStub date=Time, title="", stub="">]
23
+ def index(offset: 0, limit: nil)
24
+ working_index = @full_index || get_full_index
25
+ count = limit || working_index.size
26
+ working_index[offset, count]
27
+ end
28
+
29
+ # returns: a post - Postwave::Post
30
+ def post(slug)
31
+ post_file_path = Dir["#{File.join(@blog_root, POSTS_DIR)}/*#{slug}.md"].first
32
+
33
+ raise PostNotFoundError unless post_file_path && File.exist?(post_file_path)
34
+
35
+ Postwave::Post.new_from_file_path(post_file_path)
36
+ end
37
+
38
+ # returns: an array of posts - [Postwave::Post]
39
+ def posts(offset: 0, limit: nil, tag: nil)
40
+ posts = @all_posts || get_all_posts
41
+ posts = posts.select { |post| post.tags.include?(tag) } if tag
42
+ count = limit || posts.size
43
+ posts[offset, count]
44
+ end
45
+
46
+ # returns: an array of tags - [String]
47
+ def tags
48
+ summary = @blog_summary || get_summary
49
+ summary[:tags]
50
+ end
51
+
52
+ # returns: a Tag Struct - <Tag tag: "", count: Integer, post_slugs: ["post-slug",..]
53
+ def tag(tag)
54
+ tag_file_path = File.join(@blog_root, POSTS_DIR, META_DIR, TAGS_DIR, "#{tag}.yaml")
55
+ raise TagNotFoundError unless File.exist?(tag_file_path)
56
+
57
+ tag_info = YAML.load_file(tag_file_path)
58
+
59
+ Postwave::Tag.new(tag, tag_info[:count], tag_info[:post_slugs])
60
+ end
61
+
62
+ private
63
+
64
+ def is_valid_config?(config_path)
65
+ File.exist?(config_path)
66
+ end
67
+
68
+ def get_all_posts
69
+ posts = []
70
+ Dir.glob(File.join(@blog_root, POSTS_DIR, "*.md")) do |post_file_path|
71
+ posts << Postwave::Post.new_from_file_path(post_file_path)
72
+ end
73
+ posts.reject! { |p| p.draft if p.respond_to? :draft }
74
+ posts.sort_by { |p| p.date }.reverse
75
+ end
76
+
77
+ def get_full_index
78
+ full_index = []
79
+ index_contents = CSV.read(File.join(@blog_root, POSTS_DIR, META_DIR, INDEX_FILE_NAME))
80
+ index_contents.shift # skip header
81
+ index_contents.each do |post|
82
+ full_index << Postwave::PostStub.new(Time.parse(post[1]), post[2], post[0])
83
+ end
84
+ full_index
85
+ end
86
+
87
+ def get_summary
88
+ summary_file_path = File.join(@blog_root, POSTS_DIR, META_DIR, SUMMARY_FILE_NAME)
89
+ YAML.load_file(summary_file_path)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,16 @@
1
+ module Postwave
2
+ class PostwaveError < StandardError
3
+ end
4
+
5
+ class MissingConfigError < PostwaveError
6
+ end
7
+
8
+ class InvalidBlogError < PostwaveError
9
+ end
10
+
11
+ class PostNotFoundError < PostwaveError
12
+ end
13
+
14
+ class TagNotFoundError < PostwaveError
15
+ end
16
+ end
@@ -0,0 +1,67 @@
1
+ require_relative "blog_utilities"
2
+
3
+ module Postwave
4
+ class Post
5
+ include BlogUtilities
6
+
7
+ MEATADATA_DELIMTER = "---"
8
+
9
+ attr_reader :file_name, :slug
10
+
11
+ def self.new_from_file_path(path)
12
+ metadata_delimter_count = 0
13
+ body_buffer_count = 0
14
+ field_content = { "body" => "" }
15
+
16
+ File.readlines(path).each do |line|
17
+ clean_line = line.chomp
18
+ if clean_line == MEATADATA_DELIMTER
19
+ metadata_delimter_count += 1
20
+ next
21
+ end
22
+
23
+ if metadata_delimter_count == 0
24
+ next
25
+ elsif metadata_delimter_count == 1
26
+ field, value = clean_line.split(":", 2).map(&:strip)
27
+ field_content[field] = value
28
+ else
29
+ if body_buffer_count == 0
30
+ body_buffer_count += 1
31
+ next if clean_line.empty?
32
+ end
33
+
34
+ field_content["body"] += "#{line}\n"
35
+ end
36
+ end
37
+
38
+ # turn "date" into a Time object
39
+ field_content["date"] = Time.parse(field_content["date"])
40
+
41
+ # turn "tags" into an array
42
+ if field_content["tags"]
43
+ field_content["tags"] = field_content["tags"].split(",").map do |tag|
44
+ tag.downcase.strip.gsub(' ', '-').gsub(/[^\w-]/, '')
45
+ end
46
+ end
47
+
48
+ # turn "draft" into boolean
49
+ if field_content["draft"]
50
+ field_content["draft"] = field_content["draft"].downcase == "true"
51
+ end
52
+
53
+ self.new(path, field_content)
54
+ end
55
+
56
+ def initialize(file_name, field_content = {})
57
+ @file_name = file_name
58
+ @slug = file_name.split("/").last[...-3] # cut off ".md"
59
+
60
+ field_content.each do |field, value|
61
+ instance_variable_set("@#{field}", value) unless self.instance_variables.include?("@#{field}".to_sym)
62
+ self.class.send(:attr_reader, field) unless self.public_methods.include?(field.to_sym)
63
+ end
64
+ end
65
+ end
66
+ end
67
+
@@ -0,0 +1,3 @@
1
+ module Postwave
2
+ VERSION = "0.0.1"
3
+ end
data/lib/postwave.rb ADDED
@@ -0,0 +1,6 @@
1
+ require_relative "postwave/client"
2
+
3
+ module Postwave
4
+ PostStub = Struct.new(:date, :title, :slug)
5
+ Tag = Struct.new(:name, :count, :post_slugs)
6
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'postwave/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "postwave-client"
8
+ spec.version = Postwave::VERSION
9
+ spec.authors = ["Dave Schwantes"]
10
+ spec.email = ["dave.schwantes@gmail.com"]
11
+
12
+ spec.summary = "A Ruby client for interacting with Postwave blogs."
13
+ spec.description = "Write your posts statically. Interact with them dynamically."
14
+ spec.homepage = "https://github.com/dorkrawk/postwave-client-ruby"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ # spec.add_dependency ""
21
+
22
+ spec.add_development_dependency "bundler"
23
+ spec.add_development_dependency "rake", "~> 12.3"
24
+ end
metadata ADDED
@@ -0,0 +1,81 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: postwave-client
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dave Schwantes
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2022-09-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.3'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.3'
41
+ description: Write your posts statically. Interact with them dynamically.
42
+ email:
43
+ - dave.schwantes@gmail.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - ".gitignore"
49
+ - README.md
50
+ - Rakefile
51
+ - lib/postwave.rb
52
+ - lib/postwave/blog_utilities.rb
53
+ - lib/postwave/client.rb
54
+ - lib/postwave/errors.rb
55
+ - lib/postwave/post.rb
56
+ - lib/postwave/version.rb
57
+ - postwave-client.gemspec
58
+ homepage: https://github.com/dorkrawk/postwave-client-ruby
59
+ licenses:
60
+ - MIT
61
+ metadata: {}
62
+ post_install_message:
63
+ rdoc_options: []
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ version: '0'
71
+ required_rubygems_version: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ requirements: []
77
+ rubygems_version: 3.2.22
78
+ signing_key:
79
+ specification_version: 4
80
+ summary: A Ruby client for interacting with Postwave blogs.
81
+ test_files: []