prt 0.3.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.
Files changed (53) hide show
  1. checksums.yaml +7 -0
  2. data/.githooks/pre-commit +17 -0
  3. data/.github/workflows/ci.yml +29 -0
  4. data/.gitignore +24 -0
  5. data/.rspec +2 -0
  6. data/.rubocop.yml +28 -0
  7. data/.ruby-gemset +1 -0
  8. data/.ruby-version +1 -0
  9. data/CLAUDE.md +8 -0
  10. data/Gemfile +13 -0
  11. data/LICENSE.txt +22 -0
  12. data/README.md +340 -0
  13. data/Rakefile +26 -0
  14. data/bin/setup +11 -0
  15. data/exe/parrot +12 -0
  16. data/lib/helpers.rb +19 -0
  17. data/lib/parrot/commands/build.rb +1274 -0
  18. data/lib/parrot/commands/new.rb +25 -0
  19. data/lib/parrot/commands/post.rb +83 -0
  20. data/lib/parrot/commands/serve.rb +129 -0
  21. data/lib/parrot/constants.rb +45 -0
  22. data/lib/parrot/file_cache.rb +45 -0
  23. data/lib/parrot/logger.rb +23 -0
  24. data/lib/parrot/metadata.rb +4 -0
  25. data/lib/parrot/runner.rb +29 -0
  26. data/lib/parrot/template_handler.rb +19 -0
  27. data/lib/parrot.rb +120 -0
  28. data/parrot.gemspec +36 -0
  29. data/skel/.gitignore +3 -0
  30. data/skel/config.yaml +68 -0
  31. data/skel/css/app.scss +189 -0
  32. data/skel/images/.keep +0 -0
  33. data/skel/images/apple-touch-icon.png +0 -0
  34. data/skel/images/favicon.ico +0 -0
  35. data/skel/images/favicon.svg +40 -0
  36. data/skel/images/parrot.jpeg +0 -0
  37. data/skel/javascripts/app.js +37 -0
  38. data/skel/public/.keep +0 -0
  39. data/skel/views/404.md +7 -0
  40. data/skel/views/about.md +17 -0
  41. data/skel/views/layout.html.erb +72 -0
  42. data/skel/views/posts/about_parrot.md +63 -0
  43. data/skel/views/posts/sample.md +37 -0
  44. data/spec/parrot/commands/build_spec.rb +747 -0
  45. data/spec/parrot/commands/new_spec.rb +43 -0
  46. data/spec/parrot/commands/post_spec.rb +80 -0
  47. data/spec/parrot/commands/serve_spec.rb +66 -0
  48. data/spec/parrot/logger_spec.rb +39 -0
  49. data/spec/parrot/metadata_spec.rb +11 -0
  50. data/spec/parrot/runner_spec.rb +28 -0
  51. data/spec/parrot_spec.rb +37 -0
  52. data/spec/spec_helper.rb +7 -0
  53. metadata +237 -0
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parrot::Commands do
4
+ let(:config) { Parrot::Config.new(Dir.pwd, TestLogger) }
5
+
6
+ context 'NewCommand' do
7
+ it 'has a run method' do
8
+ expect(Parrot::Commands::NewCommand.new(%w[foo], config)).to respond_to(:run)
9
+ end
10
+
11
+ context 'Setup a blog' do
12
+ let(:app_name) { 'my-test-blog' }
13
+
14
+ after(:each) do
15
+ FileUtils.rm_rf(app_name)
16
+ end
17
+
18
+ it 'Verify all files from skeleton template is copied correctly' do
19
+ Parrot::Commands::NewCommand.new([app_name], config).run
20
+
21
+ expect(Dir.exist?(app_name)).to be true
22
+
23
+ file_list = [
24
+ "#{app_name}/config.yaml",
25
+ "#{app_name}/css/app.scss",
26
+ "#{app_name}/images/favicon.ico",
27
+ "#{app_name}/images/favicon.svg",
28
+ "#{app_name}/images/apple-touch-icon.png",
29
+ "#{app_name}/images/parrot.jpeg",
30
+ "#{app_name}/javascripts/app.js",
31
+ "#{app_name}/public/.keep",
32
+ "#{app_name}/views/404.md",
33
+ "#{app_name}/views/about.md",
34
+ "#{app_name}/views/layout.html.erb",
35
+ "#{app_name}/views/posts/about_parrot.md",
36
+ "#{app_name}/views/posts/sample.md"
37
+ ]
38
+
39
+ expect(file_list.all? { |file| File.exist?(file) }).to be true
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parrot::Commands do
4
+ let(:config) { Parrot::Config.new(File.expand_path('blog'), TestLogger) }
5
+
6
+ before do
7
+ FileUtils.rm_rf('blog')
8
+ Parrot::Commands::NewCommand.new(%w[blog], config).run
9
+ end
10
+
11
+ after do
12
+ FileUtils.rm_rf('blog')
13
+ end
14
+
15
+ context 'PostCommand' do
16
+ it 'has a run method' do
17
+ expect(Parrot::Commands::PostCommand.new(%w[--title hello], config)).to respond_to(:run)
18
+ end
19
+
20
+ it 'shows the usage example when called without a title' do
21
+ expect { Parrot::Commands::PostCommand.new([], config) }
22
+ .to raise_error(ArgumentError, /parrot post --title "My new post"/)
23
+ end
24
+
25
+ it 'shows the usage example when --title is given no value' do
26
+ expect { Parrot::Commands::PostCommand.new(%w[--title], config) }
27
+ .to raise_error(ArgumentError, /parrot post --title "My new post"/)
28
+ end
29
+
30
+ it 'shows the usage example when the title has no usable characters' do
31
+ expect { Parrot::Commands::PostCommand.new(%w[--title !!!], config).run }
32
+ .to raise_error(ArgumentError, /parrot post --title "My new post"/)
33
+ end
34
+
35
+ it 'accepts the title from --title, including trailing words' do
36
+ Parrot::Commands::PostCommand.new(['--title', 'My', 'First', 'Post!'], config).run
37
+
38
+ post_path = 'blog/views/posts/my-first-post.md'
39
+ expect(File.exist?(post_path)).to be true
40
+
41
+ body = File.read(post_path)
42
+ expect(body).to include("<!--\ntitle: My First Post!\ndate: #{Date.today.strftime('%d/%m/%Y')}\nlang: en\ncategory:\n-->")
43
+ expect(body).to include('# {post_title}')
44
+ # the build adds the date under the title itself now
45
+ expect(body).not_to include('{post_date}')
46
+ end
47
+
48
+ it 'also accepts the title as a plain quoted argument' do
49
+ Parrot::Commands::PostCommand.new(['A plain title'], config).run
50
+ expect(File.exist?('blog/views/posts/a-plain-title.md')).to be true
51
+ end
52
+
53
+ %w[About 404 Index index2 ABOUT Now post Posts note NOTES Category Categories].each do |title|
54
+ it "refuses the reserved title #{title.inspect}" do
55
+ expect { Parrot::Commands::PostCommand.new(['--title', title], config).run }
56
+ .to raise_error(ArgumentError, /is a reserved post name/)
57
+ expect(Dir['blog/views/posts/*.md'].map { |path| File.basename(path) })
58
+ .to contain_exactly('about_parrot.md', 'sample.md')
59
+ end
60
+ end
61
+
62
+ it 'allows titles that only contain a reserved word' do
63
+ Parrot::Commands::PostCommand.new(['--title', 'About me'], config).run
64
+ Parrot::Commands::PostCommand.new(['--title', 'Index 2'], config).run
65
+ Parrot::Commands::PostCommand.new(['--title', 'Notes on Ruby'], config).run
66
+ Parrot::Commands::PostCommand.new(['--title', 'Nowhere'], config).run
67
+ Parrot::Commands::PostCommand.new(['--title', 'Category theory'], config).run
68
+ expect(File.exist?('blog/views/posts/about-me.md')).to be true
69
+ expect(File.exist?('blog/views/posts/index-2.md')).to be true
70
+ expect(File.exist?('blog/views/posts/notes-on-ruby.md')).to be true
71
+ expect(File.exist?('blog/views/posts/nowhere.md')).to be true
72
+ expect(File.exist?('blog/views/posts/category-theory.md')).to be true
73
+ end
74
+
75
+ it 'refuses to overwrite an existing post' do
76
+ Parrot::Commands::PostCommand.new(%w[--title Dup], config).run
77
+ expect { Parrot::Commands::PostCommand.new(%w[--title Dup], config).run }.to raise_error(/already exists/)
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,66 @@
1
+ require 'net/http'
2
+
3
+ describe Parrot::Commands do
4
+ let(:config) { Parrot::Config.new(Dir.pwd, TestLogger) }
5
+
6
+ before do
7
+ # create a new application
8
+ Parrot::Commands::NewCommand.new(%w[blog], config).run
9
+ end
10
+
11
+ after do
12
+ # cleanup
13
+ FileUtils.rm_rf('blog')
14
+ end
15
+
16
+ context 'ServeCommand' do
17
+ it 'has a run method' do
18
+ expect(Parrot::Commands::ServeCommand.new([], config)).to respond_to(:run)
19
+ end
20
+
21
+ it 'run a webserver at port 8000 serving files' do
22
+ # Parrot::Commands::ServeCommand.new([]).run
23
+ end
24
+ end
25
+
26
+ context 'ServeCommand::FileHandler' do
27
+ let(:document_root) { File.expand_path('blog/public') }
28
+
29
+ before do
30
+ FileUtils.mkdir_p(document_root)
31
+ File.write(File.join(document_root, 'index.html'), 'home page')
32
+ @server = WEBrick::HTTPServer.new(Port: 0, Logger: WEBrick::Log.new(File::NULL), AccessLog: [])
33
+ @server.mount('/', Parrot::Commands::ServeCommand::FileHandler, document_root)
34
+ @thread = Thread.new { @server.start }
35
+ @port = @server.config[:Port]
36
+ end
37
+
38
+ after do
39
+ @server.shutdown
40
+ @thread.join
41
+ end
42
+
43
+ def get(path)
44
+ Net::HTTP.get_response(URI("http://127.0.0.1:#{@port}#{path}"))
45
+ end
46
+
47
+ it 'serves existing files' do
48
+ response = get('/index.html')
49
+ expect(response.code).to eq('200')
50
+ expect(response.body).to eq('home page')
51
+ end
52
+
53
+ it 'answers a missing path with the built 404.html and a 404 status' do
54
+ File.write(File.join(document_root, '404.html'), 'custom not found')
55
+ response = get('/missing')
56
+ expect(response.code).to eq('404')
57
+ expect(response.body).to eq('custom not found')
58
+ end
59
+
60
+ it "falls back to WEBrick's error page when no 404.html was built" do
61
+ response = get('/missing')
62
+ expect(response.code).to eq('404')
63
+ expect(response.body).to include('Not Found')
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parrot::ParrotLoggerBuilder do
4
+ let(:parrot_logger) { Parrot::ParrotLoggerBuilder.new(false) }
5
+
6
+ it 'is an instance of Logger' do
7
+ expect(parrot_logger.logger).to be_an_instance_of(Logger)
8
+ end
9
+
10
+ it 'has a log method' do
11
+ expect(parrot_logger).to respond_to(:log)
12
+ end
13
+
14
+ it 'logs a message to stdout by default' do
15
+ expect(parrot_logger).to receive(:log).with('Hello parrot')
16
+ parrot_logger.log('Hello parrot')
17
+ end
18
+
19
+ it 'sets the instance variable @logger' do
20
+ expect(parrot_logger.instance_variable_defined?(:@logger)).to be true
21
+ end
22
+
23
+ it 'sets the instance variable @device' do
24
+ expect(parrot_logger.instance_variable_defined?(:@device)).to be true
25
+ end
26
+
27
+ it 'sets the log device as STDOUT by default' do
28
+ allow(Parrot::Helpers).to receive(:testing?).and_return(false)
29
+ expect(parrot_logger.device).to eq($stdout)
30
+ end
31
+
32
+ it 'In test mode the device will be set to a test log file' do
33
+ expect(File.basename(parrot_logger.device.path)).to eq('parrot.test.log')
34
+ end
35
+
36
+ it 'sets the log device as a file if the quiet mode is turned on' do
37
+ expect(Parrot::ParrotLoggerBuilder.new(true).device).to be_a File
38
+ end
39
+ end
@@ -0,0 +1,11 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Metadata' do
4
+ it 'should have the right version string' do
5
+ expect(Parrot::VERSION).to eq '0.3.0'
6
+ end
7
+
8
+ it 'should have the HOMEPAGE set' do
9
+ expect(Parrot::HOMEPAGE).not_to be_empty
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parrot::Runner do
4
+ let(:config) { Parrot::Config.new(Dir.pwd, TestLogger) }
5
+
6
+ context 'Runner class' do
7
+ it 'should raise error if no application name provided for the new sub command' do
8
+ expect { Parrot::Runner.new('new', [], config) }.to raise_error(ArgumentError)
9
+ end
10
+
11
+ it 'should create a NewCommand object' do
12
+ runner = Parrot::Runner.new('new', %w[foo], config)
13
+ expect(runner.command).to be_an_instance_of(Parrot::Commands::NewCommand)
14
+ end
15
+
16
+ it 'should create a BuildCommand object' do
17
+ expect(Parrot::Runner.new('build', %w[foo], config).command).to be_an_instance_of(Parrot::Commands::BuildCommand)
18
+ end
19
+
20
+ it 'should create a ServeCommand object' do
21
+ expect(Parrot::Runner.new('serve', %w[foo], config).command).to be_an_instance_of(Parrot::Commands::ServeCommand)
22
+ end
23
+
24
+ it 'should raise error if the command is invalid' do
25
+ expect { Parrot::Runner.new('foo', config) }.to raise_error(NameError)
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ describe Parrot do
4
+ context 'with option -v' do
5
+ let(:args) { %w[-v] }
6
+
7
+ it 'displays version info' do
8
+ expect { Parrot::Parrot.new(args).run }.to output("Parrot: #{Parrot::VERSION}\n").to_stdout
9
+ end
10
+ end
11
+
12
+ context 'with option -h' do
13
+ let(:args) { %w[-h] }
14
+ it 'displays help message' do
15
+ expect { Parrot::Parrot.new(args).run }.to output(/Sub Commands:/).to_stdout
16
+ end
17
+ end
18
+
19
+ context 'in quiet mode' do
20
+ it 'will be quiet by default while testing' do
21
+ parrot = Parrot::Parrot.new
22
+ expect(parrot).to be_quiet
23
+ end
24
+
25
+ it 'will be be quiet on quiet option' do
26
+ args = %w[-q]
27
+ parrot = Parrot::Parrot.new(args)
28
+ expect(parrot).to be_quiet
29
+ end
30
+ end
31
+
32
+ context 'sub commands' do
33
+ it 'has the following commands' do
34
+ expect(Parrot::Parrot::SUB_COMMANDS).to eq(Parrot::SUB_COMMANDS_DOC.keys.map(&:to_s))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ require 'pry'
2
+ require 'debug'
3
+ require_relative '../lib/parrot'
4
+
5
+ ENV['PARROT_TESTING'] = 'true'
6
+
7
+ TestLogger = Parrot::ParrotLoggerBuilder.new(false).logger
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: prt
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.3.0
5
+ platform: ruby
6
+ authors:
7
+ - Deepak Kumar
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: kramdown
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
+ - !ruby/object:Gem::Dependency
27
+ name: kramdown-parser-gfm
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: logger
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: nokogiri
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'
68
+ - !ruby/object:Gem::Dependency
69
+ name: observer
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ type: :runtime
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ - !ruby/object:Gem::Dependency
83
+ name: rouge
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ type: :runtime
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ - !ruby/object:Gem::Dependency
97
+ name: sassc
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ type: :runtime
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: tilt
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ type: :runtime
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ - !ruby/object:Gem::Dependency
125
+ name: watchr
126
+ requirement: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - ">="
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ type: :runtime
132
+ prerelease: false
133
+ version_requirements: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ - !ruby/object:Gem::Dependency
139
+ name: webrick
140
+ requirement: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: '0'
145
+ type: :runtime
146
+ prerelease: false
147
+ version_requirements: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ description: A static blogging tool written in ruby, posts are in markdown format
153
+ email:
154
+ - deepakkumarnd@gmail.com
155
+ executables:
156
+ - parrot
157
+ extensions: []
158
+ extra_rdoc_files: []
159
+ files:
160
+ - ".githooks/pre-commit"
161
+ - ".github/workflows/ci.yml"
162
+ - ".gitignore"
163
+ - ".rspec"
164
+ - ".rubocop.yml"
165
+ - ".ruby-gemset"
166
+ - ".ruby-version"
167
+ - CLAUDE.md
168
+ - Gemfile
169
+ - LICENSE.txt
170
+ - README.md
171
+ - Rakefile
172
+ - bin/setup
173
+ - exe/parrot
174
+ - lib/helpers.rb
175
+ - lib/parrot.rb
176
+ - lib/parrot/commands/build.rb
177
+ - lib/parrot/commands/new.rb
178
+ - lib/parrot/commands/post.rb
179
+ - lib/parrot/commands/serve.rb
180
+ - lib/parrot/constants.rb
181
+ - lib/parrot/file_cache.rb
182
+ - lib/parrot/logger.rb
183
+ - lib/parrot/metadata.rb
184
+ - lib/parrot/runner.rb
185
+ - lib/parrot/template_handler.rb
186
+ - parrot.gemspec
187
+ - skel/.gitignore
188
+ - skel/config.yaml
189
+ - skel/css/app.scss
190
+ - skel/images/.keep
191
+ - skel/images/apple-touch-icon.png
192
+ - skel/images/favicon.ico
193
+ - skel/images/favicon.svg
194
+ - skel/images/parrot.jpeg
195
+ - skel/javascripts/app.js
196
+ - skel/public/.keep
197
+ - skel/views/404.md
198
+ - skel/views/about.md
199
+ - skel/views/layout.html.erb
200
+ - skel/views/posts/about_parrot.md
201
+ - skel/views/posts/sample.md
202
+ - spec/parrot/commands/build_spec.rb
203
+ - spec/parrot/commands/new_spec.rb
204
+ - spec/parrot/commands/post_spec.rb
205
+ - spec/parrot/commands/serve_spec.rb
206
+ - spec/parrot/logger_spec.rb
207
+ - spec/parrot/metadata_spec.rb
208
+ - spec/parrot/runner_spec.rb
209
+ - spec/parrot_spec.rb
210
+ - spec/spec_helper.rb
211
+ homepage: https://github.com/deepakkumarnd/parrot
212
+ licenses:
213
+ - MIT
214
+ metadata:
215
+ rubygems_mfa_required: 'true'
216
+ post_install_message: 'Refer to readme at README.md to know how to get started with
217
+ Parrot
218
+
219
+ '
220
+ rdoc_options: []
221
+ require_paths:
222
+ - lib
223
+ required_ruby_version: !ruby/object:Gem::Requirement
224
+ requirements:
225
+ - - ">="
226
+ - !ruby/object:Gem::Version
227
+ version: '3.2'
228
+ required_rubygems_version: !ruby/object:Gem::Requirement
229
+ requirements:
230
+ - - ">="
231
+ - !ruby/object:Gem::Version
232
+ version: '0'
233
+ requirements: []
234
+ rubygems_version: 4.0.16
235
+ specification_version: 4
236
+ summary: A static markdown blog builder written in ruby for minimalist bloggers
237
+ test_files: []