black-widow 0.0.1.alpha

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: 451595693d9f9d1da12fde89fceabc4d2f2910f845828e9a6741fc500af34804
4
+ data.tar.gz: 0f0e5bd73f61d9df812fb018e3f78205c435e9b4fee5b03f2dca53c03ee4ab91
5
+ SHA512:
6
+ metadata.gz: 0a68f6762019fdbb776eacd54e1fc61177050922b4c42694e566b96fc8dc599a539c91a75e26c753ca2568fba69b39eca5fa57c0989fa34c48382ed2b248ba48
7
+ data.tar.gz: ddc7f3a8648225dbef2f15ee650aa8dc5dc3ebc68028048f4606c2dbc3240e7781045ac9e9b1ee27843006d9d8c3dd1effcc7b67cbd2ea4e9ccd0945d88b4b28
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ # Temporary Files and Folders
2
+ tmp/*
3
+ *.tmp
4
+ **/*/tmp/*
5
+
6
+ # Logs
7
+ *.log
8
+
9
+ # Ruby Stuff
10
+ Gemfile.lock
11
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem 'rake'
6
+ # TODO:
7
+ # gem 'sass'
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright 2019 Donald Isaac
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7
+ of the Software, and to permit persons to whom the Software is furnished to do
8
+ so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ # Black Widow
2
+
3
+ Do *NOT* use this gem yet! It isn't even close to done!
4
+ *TODO*
data/Rakefile ADDED
@@ -0,0 +1,5 @@
1
+ task default: %w[test]
2
+
3
+ task :test do
4
+ ruby "test/driver.rb"
5
+ end
data/bin/black-widow ADDED
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # BlackWidow
5
+ # by Donald Isaac (https://www.opensourceryumd.com)
6
+ # Copyright (c) 2019 Open Sourcery. See LICENSE for license details.
7
+
8
+ require 'pathname'
9
+ require 'getoptlong'
10
+ require 'black-widow'
11
+
12
+ # Base command usage message
13
+ USAGE = {}
14
+ HELP = {}
15
+
16
+ # Usage messages
17
+
18
+ USAGE[:base] = <<-EOF
19
+ usage: black-widow [--help] [--version] command
20
+ EOF
21
+
22
+ USAGE[:generate] = <<-EOF
23
+ usage: black-widow generate [--help] <component> [--name <name>]
24
+ EOF
25
+
26
+ USAGE[:new] = <<-EOF
27
+ usage: black-widow new [--help] --name <name>
28
+ EOF
29
+
30
+ USAGE[:render] = <<-EOF
31
+ usage: black-widow render [--help] <root-dir>
32
+ EOF
33
+
34
+ # Help messages
35
+
36
+ HELP[:base] = <<-EOF
37
+ usage: black-widow [--help] [--version] <command>
38
+
39
+ Options:
40
+ -h, --help Display this help message
41
+ -v, --version Display BlackWidow's version
42
+
43
+ Available commands:
44
+ new Create a new BlackWidow project
45
+ generate Generate a new view component
46
+ render Compile a project into a servable site
47
+ EOF
48
+
49
+ HELP[:generate] = <<-EOF
50
+ EOF
51
+
52
+ HELP[:new] = <<-EOF
53
+ usage: black-widow new [--help] --name <name>
54
+
55
+ Discription:
56
+ This command generates a project skeleton for you to start your site from.
57
+ The skeleton is populated with the required view component directories,
58
+ along with a default .black-widow.yaml file.
59
+
60
+ Options:
61
+ -h, --help Display this help message
62
+ -n, --name Required. The name of the new project
63
+ EOF
64
+
65
+ HELP[:render] = <<-EOF
66
+ EOF
67
+
68
+ # :_ key is all unnamed arguments from ARGV
69
+ ARGS = { _: [] }
70
+ next_arg = :command # First unnamed arg is expected to be a sub command
71
+ ARGV.each do |arg|
72
+ case arg
73
+ when '-h', '--help' then ARGS[:help] = true
74
+ when '-v', '--version' then ARGS[:version] = true
75
+ when '-n', '--name' then next_arg = :name
76
+ # TODO: Are more args needed?
77
+ else
78
+ if next_arg
79
+ ARGS[next_arg] = arg
80
+ next_arg = nil
81
+ else
82
+ ARGS[:_].push arg
83
+ end
84
+ end
85
+ end
86
+
87
+ if ARGS[:help] || !!ARGV.length
88
+ if ARGS[:command] && HELP.include?(ARGS[:command].to_sym)
89
+ puts HELP[ARGS[:command].to_sym]
90
+ else
91
+ puts HELP[:base]
92
+ end
93
+ exit 0
94
+ end
95
+
96
+ if ARGS[:version]
97
+ puts BlackWidow::VERSION
98
+ exit 0
99
+ end
100
+
101
+ case ARGS[:command]
102
+ # Handle 'new' command
103
+ when 'n', 'new'
104
+ unless ARGS[:name]
105
+ puts USAGE_NEW
106
+ exit 1
107
+ end
108
+ # Handle 'generate' command
109
+ when 'g', 'gen', 'generate'
110
+ type, name, *rest = ARGV{:_}
111
+
112
+ if !type || !BlackWidow::VIEW_FOLDERS.include?(type) then
113
+ err('Invalid component type', 2)
114
+ end
115
+
116
+ err('You must specify a name.') unless name
117
+
118
+ puts "BlackWidow: This feature does not exist yet."
119
+ exit 0
120
+ # Handle 'render' command
121
+ when 'r', 'render'
122
+ begin
123
+ render_project
124
+ rescue StandardError => e
125
+ err(e, 3)
126
+ end
127
+
128
+ exit 0
129
+ else
130
+ puts USAGE
131
+ exit 1
132
+ end
133
+
134
+ ##
135
+ # Creates a new BlackWidow project skeleton
136
+ #
137
+ # This function creates a new project directory and populates it with a
138
+ # config.yaml file and folders for view components.
139
+ #
140
+ # @param name: String
141
+ # The name of the project. This will also be the name of the project
142
+ # directory.
143
+ #
144
+ # @return void
145
+ #
146
+ def init_project name
147
+ root = Pathname.new Dir.pwd
148
+ if (root + name).exist? then
149
+ raise ArgumentError 'Project could not be created; directory already exists.'
150
+ end
151
+
152
+ root.mkpath name
153
+ root.join name
154
+ ViewEngine::FOLDERS.each_value do |dir|
155
+ root.mkpath dir
156
+ end
157
+ config = File.new root.join(BlackWidow::CONFIG_FILE_NAME), 'w+'
158
+ end
159
+
160
+ def render_project
161
+ # Project root directory is the directory with the config file
162
+ root = File.dirname BlackWidow::resolve_config
163
+ Dir.chdir root
164
+ end
165
+
166
+ ##
167
+ # Prints an error message and exits with an error status
168
+ #
169
+ # @param err: Exception | RuntimeException | StandardErr | String
170
+ # The error being thrown.
171
+ #
172
+ # @param status: Number
173
+ # The exit status. Defaults to 1.
174
+ #
175
+ def err err, status = 1
176
+ err_type = if err.is_a? Error then err.class.name else 'Error' end
177
+ err_msg = err.message || err
178
+
179
+ puts "BlackWidow: - #{err.type} #{err_msg}"
180
+ exit status
181
+ end
182
+
183
+ # engine = BlackWidow::ViewEngine.new Pathname.new(Dir.pwd).join 'src'
184
+ # puts(engine.render 'index')
@@ -0,0 +1,27 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+
4
+ require 'black-widow/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = 'black-widow'
8
+ s.version = BlackWidow::VERSION
9
+ s.platform = Gem::Platform::RUBY
10
+ s.summary = "Black Widow - Static Site Generator"
11
+ s.description = "A simple static site generator powered by eRuby"
12
+ s.author = "Donald Isaac"
13
+ s.homepage = 'https://www.opensourceryumd.com'
14
+ s.license = 'MIT'
15
+ # s.bindir = 'bin'
16
+
17
+ s.add_dependency 'rake', '~> 0'
18
+
19
+ s.files = `git ls-files`.split("\n")
20
+
21
+ s.test_files = `git ls-files -- {test}/*`.split("\n")
22
+
23
+ s.executables = `git ls-files -- bin/*`
24
+ .split("\n")
25
+ .map { |f| File.basename(f) }
26
+ s.require_paths = ['lib']
27
+ end
@@ -0,0 +1,239 @@
1
+ require 'erb' # ERuby template engine
2
+ require 'pathname' # finding paths
3
+
4
+ module BlackWidow
5
+ ##
6
+ # This class contains all the functionality required for rendering
7
+ # HTML pages and partials
8
+ #
9
+ # The set of all assets required to render a page, such as the page template
10
+ # and it's context, are collectively known as a +view+. View assets must be
11
+ # located in specific locations, as Black Widow follows a 'configuration by
12
+ # convention' principle.
13
+ #
14
+ # Black Widow's conventions are based heavily on Ruby on Rails. If you are
15
+ # familiar with Rails, ViewEngine works much like ActionView.
16
+ #
17
+ # * page templates must be located in the `src/pages` directory
18
+ # * layout templates must be located in the `src/layouts` directory
19
+ # * partials must be located in the `src/partials` directory
20
+ # * contexts must be located in the `src/contexts` directory
21
+ #
22
+ #
23
+ # @author Donald Isaac
24
+ #
25
+ # Copyright (c) 2019 Open Sourcery. See LICENSE for license details.
26
+ #
27
+ class ViewEngine
28
+
29
+ # Keys are the available view components, values are component folder names
30
+ # TODO: Make this configurable from config.yaml
31
+ VIEW_FOLDERS = {
32
+ page: 'pages',
33
+ context: 'contexts',
34
+ partial: 'partials',
35
+ layout: 'layouts'
36
+ }
37
+
38
+ ##
39
+ # Creates a new ViewEngine.
40
+ #
41
+ # @param root_dir: String | Pathname
42
+ # Directory of a Black Widow project's source code. This directory must
43
+ # be the immediate parent of all view component VIEW_FOLDERS.
44
+ #
45
+ def initialize(root_dir = Dir.pwd)
46
+ @root = Pathname.new root_dir
47
+ raise ArgumentError.new "The root path '" + @root.to_s + "'is a file." unless @root.directory?
48
+
49
+ @view_components = {
50
+ page: { dir: @root + Pathname.new(VIEW_FOLDERS[:page]), ext: '.erb'},
51
+ context: { dir: @root + Pathname.new(VIEW_FOLDERS[:context]), ext: '.rb' },
52
+ partial: { dir: @root + Pathname.new(VIEW_FOLDERS[:partial]), ext: '.erb' },
53
+ layout: { dir: @root + Pathname.new(VIEW_FOLDERS[:layout]), ext: '.erb' }
54
+ }
55
+ end
56
+
57
+ ##
58
+ # Renders ERB files to HTML.
59
+ #
60
+ # ### Usage
61
+ #
62
+ # ```ruby
63
+ # # Render a page
64
+ # render [name], layout: 'foo'
65
+ # render page: [name]
66
+ # render layout: [name], [page]
67
+ #
68
+ # # Render a partial
69
+ # render partial [name]
70
+ # render :partial [name], :locals
71
+ #```
72
+ #
73
+ # ### Available Options
74
+ #
75
+ # - :page
76
+ # Renders a page view. This option is mutually exlusive with
77
+ # the :partial option.
78
+ #
79
+ # - :partial
80
+ # Renders a partial view. This option is mutually exclusive with
81
+ # the :page option.
82
+ #
83
+ # - :layout
84
+ # Specifies the name of the layout to use when rendering a page
85
+ # view. If no layout is specified, the default layout is used.
86
+ #
87
+ # - :namespace
88
+ # The namespace to use. See the namespace section for more info.
89
+ #
90
+ # @param options: Hash | String
91
+ # If options is a string, then a page view is rendered where
92
+ # options is the name of the view. When options is a hash, it
93
+ # contains user specified options. See the usage section for all
94
+ # available options.
95
+ #
96
+ # @param extra_options: Hash
97
+ # When options is the name of a view as a string, this hash
98
+ # contains the rendering options.
99
+ #
100
+ # @return The rendered view as a, HTML string.
101
+ #
102
+ # @author Donald Isaac
103
+ #
104
+ def render(options = {}, extra_options = {}, &block)
105
+ case options
106
+ when Hash
107
+ if options[:page]
108
+ render_page options[:page], options
109
+ elsif options[:partial] # Only partials can take blocks
110
+ render_partial options[:partial], options, &block
111
+ end
112
+ # `options` param is the name of the page view
113
+ when String
114
+ render_page options, extra_options
115
+ else
116
+ raise ArgumentError 'options parameter must be a Hash or String'\
117
+ ' You passed a(n)' + options.class.name + '.'
118
+ end
119
+ end
120
+
121
+ ##
122
+ # Gets the view component hash.
123
+ #
124
+ # @return the view component hash.
125
+ #
126
+ def components
127
+ @view_components
128
+ end
129
+
130
+ private
131
+
132
+ ##
133
+ # Renders a view to HTML.
134
+ #
135
+ # A view is the collection of data associated with a specific page.
136
+ # This data includes the erb template and it's associated context data.
137
+ #
138
+ # @param view: String
139
+ # The name of the view to render.
140
+ #
141
+ # @return The rendered page view as an HTML string.
142
+ #
143
+ def render_page(view, options={})
144
+ loaded_components = load_components view, [:page, :context]
145
+ page = ERB.new loaded_components[:page]
146
+ binding = loaded_components[:context].new.method(:context).call
147
+ raise 'Context method must return a binding obect.' unless binding.is_a? Binding
148
+
149
+ page.result binding
150
+ end
151
+
152
+ def render_partial(partial, options={}, &block)
153
+ 'render_partial called! ' + partial + ', ' + options.to_s
154
+ end
155
+ ##
156
+ # Resolves the paths for all of a view's assets.
157
+ #
158
+ # Resolved assets include the view's templates and rendering context.
159
+ #
160
+ # ==== Params:
161
+ # [view]
162
+ # String. The name or relative path of the view to resolve. The path
163
+ # must be relative to the +pages+ template directory.
164
+ #
165
+ # ==== Returns:
166
+ #
167
+ # String[]. The resolved asset paths in an array. The asset contains
168
+ # The following items (in order):
169
+ # [page]
170
+ # The page template
171
+ # [context]
172
+ # The view context
173
+ #
174
+
175
+ ##
176
+ # Loads a set of components for a view.
177
+ #
178
+ #
179
+ def load_components(view = '', components = [], options = {})
180
+ # Return an empty hash if no component types are specified
181
+ if !components.length then return {} end
182
+
183
+ # Strip whitespace
184
+ view.strip
185
+ namespace = options[:namespace] || ''
186
+
187
+ # Raise an error if +view+ was just whitespace and/or a forward slash
188
+ raise ArgumentError 'Invalid view name or directory.' unless view.length
189
+ raise ArgumentError 'No view components specified ' unless components
190
+
191
+ # Contents of view component files, where the key is component type,
192
+ # and the value is the file's contents
193
+ loaded_components = {}
194
+
195
+ # Load the contents of each component file,
196
+ # verifying that each one exists
197
+ components.each do |component_type|
198
+
199
+ # Validate that the specified view component type exists
200
+ if !@view_components.include? component_type
201
+ raise ArgumentError 'Component type ' + component_type + 'does not exist.'
202
+ end
203
+
204
+ component = @view_components[component_type]
205
+
206
+ # Validate that view component hash has keys for the directory and extension type
207
+ if !component.include?(:dir) || !component.include?(:ext)
208
+ raise ArgumentError 'Component hashes must have the :dir and :ext keys.'
209
+ end
210
+
211
+ component_path = component[:dir].join view + namespace + component[:ext]
212
+
213
+ # Throw an exception if the component file does not exist
214
+ if !component_path.exist?
215
+ raise "The '" + component_type.to_s + "' file for the '" +
216
+ view + "' view does not exist. View component path is " +
217
+ component_path.to_s
218
+ end
219
+
220
+ # Read the contents of the component file and
221
+ # store it into an array
222
+ loaded_components[component_type] = case component[:ext]
223
+ when '.rb'
224
+ context_classname = if namespace.length then
225
+ namespace.capitalize + '::'
226
+ end + view.capitalize + component_type.to_s.capitalize
227
+ # Import the context class
228
+ require_relative component_path
229
+ # TODO: Handle modules as :namespace option
230
+ Object.const_get context_classname
231
+ else
232
+ File.new(component_path, 'r').read
233
+ end
234
+ end
235
+
236
+ return loaded_components
237
+ end
238
+ end # !ViewEngine
239
+ end # !Module
@@ -0,0 +1,3 @@
1
+ module BlackWidow
2
+ VERSION = '0.0.1.alpha'
3
+ end
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Black Widow
4
+ # by Donald Isaac (https://www.opensourceryumd.com)
5
+ # Copyright (c) 2019 Open Sourcery. See LICENSE for license details.
6
+ require 'black-widow/engine'
7
+ require 'pathname'
8
+ require 'yaml'
9
+
10
+ module BlackWidow
11
+ CONFIG_FILE_NAME = '.black-widow.yaml'
12
+ @config = nil
13
+
14
+ ##
15
+ # Resolves the location of the config file.
16
+ #
17
+ # If no config file exists in the current directory, then the parent
18
+ # directory is checked. This is done until the file is found or the root
19
+ # directory is reached, in which case an IOError is raised.
20
+ #
21
+ # @return The absolute path to the config file.
22
+ #
23
+ def self.resolve_config
24
+ config_path = Pathname.new Dir.pwd
25
+
26
+ # Ascend up the file tree until a .black-widow.yaml file is found
27
+ until config_path.children(false).include? CONFIG_FILE_NAME do
28
+ # A path is equal to its parent if and only if it is the root directory
29
+ if config_path == config_path.parent
30
+ raise IOError.new 'BlackWidow: Could not find a ' + CONFIG_FILE_NAME +
31
+ ' file. Are you running this command in your project directory?'
32
+ else
33
+ config_path = config_path.parent
34
+ end
35
+ end
36
+
37
+ config_path.join CONFIG_FILE_NAME
38
+ end
39
+
40
+ def self.config
41
+ if !@config
42
+ @config = resolve_config
43
+ else
44
+ @config
45
+ end
46
+ end
47
+
48
+ end
data/test/driver.rb ADDED
@@ -0,0 +1 @@
1
+ # TODO
metadata ADDED
@@ -0,0 +1,69 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: black-widow
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1.alpha
5
+ platform: ruby
6
+ authors:
7
+ - Donald Isaac
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-06-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rake
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: A simple static site generator powered by eRuby
28
+ email:
29
+ executables:
30
+ - black-widow
31
+ extensions: []
32
+ extra_rdoc_files: []
33
+ files:
34
+ - ".gitignore"
35
+ - Gemfile
36
+ - LICENSE
37
+ - README.md
38
+ - Rakefile
39
+ - bin/black-widow
40
+ - black-widow.gemspec
41
+ - lib/black-widow.rb
42
+ - lib/black-widow/engine.rb
43
+ - lib/black-widow/version.rb
44
+ - test/driver.rb
45
+ homepage: https://www.opensourceryumd.com
46
+ licenses:
47
+ - MIT
48
+ metadata: {}
49
+ post_install_message:
50
+ rdoc_options: []
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.3.1
63
+ requirements: []
64
+ rubyforge_project:
65
+ rubygems_version: 2.7.8
66
+ signing_key:
67
+ specification_version: 4
68
+ summary: Black Widow - Static Site Generator
69
+ test_files: []