raindeer 0.9.1 → 0.9.3

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: d42828c094ed42e854e71771f9ead3dfe12ff79322bf8348fe35eb3c96caec94
4
- data.tar.gz: 2aa21a01220ecad0e959e8edd05e535482bfe4afb3204703ae82460979d0cd89
3
+ metadata.gz: 41aa9563466af4e0a352cb288bf3af4dc340de96baa2417928f0807d745e7f4e
4
+ data.tar.gz: cf4ba9e858b140c6e3893814df02e04acc85a24320cf9e71580d2df5188aa615
5
5
  SHA512:
6
- metadata.gz: 8625736bca1b2f5ef9ff7f139a4b8172989fcd302816a5f3216f8b8c4c44c08f90b33b118c85ce4f6b242711be07a5b75c46bc7b475b3bbc9cb75641b122ea0e
7
- data.tar.gz: b2b025031b825409cbfe418fdf336e5b8747cab209e45d2c935ac0ad5a2028194a3063c9cb48138e0889d3506a174d22d15d76044b8a875ed9711e0ccf700818
6
+ metadata.gz: 90762ce5864de9c7bdd5a69f6d30f5246388745d6bf2c44c85e01b661b48431210d6bc475cfe7b2d83f21de2305053292de4fbbfafc0b8922be5294761b36e5c
7
+ data.tar.gz: c63d7c49fedcbf168731510c4e5d07fdeeac7dfd27c81fdd099516f44a4940a8d700513b9cbfcab56eeea0aa46849774eae036aa1f5585cf4940cdae7a3b6395
data/bin/rain CHANGED
@@ -1,4 +1,5 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
4
  # Call "rain" to access the CLI loaded on your $PATH by Bundler.
4
5
  # Uncomment the line below and call "bin/rain" to load local gems from your Gemfile in development.
data/lib/cli/cli.rb CHANGED
@@ -1,19 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'trees'
4
- require_relative 'static/static'
4
+ require_relative 'static'
5
5
 
6
6
  module Rain
7
7
  module CLI
8
8
  extend Trees
9
9
 
10
+ TEMPLATE_URL = 'https://github.com/raindeer-rb/raindeer-template'
11
+
10
12
  line('new :app_name') do |app_name|
11
- summary { "Generates a Raindeer application with the specified name." }
12
- execute { 'TODO' }
13
+ summary { 'Generates a Raindeer application with the specified name.' }
14
+ execute do
15
+ system("git clone #{TEMPLATE_URL} #{app_name}")
16
+ system("cd #{app_name}")
17
+ end
13
18
  end
14
19
 
15
20
  line('server') do
16
- summary { "Starts a Raindeer application. Once run visit http://127.0.0.1:4133" }
21
+ summary { 'Starts a Raindeer application. Once run visit http://127.0.0.1:4133' }
17
22
  execute { system('bin/server') }
18
23
  end
19
24
 
data/lib/cli/static.rb ADDED
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby-progressbar'
4
+ require 'fileutils'
5
+
6
+ require 'antlers' # Adds antlers support to lowload.
7
+ require 'lowload'
8
+
9
+ require_relative '../pages/pages'
10
+
11
+ module Rain
12
+ module CLI
13
+ module Static
14
+ extend self
15
+
16
+ FakeRequest = Data.define(:path)
17
+ RequestResult = Data.define(:path, :status, :html)
18
+
19
+ def build(application_path:)
20
+ metadata = load_user_application(app_path: File.expand_path('app', application_path))
21
+ build_path = File.expand_path('build', application_path)
22
+
23
+ FileUtils.rm_rf(build_path)
24
+ FileUtils.mkdir_p(build_path)
25
+ FileUtils.cp_r(File.expand_path('public', application_path), File.expand_path('public', build_path))
26
+
27
+ Low::Events::RequestEvent.define do |observers|
28
+ observers << Providers['rain.router']
29
+ end
30
+
31
+ request_results(metadata:).each do |request_result|
32
+ folder_path = File.join(build_path, request_result.path)
33
+
34
+ puts "#{folder_path} => #{request_result.status}"
35
+
36
+ FileUtils.mkdir_p(folder_path)
37
+ File.write(File.expand_path('index.html', folder_path), request_result.html)
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def load_user_application(app_path:)
44
+ metadata = LowLoad.dirload(app_path)
45
+
46
+ if Dir.exist?(File.expand_path('pages', app_path))
47
+ Providers.define('rain.pages', eager: true) do
48
+ require_relative '../pages/pages'
49
+ Rain::Pages.new(metadata:)
50
+ end
51
+ end
52
+
53
+ metadata
54
+ end
55
+
56
+ def request_results(metadata:)
57
+ file_paths = metadata.file_types.values_at('md', 'rd', 'markdown', 'raindown').flat_map { it }.compact
58
+ paths = file_paths.map { |file_path| Rain::Pages.url_path(file_path:) }.compact
59
+ paths << '/'
60
+ # Skip files that became solely metadata due to underscores hiding the entire file path.
61
+ paths.reject! { |url_path| url_path == '' }
62
+
63
+ progress_bar = ProgressBar.create(
64
+ total: paths.size,
65
+ format: "\e[0;34m%a |%B| %p%%\e[0m"
66
+ )
67
+
68
+ paths.map do |path|
69
+ request = FakeRequest.new(path:)
70
+ response = Low::Events::RequestEvent.take(request:).response
71
+ result = RequestResult.new(path:, status: response.status, html: response.read)
72
+ progress_bar.increment
73
+ result
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -8,6 +8,7 @@ module Rain
8
8
  color = next_output ? @config.cell_color : @config.lead_color
9
9
 
10
10
  return Paint[output, '#006ab0'] if @config.leet_keys.invert.key?(output)
11
+
11
12
  output ? Paint[output, color] : Paint[' ']
12
13
  end
13
14
  end
@@ -11,14 +11,14 @@ module Rain
11
11
  @leet_letters = @leet_numbers.invert
12
12
  end
13
13
 
14
- def render(output:, next_output:, x:, y:)
15
- output = filter(output:, next_output:, x:, y:)
14
+ def render(output:, x:, y:, **)
15
+ output = filter(output:, x:, y:, **)
16
16
  save(output:, x:, y:)
17
17
  end
18
18
 
19
19
  private
20
20
 
21
- def filter(output:, next_output:, x:, y:)
21
+ def filter(output:, x:, y:, **)
22
22
  return unless output
23
23
 
24
24
  character = output.downcase
@@ -26,7 +26,7 @@ module Rain
26
26
 
27
27
  return @leet_numbers[character] if @leet_letters.key?(last_character) && rand < 0.99
28
28
  return @leet_numbers[character] if @leet_numbers.key?(character) && rand < 0.005
29
-
29
+
30
30
  output
31
31
  end
32
32
  end
data/lib/matrix/stream.rb CHANGED
@@ -75,8 +75,8 @@ module Rain
75
75
  # └─────┴─────┴─────┘
76
76
  def render(duration: nil)
77
77
  @show_cursor.increment(delays:, inputs:, duration:) do |index|
78
- prev_index = index.zero? ? @inputs.count - 1 : index - 1
79
- next_index = index + 1 >= @inputs.count ? 0 : index + 1
78
+ index.zero? ? @inputs.count - 1 : index - 1
79
+ index + 1 >= @inputs.count ? 0 : index + 1
80
80
 
81
81
  if @inputs[index]
82
82
  @outputs[index] = @inputs[index]
data/lib/pages/pages.rb CHANGED
@@ -11,7 +11,7 @@ module Rain
11
11
  Page = Struct.new(:metadata, :html)
12
12
 
13
13
  def initialize(metadata:)
14
- @file_paths = [*metadata.file_types['md'], *metadata.file_types['markdown']]
14
+ @file_paths = metadata.file_types.values_at('md', 'rd', 'markdown', 'raindown').flat_map { it }.compact
15
15
  @url_paths = {}
16
16
  @tags = {}
17
17
 
@@ -20,8 +20,7 @@ module Rain
20
20
 
21
21
  def page(path:)
22
22
  path = '/home' if path == '/'
23
- url_path = File.expand_path("app/pages#{path}", Dir.pwd)
24
- file_path = @url_paths[url_path] || return
23
+ file_path = @url_paths[path] || return
25
24
 
26
25
  metadata, markdown = parse_file(file_path:)
27
26
  raindown = Raindown.render(markdown:)
@@ -29,55 +28,38 @@ module Rain
29
28
  Page.new(metadata, raindown)
30
29
  end
31
30
 
32
- def list(file_paths:)
31
+ def list(**typed_tags)
32
+ file_paths = tagged(**typed_tags)
33
33
  sorted_paths = file_paths.sort_by { order(it) }
34
-
35
- sorted_paths.map do |file_path|
36
- metadata, markdown = parse_file(file_path:)
37
- metadata[:content] = markdown.empty? ? '' : Raindown.render(markdown:)
38
- OpenStruct.new(metadata)
39
- end
34
+ sorted_paths.map { |file_path| present_file(file_path:) }
40
35
  end
41
36
 
42
37
  def tagged(**typed_tags)
43
- results = []
38
+ file_paths = []
44
39
 
45
40
  typed_tags.each do |type, tag|
46
- results = [*results, *@tags.dig(type, tag)]
41
+ file_paths = [*file_paths, *@tags.dig(type, tag)]
47
42
  end
48
43
 
49
- results
44
+ file_paths
50
45
  end
51
46
 
52
47
  private
53
48
 
54
49
  def process_files
55
50
  @file_paths.each do |file_path|
56
- url_path = url_path(file_path:)
57
- @url_paths[url_path] = file_path
51
+ url_path = Pages.url_path(file_path:)
52
+ @url_paths[url_path] = file_path unless url_path.empty?
58
53
 
59
54
  tag(type: :folder, tag: folders(file_path:).last, file_path:)
60
55
 
61
56
  metadata, * = parse_file(file_path:, parse_content: false)
62
57
  metadata.each do |type, tag|
63
58
  tag(type:, tag:, file_path:)
64
- end
59
+ end
65
60
  end
66
61
  end
67
62
 
68
- # Remove segments beginning with "_" and ending with "/" or "-".
69
- # Example: app/pages/docs/_basics/_1-getting-started.md => app/pages/docs/getting-started.md
70
- def url_path(file_path:)
71
- url_path = file_path.split('/').map do |segment|
72
- next segment.sub(/^_\d\W/, '') if segment.sub(/^_\d\W/, '') != segment
73
- next nil if segment.sub(/^_/, '') != segment
74
-
75
- segment
76
- end.compact.join('/')
77
-
78
- url_path.delete_suffix(File.extname(url_path))
79
- end
80
-
81
63
  def tag(type:, tag:, file_path:)
82
64
  @tags[type] ||= {}
83
65
  @tags[type][tag] ||= []
@@ -86,15 +68,15 @@ module Rain
86
68
 
87
69
  def folders(file_path:)
88
70
  File.dirname(file_path)
89
- .split(Regexp.union(['/', '-']))
90
- .filter { it.start_with?('_') }
91
- .map { it.delete_prefix('_') }
92
- .filter { !number?(it) }
71
+ .split(Regexp.union(['/', '&', '-']))
72
+ .filter { it.start_with?('_') }
73
+ .map { it.delete_prefix('_') }
74
+ .filter { !number?(it) }
93
75
  end
94
76
 
95
77
  def order(file_path)
96
78
  file_path
97
- .split(Regexp.union(['/', '-']))
79
+ .split(Regexp.union(['/', '&', '-']))
98
80
  .filter { it.start_with?('_') }
99
81
  .map { it.delete_prefix('_') }
100
82
  .filter { number?(it) }
@@ -105,12 +87,20 @@ module Rain
105
87
  !Float(string, exception: false).nil?
106
88
  end
107
89
 
90
+ def present_file(file_path:)
91
+ metadata, markdown = parse_file(file_path:)
92
+ metadata[:content] = markdown.empty? ? '' : Raindown.render(markdown:)
93
+ metadata[:path] = Pages.url_path(file_path:)
94
+
95
+ OpenStruct.new(metadata)
96
+ end
97
+
108
98
  def parse_file(file_path:, parse_content: true)
109
99
  dash_lines = []
110
100
  data_lines = []
111
101
  text_lines = []
112
102
 
113
- File.foreach(file_path).with_index do |line, index|
103
+ File.foreach(file_path) do |line|
114
104
  if line.strip == '---' && dash_lines.count < 2
115
105
  dash_lines << line
116
106
  next
@@ -126,5 +116,24 @@ module Rain
126
116
 
127
117
  [YAML.safe_load(data_lines.join, symbolize_names: true), text_lines.join.strip]
128
118
  end
119
+
120
+ class << self
121
+ # Remove segments beginning with "_" and ending with "&", "-" or "/".
122
+ # Example: app/pages/docs/_basics/_1-getting-started.md => app/pages/docs/getting-started.md
123
+ def url_path(file_path:)
124
+ url_path = file_path.split(Regexp.union(['/', '&'])).map do |segment|
125
+ next segment.sub(/^_\d\W/, '') if segment.sub(/^_\d\W/, '') != segment
126
+ next nil if segment.sub(/^_/, '') != segment
127
+
128
+ segment
129
+ end.compact.join('/')
130
+
131
+ url_path.delete_prefix(pages_path).delete_suffix(File.extname(url_path))
132
+ end
133
+
134
+ def pages_path
135
+ File.expand_path('app/pages', Dir.pwd)
136
+ end
137
+ end
129
138
  end
130
139
  end
@@ -5,7 +5,8 @@ require 'interfaces/lexeme' # From antlers gem, too generic.
5
5
  module Rain
6
6
  module TOCLexeme
7
7
  include Antlers::Lexeme
8
- extend self
8
+
9
+ module_function
9
10
 
10
11
  KEYWORDS = [':toc'].freeze
11
12
 
@@ -14,17 +14,17 @@ module Rain
14
14
  def render(current_binding: nil, parent_binding: nil, slot_node: nil)
15
15
  doc = Nokogiri::HTML(@template)
16
16
 
17
- output = <<~HTML
17
+ <<~HTML
18
18
  <div class="floating">
19
19
  <details id="toc" open>
20
20
  <summary>Table of contents</summary>
21
21
  <ul>
22
- #{doc.css('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').map { |h|
22
+ #{doc.css('h1[id], h2[id], h3[id], h4[id], h5[id], h6[id]').map do |h|
23
23
  "<li class='#{h.name}'><a href='\##{h['id']}'>#{h.text.strip}</a></li>"
24
- }.join("\n")}
24
+ end.join("\n")}
25
25
  </ul>
26
- </div>
27
- </details>
26
+ </details>
27
+ </div>
28
28
  HTML
29
29
  end
30
30
 
@@ -6,14 +6,14 @@ require_relative 'elements'
6
6
 
7
7
  module Rain
8
8
  module Raindown
9
- extend self
9
+ module_function
10
10
 
11
11
  def render(markdown:)
12
12
  template = markdown.gsub('<{', '<!-- ANTLERS').gsub('}>', 'ANTLERS -->')
13
13
 
14
14
  doc = Commonmarker.parse(template)
15
15
  doc.walk do |node|
16
- if node.type == :code || node.type == :code_block
16
+ if %i[code code_block].include?(node.type)
17
17
  node.string_content = node.string_content.gsub('<!-- ANTLERS', '<{').gsub('ANTLERS -->', '}>')
18
18
  end
19
19
  end
data/lib/raindeer/cli.rb CHANGED
@@ -5,7 +5,12 @@ require 'low_type'
5
5
  require 'observers'
6
6
  require 'providers'
7
7
 
8
- # Allows the CLI to load application code and metadata, but not start a server.
8
+ # The CLI loads user application code and metadata, but doesn't start a server.
9
+ #
10
+ # Flow:
11
+ # 1. bin/rain - Located in raindeer or user application, loads "raindeer/cli" which is available on $PATH.
12
+ # 2. lib/raindeer/cli - Boots up a raindeer/user application that is suitable for use by the CLI. <-- YOU ARE HERE
13
+ # 3. lib/cli/cli - Defines and runs the CLI that responds to the ARGs given to bin/rain.
9
14
 
10
15
  #################################################
11
16
  # FRAMEWORK INTERNAL API
@@ -24,13 +29,7 @@ end
24
29
  # FRAMEWORK EXTERNAL API
25
30
  #################################################
26
31
 
27
- module Raindeer
28
- class << self
29
- def router(&block)
30
- Providers['rain.router'].instance_eval(&block)
31
- end
32
- end
33
- end
32
+ require_relative 'raindeer'
34
33
 
35
34
  #################################################
36
35
  # CLI
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'boot'
4
-
5
3
  module Raindeer
6
4
  class << self
7
5
  def router(&block)
data/lib/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Raindeer
4
- VERSION = '0.9.1'
4
+ VERSION = '0.9.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: raindeer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
4
+ version: 0.9.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - maedi
@@ -10,7 +10,7 @@ cert_chain: []
10
10
  date: 1980-01-02 00:00:00.000000000 Z
11
11
  dependencies:
12
12
  - !ruby/object:Gem::Dependency
13
- name: ostruct
13
+ name: commonmarker
14
14
  requirement: !ruby/object:Gem::Requirement
15
15
  requirements:
16
16
  - - ">="
@@ -24,7 +24,7 @@ dependencies:
24
24
  - !ruby/object:Gem::Version
25
25
  version: '0'
26
26
  - !ruby/object:Gem::Dependency
27
- name: paint
27
+ name: nokogiri
28
28
  requirement: !ruby/object:Gem::Requirement
29
29
  requirements:
30
30
  - - ">="
@@ -38,7 +38,21 @@ dependencies:
38
38
  - !ruby/object:Gem::Version
39
39
  version: '0'
40
40
  - !ruby/object:Gem::Dependency
41
- name: commonmarker
41
+ name: ostruct
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: paint
42
56
  requirement: !ruby/object:Gem::Requirement
43
57
  requirements:
44
58
  - - ">="
@@ -66,7 +80,7 @@ dependencies:
66
80
  - !ruby/object:Gem::Version
67
81
  version: '0'
68
82
  - !ruby/object:Gem::Dependency
69
- name: nokogiri
83
+ name: ruby-progressbar
70
84
  requirement: !ruby/object:Gem::Requirement
71
85
  requirements:
72
86
  - - ">="
@@ -246,7 +260,7 @@ extra_rdoc_files: []
246
260
  files:
247
261
  - bin/rain
248
262
  - lib/cli/cli.rb
249
- - lib/cli/static/static.rb
263
+ - lib/cli/static.rb
250
264
  - lib/matrix/cursor.rb
251
265
  - lib/matrix/effects/color_effect.rb
252
266
  - lib/matrix/effects/effect.rb
@@ -1,77 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'antlers'
4
- require 'lowload'
5
-
6
- require 'async'
7
- require 'async/http/internet'
8
- require 'fileutils'
9
-
10
- module Rain
11
- module CLI
12
- module Static
13
- extend self
14
-
15
- Result = Data.define(:path, :html)
16
-
17
- def build(application_path:)
18
- metadata = LowLoad.dirload(File.expand_path('app', application_path))
19
-
20
- build_path = File.expand_path('build', application_path)
21
- FileUtils.rm_rf(build_path)
22
- FileUtils.mkdir_p(build_path)
23
- FileUtils.cp_r(File.expand_path('public', application_path), File.expand_path('public', build_path))
24
-
25
- request_paths(metadata:, application_path:).each do |result|
26
- path = File.expand_path(result.path, build_path)
27
- FileUtils.mkdir_p(path)
28
- File.write(File.expand_path('index.html', path), result.html)
29
- end
30
- end
31
-
32
- private
33
-
34
- def request_paths(metadata:, application_path:)
35
- tasks = metadata.url_paths.keys.compact.map do |file_path|
36
- Async do
37
- request_path = request_path(application_path:, file_path:)
38
- request_url = endpoint + request_path
39
-
40
- response = client.get(request_url)
41
- response_html = response.read
42
- response.close
43
-
44
- Result.new(path: request_path, html: response_html)
45
- end
46
- end
47
-
48
- tasks.map(&:result)
49
- end
50
-
51
- def request_path(application_path:, file_path:)
52
- file_path.delete_prefix("#{application_path}/app/pages/")
53
- end
54
-
55
- def client
56
- Async::HTTP::Internet.new
57
- end
58
-
59
- def endpoint
60
- "http://#{config.host}:#{config.port}/"
61
- end
62
-
63
- def config
64
- env = {
65
- host: ENV.fetch('RAIN_HOST', nil),
66
- port: ENV.fetch('RAIN_PORT', nil),
67
- web_root: ENV.fetch('RAIN_WEB_ROOT', nil),
68
- debug_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_DEBUG', true)),
69
- matrix_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_MATRIX', nil)),
70
- mirror_mode: ConfigLoader.parse_boolean(ENV.fetch('RAIN_MIRROR', nil)),
71
- }
72
- config_path = File.expand_path('config/config.yaml', Dir.pwd)
73
- ConfigLoader.load(config_path, env)
74
- end
75
- end
76
- end
77
- end