blog-tools 0.1.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.
- checksums.yaml +7 -0
- data/bin/blog-tools +6 -0
- data/lib/blog-tools/cli.rb +31 -0
- data/lib/blog-tools/commands/config.rb +14 -0
- data/lib/blog-tools/commands/generate.rb +43 -0
- data/lib/blog-tools/commands/lists.rb +132 -0
- data/lib/blog-tools/storage.rb +86 -0
- data/lib/blog_tools.rb +3 -0
- metadata +60 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 6162eaac9b16ffe613140a0c3153a05d195f7e9fea588437d460c7dfa8cbc30e
|
|
4
|
+
data.tar.gz: 354be29df9a617cb1c1e4cf4ef0428d5085048c25d977dd310e0f560d132e6cc
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a8c2914c2d4328faf4bea4234a47c7257170a90a21dd85b788a638068c3152546b5b62d24397d046effa0d7cee5f346f8b8922f53025aefd9b9e131a7bae8a77
|
|
7
|
+
data.tar.gz: 5b891802c32e5ec9ced9e069fd5f81b6f3f37a4b3bd13f0c6374845fec440dabdfa095b046d806705772a5923e8296ea7a298e83afe755d6106c40184300a32a
|
data/bin/blog-tools
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'thor'
|
|
4
|
+
|
|
5
|
+
require_relative 'commands/generate'
|
|
6
|
+
require_relative 'commands/lists'
|
|
7
|
+
require_relative 'commands/config'
|
|
8
|
+
|
|
9
|
+
require_relative 'storage'
|
|
10
|
+
|
|
11
|
+
# BlogTools is the main namespace for the blog-tools CLI application
|
|
12
|
+
# It contains submodules for command logic, file storage, and CLI Interfaces
|
|
13
|
+
module BlogTools
|
|
14
|
+
# CLI handles all top-level commands for the blog-tools application
|
|
15
|
+
# It delegates to subcommands
|
|
16
|
+
class CLI < Thor
|
|
17
|
+
def initialize(*args)
|
|
18
|
+
super
|
|
19
|
+
Storage.setup!
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
desc 'generate', 'Create a new post'
|
|
23
|
+
subcommand 'generate', Commands::Generate
|
|
24
|
+
|
|
25
|
+
desc 'lists', 'Lists tools'
|
|
26
|
+
subcommand 'lists', Commands::Lists
|
|
27
|
+
|
|
28
|
+
desc 'config', 'Configure blog-tools'
|
|
29
|
+
subcommand 'config', Commands::Config
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module BlogTools
|
|
4
|
+
module Commands
|
|
5
|
+
# Config deals with all commands that configure how blog-tools works
|
|
6
|
+
class Config < Thor
|
|
7
|
+
desc 'TITLE', 'Configure blog-tools'
|
|
8
|
+
|
|
9
|
+
def default(test)
|
|
10
|
+
puts test
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'erb'
|
|
4
|
+
require 'date'
|
|
5
|
+
require_relative '../storage'
|
|
6
|
+
|
|
7
|
+
module BlogTools
|
|
8
|
+
module Commands
|
|
9
|
+
# Generate handles all of the subcommands related to generating posts
|
|
10
|
+
class Generate < Thor
|
|
11
|
+
desc 'post TITLE', 'Generate a new blog post'
|
|
12
|
+
method_option :template, type: :string, desc: 'Specify a template file'
|
|
13
|
+
method_option :tags, type: :array, desc: 'Specify tags (space separated)'
|
|
14
|
+
method_option :author, type: :string, desc: 'Specify author'
|
|
15
|
+
method_option :output, type: :string, desc: 'Output path or filename (default: title.md)'
|
|
16
|
+
method_option :content, type: :string, desc: 'Path to content of post'
|
|
17
|
+
def post(title)
|
|
18
|
+
config = File.exist?(Storage::CONFIG_FILE) ? YAML.load_file(Storage::CONFIG_FILE) : {}
|
|
19
|
+
|
|
20
|
+
template_file = options[:template] || config['default_template'] || 'post.md'
|
|
21
|
+
template_path = File.expand_path(File.join(Storage::TEMPLATES_DIR + template_file))
|
|
22
|
+
|
|
23
|
+
return puts("[!] Template file not found: #{template_path}") unless File.exist?(template_path)
|
|
24
|
+
|
|
25
|
+
template = File.read(template_path)
|
|
26
|
+
renderer = ERB.new(template)
|
|
27
|
+
|
|
28
|
+
result = renderer.result_with_hash(
|
|
29
|
+
title: title,
|
|
30
|
+
date: Date.today.to_s,
|
|
31
|
+
author: options[:author] || config['author'] || ENV['USER'] || 'unknown',
|
|
32
|
+
tags: options[:tags] || config['tags'] || [],
|
|
33
|
+
content: options[:content] ? File.read(File.expand_path(options[:content])) : ''
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
output_filename = options[:output] || "#{title.downcase.strip.gsub(/\s+/, '_')}.md"
|
|
37
|
+
File.write(output_filename, result)
|
|
38
|
+
|
|
39
|
+
puts "[✓] Post generated at #{output_filename}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../storage'
|
|
4
|
+
|
|
5
|
+
module BlogTools
|
|
6
|
+
module Commands
|
|
7
|
+
# List handles all of the subcommands dealing with makes, editing, and
|
|
8
|
+
# creating lists of blog post ideas
|
|
9
|
+
#
|
|
10
|
+
# TODO option to add file path to the post
|
|
11
|
+
class Lists < Thor
|
|
12
|
+
def initialize(*args)
|
|
13
|
+
super
|
|
14
|
+
@lists = Storage.read_lists || {}
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
desc 'list LIST', 'View all posts in a list'
|
|
18
|
+
method_option :completed, type: :boolean, default: false, desc: 'Show only completed posts'
|
|
19
|
+
method_option :in_progress, type: :boolean, default: false, desc: 'show only in-progress posts'
|
|
20
|
+
method_option :status, type: :boolean, default: false, desc: 'Show post status as well'
|
|
21
|
+
def list(list)
|
|
22
|
+
return puts('[!] List not found') unless @lists[list]
|
|
23
|
+
|
|
24
|
+
puts list.upcase
|
|
25
|
+
@lists[list][:posts].each do |item, data|
|
|
26
|
+
next if options[:completed] && !data[:completed]
|
|
27
|
+
next if options[:in_progress] && !data[:in_progress]
|
|
28
|
+
|
|
29
|
+
if options[:status]
|
|
30
|
+
status =
|
|
31
|
+
if data[:completed]
|
|
32
|
+
'[✓]'
|
|
33
|
+
elsif data[:in_progress]
|
|
34
|
+
'[~]'
|
|
35
|
+
else
|
|
36
|
+
'[ ]'
|
|
37
|
+
end
|
|
38
|
+
puts "- #{status} #{item}"
|
|
39
|
+
else
|
|
40
|
+
puts "- #{item}"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
desc 'create NAME', 'Create a list'
|
|
46
|
+
def create(name)
|
|
47
|
+
@lists[name] = { posts: {} }
|
|
48
|
+
Storage.write_lists(@lists)
|
|
49
|
+
puts "[✓] Created list: #{name}"
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
desc 'add LIST POST', 'Add a post to a list'
|
|
53
|
+
def add(list, post_name)
|
|
54
|
+
return puts('[!] List not found') unless @lists[list]
|
|
55
|
+
|
|
56
|
+
@lists[list][:posts][post_name] = { completed: false, in_progress: false }
|
|
57
|
+
puts "[✓] Added #{post_name} to #{list} list"
|
|
58
|
+
Storage.write_lists(@lists)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
desc 'delete LIST', 'Delete a list'
|
|
62
|
+
def delete(list)
|
|
63
|
+
return puts('[!] List not found') unless @lists[list]
|
|
64
|
+
|
|
65
|
+
puts "[?] Are you sure you want to delete the '#{list}' list?"
|
|
66
|
+
print "[?] This action cannot be undone. Proceed? (y/N)\n> "
|
|
67
|
+
input = $stdin.gets.chomp.strip
|
|
68
|
+
|
|
69
|
+
case input.downcase
|
|
70
|
+
when 'y'
|
|
71
|
+
@lists.delete(list)
|
|
72
|
+
Storage.write_lists(@lists)
|
|
73
|
+
puts "[✓] Deleted '#{list}' list"
|
|
74
|
+
when 'n', ''
|
|
75
|
+
puts '[i] Cancelled.'
|
|
76
|
+
else
|
|
77
|
+
puts '[!] Invalid input. Not deleting.'
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
desc 'remove LIST POST', 'Remove a post from a list'
|
|
82
|
+
def remove(list, post_name)
|
|
83
|
+
return puts('[!] List not found') unless @lists[list]
|
|
84
|
+
|
|
85
|
+
puts "[?] Are you sure you want to delete the post '#{post_name}'?"
|
|
86
|
+
print "[?] This action cannot be undone. Proceed? (y/N)\n> "
|
|
87
|
+
input = $stdin.gets.chomp.strip
|
|
88
|
+
|
|
89
|
+
case input.downcase
|
|
90
|
+
when 'y'
|
|
91
|
+
@lists[list][:posts].delete(post_name)
|
|
92
|
+
Storage.write_lists(@lists)
|
|
93
|
+
puts "[✓] Deleted '#{post_name}' post"
|
|
94
|
+
when 'n', ''
|
|
95
|
+
puts '[i] Cancelled.'
|
|
96
|
+
else
|
|
97
|
+
puts '[!] Invalid input. Not deleting.'
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
desc 'update LIST POST', 'Update the status of a blog post idea (use --complete or --in_progress)'
|
|
102
|
+
method_option :complete, type: :boolean, default: false, desc: 'Mark as complete'
|
|
103
|
+
method_option :in_progress, type: :boolean, default: false, desc: 'Mark as in progress'
|
|
104
|
+
def update(list, post_name)
|
|
105
|
+
return puts('[!] List not found') unless @lists[list]
|
|
106
|
+
return puts('[!] Post not found') unless @lists[list][:posts].key?(post_name)
|
|
107
|
+
|
|
108
|
+
post = @lists[list][:posts][post_name]
|
|
109
|
+
|
|
110
|
+
if options[:complete]
|
|
111
|
+
if post[:completed]
|
|
112
|
+
puts('[!] Post already marked as complete')
|
|
113
|
+
else
|
|
114
|
+
post[:completed] = true
|
|
115
|
+
puts("[✓] Marked '#{post_name}' as complete")
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
if options[:in_progress]
|
|
120
|
+
if post[:in_progress]
|
|
121
|
+
puts('[!] Post already marked as in progress')
|
|
122
|
+
else
|
|
123
|
+
post[:in_progress] = true
|
|
124
|
+
puts("[✓] Marked '#{post_name}' as in progress")
|
|
125
|
+
end
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
Storage.write_lists(@lists)
|
|
129
|
+
end
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
end
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'fileutils'
|
|
4
|
+
require 'yaml'
|
|
5
|
+
|
|
6
|
+
module BlogTools
|
|
7
|
+
# Handles paths and file initialization for blog-tools
|
|
8
|
+
# configuration and templates.
|
|
9
|
+
module Storage
|
|
10
|
+
CONFIG_DIR = ENV['BLOG_TOOLS_DIR'] || File.join(Dir.home, '.config', 'blog-tools')
|
|
11
|
+
LISTS_FILE = File.join(CONFIG_DIR, 'lists.yml')
|
|
12
|
+
CONFIG_FILE = File.join(CONFIG_DIR, 'config.yml')
|
|
13
|
+
TEMPLATES_DIR = File.join(CONFIG_DIR, 'templates/')
|
|
14
|
+
DEFAULT_TEMPLATE_FILE = File.join(TEMPLATES_DIR, 'post.md')
|
|
15
|
+
|
|
16
|
+
# Ensure required directories and files exist.
|
|
17
|
+
def self.setup!
|
|
18
|
+
create_directories
|
|
19
|
+
create_lists_file
|
|
20
|
+
create_config_file
|
|
21
|
+
create_default_template
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Path to the config file
|
|
25
|
+
def self.config_path
|
|
26
|
+
CONFIG_FILE
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.create_directories
|
|
30
|
+
FileUtils.mkdir_p(CONFIG_DIR)
|
|
31
|
+
FileUtils.mkdir_p(TEMPLATES_DIR)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def self.create_lists_file
|
|
35
|
+
FileUtils.touch(LISTS_FILE) unless File.exist?(LISTS_FILE)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.write_lists(data)
|
|
39
|
+
File.write(LISTS_FILE, data.to_yaml)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.read_lists
|
|
43
|
+
return {} unless File.exist?(LISTS_FILE)
|
|
44
|
+
|
|
45
|
+
data = YAML.safe_load_file(LISTS_FILE, permitted_classes: [Symbol, Date, Time], aliases: true)
|
|
46
|
+
data.is_a?(Hash) ? data : {}
|
|
47
|
+
rescue Psych::SyntaxError => e
|
|
48
|
+
warn "[!] Failed to parse lists.yml: #{e.message}"
|
|
49
|
+
{}
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def self.create_config_file
|
|
53
|
+
return if File.exist?(CONFIG_FILE)
|
|
54
|
+
|
|
55
|
+
puts '[!] No configuration file found, generating now...'
|
|
56
|
+
|
|
57
|
+
default_config = {
|
|
58
|
+
'author' => ENV['USER'] || 'changeme',
|
|
59
|
+
'default_template' => 'post.md',
|
|
60
|
+
'tags' => ['blog']
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
File.write(CONFIG_FILE, default_config.to_yaml)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def self.create_default_template
|
|
67
|
+
return unless Dir.empty?(TEMPLATES_DIR)
|
|
68
|
+
|
|
69
|
+
default_template = <<~MARKDOWN
|
|
70
|
+
---
|
|
71
|
+
title: "<%= title %>"
|
|
72
|
+
date: <%= date %>
|
|
73
|
+
author: <%= author %>
|
|
74
|
+
tags: [<%= tags.map { |t| ""#{t}"" }.join(", ") %>]
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
# <%= title %>
|
|
78
|
+
|
|
79
|
+
<%= content %>
|
|
80
|
+
MARKDOWN
|
|
81
|
+
|
|
82
|
+
File.write(DEFAULT_TEMPLATE_FILE, default_template)
|
|
83
|
+
puts "[+] Created default template: #{DEFAULT_TEMPLATE_FILE}"
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
end
|
data/lib/blog_tools.rb
ADDED
metadata
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: blog-tools
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Slavetomints
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: thor
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - ">="
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0'
|
|
26
|
+
email:
|
|
27
|
+
- slavetomints@protonmail.com
|
|
28
|
+
executables:
|
|
29
|
+
- blog-tools
|
|
30
|
+
extensions: []
|
|
31
|
+
extra_rdoc_files: []
|
|
32
|
+
files:
|
|
33
|
+
- bin/blog-tools
|
|
34
|
+
- lib/blog-tools/cli.rb
|
|
35
|
+
- lib/blog-tools/commands/config.rb
|
|
36
|
+
- lib/blog-tools/commands/generate.rb
|
|
37
|
+
- lib/blog-tools/commands/lists.rb
|
|
38
|
+
- lib/blog-tools/storage.rb
|
|
39
|
+
- lib/blog_tools.rb
|
|
40
|
+
licenses: []
|
|
41
|
+
metadata:
|
|
42
|
+
rubygems_mfa_required: 'true'
|
|
43
|
+
rdoc_options: []
|
|
44
|
+
require_paths:
|
|
45
|
+
- lib
|
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
47
|
+
requirements:
|
|
48
|
+
- - ">="
|
|
49
|
+
- !ruby/object:Gem::Version
|
|
50
|
+
version: 3.4.0
|
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
52
|
+
requirements:
|
|
53
|
+
- - ">="
|
|
54
|
+
- !ruby/object:Gem::Version
|
|
55
|
+
version: '0'
|
|
56
|
+
requirements: []
|
|
57
|
+
rubygems_version: 3.6.7
|
|
58
|
+
specification_version: 4
|
|
59
|
+
summary: CLI tools for managing blog posts
|
|
60
|
+
test_files: []
|