aspen-cli 0.1.2
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/.github/FUNDING.yml +12 -0
- data/.gitignore +16 -0
- data/.travis.yml +6 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +175 -0
- data/LICENSE.txt +21 -0
- data/README.md +84 -0
- data/Rakefile +10 -0
- data/aspen.gemspec +48 -0
- data/bin/aspen +3 -0
- data/bin/console +22 -0
- data/bin/setup +8 -0
- data/lib/aspen/abstract_parser.rb +72 -0
- data/lib/aspen/abstract_statement.rb +34 -0
- data/lib/aspen/actions/compile.rb +31 -0
- data/lib/aspen/actions/push.rb +39 -0
- data/lib/aspen/actions/watch.rb +55 -0
- data/lib/aspen/actions.rb +8 -0
- data/lib/aspen/adapters.rb +38 -0
- data/lib/aspen/ast/nodes/attribute.rb +16 -0
- data/lib/aspen/ast/nodes/comment.rb +15 -0
- data/lib/aspen/ast/nodes/content.rb +17 -0
- data/lib/aspen/ast/nodes/custom_statement.rb +19 -0
- data/lib/aspen/ast/nodes/edge.rb +15 -0
- data/lib/aspen/ast/nodes/label.rb +15 -0
- data/lib/aspen/ast/nodes/narrative.rb +15 -0
- data/lib/aspen/ast/nodes/node.rb +20 -0
- data/lib/aspen/ast/nodes/statement.rb +17 -0
- data/lib/aspen/ast/nodes/type.rb +46 -0
- data/lib/aspen/ast.rb +18 -0
- data/lib/aspen/cli/commands/build.rb +26 -0
- data/lib/aspen/cli/commands/build_steps.rb +204 -0
- data/lib/aspen/cli/commands/compile.rb +27 -0
- data/lib/aspen/cli/commands/generate.rb +23 -0
- data/lib/aspen/cli/commands/new.rb +115 -0
- data/lib/aspen/cli/commands/push.rb +15 -0
- data/lib/aspen/cli/commands/version.rb +15 -0
- data/lib/aspen/cli/commands/watch.rb +30 -0
- data/lib/aspen/cli/commands.rb +28 -0
- data/lib/aspen/cli/templates/.gitignore +6 -0
- data/lib/aspen/cli/templates/airtable.yml +1 -0
- data/lib/aspen/cli/templates/convert +16 -0
- data/lib/aspen/cli/templates/db.yml.erb +22 -0
- data/lib/aspen/cli/templates/docker-compose.yml +23 -0
- data/lib/aspen/cli/templates/manifest.yml.erb +31 -0
- data/lib/aspen/cli.rb +9 -0
- data/lib/aspen/compiler.rb +209 -0
- data/lib/aspen/contracts/default_attribute_contract.rb +29 -0
- data/lib/aspen/contracts.rb +1 -0
- data/lib/aspen/conversion.rb +43 -0
- data/lib/aspen/custom_grammar/ast/nodes/bare.rb +17 -0
- data/lib/aspen/custom_grammar/ast/nodes/capture_segment.rb +19 -0
- data/lib/aspen/custom_grammar/ast/nodes/content.rb +17 -0
- data/lib/aspen/custom_grammar/ast/nodes/expression.rb +17 -0
- data/lib/aspen/custom_grammar/ast.rb +13 -0
- data/lib/aspen/custom_grammar/compiler.rb +80 -0
- data/lib/aspen/custom_grammar/grammar.rb +78 -0
- data/lib/aspen/custom_grammar/lexer.rb +76 -0
- data/lib/aspen/custom_grammar/matcher.rb +43 -0
- data/lib/aspen/custom_grammar/parser.rb +51 -0
- data/lib/aspen/custom_grammar.rb +23 -0
- data/lib/aspen/custom_statement.rb +35 -0
- data/lib/aspen/discourse.rb +122 -0
- data/lib/aspen/edge.rb +35 -0
- data/lib/aspen/errors.rb +158 -0
- data/lib/aspen/helpers.rb +17 -0
- data/lib/aspen/lexer.rb +195 -0
- data/lib/aspen/list.rb +19 -0
- data/lib/aspen/node.rb +53 -0
- data/lib/aspen/parser.rb +183 -0
- data/lib/aspen/renderers/abstract_renderer.rb +22 -0
- data/lib/aspen/renderers/cypher_base_renderer.rb +36 -0
- data/lib/aspen/renderers/cypher_batch_renderer.rb +55 -0
- data/lib/aspen/renderers/cypher_renderer.rb +18 -0
- data/lib/aspen/renderers/gexf_renderer.rb +47 -0
- data/lib/aspen/renderers/json_renderer.rb +40 -0
- data/lib/aspen/renderers.rb +9 -0
- data/lib/aspen/schemas/discourse_schema.rb +64 -0
- data/lib/aspen/schemas/grammar_schema.rb +24 -0
- data/lib/aspen/statement.rb +42 -0
- data/lib/aspen/system_default.rb +12 -0
- data/lib/aspen/version.rb +3 -0
- data/lib/aspen.rb +65 -0
- metadata +300 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
require 'listen'
|
|
2
|
+
|
|
3
|
+
module Aspen
|
|
4
|
+
module Actions
|
|
5
|
+
class Watch
|
|
6
|
+
|
|
7
|
+
def initialize(options: {})
|
|
8
|
+
@options = options
|
|
9
|
+
@logger = options.fetch(:logger) { Logger.new(STDOUT, level: :warn) }
|
|
10
|
+
Listen.logger = @logger
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call
|
|
14
|
+
puts warning_message if using_database?
|
|
15
|
+
|
|
16
|
+
listener = Listen.to('src/', only: /\.aspen$/) do |mod, add, _rem|
|
|
17
|
+
Aspen::CLI::Commands::Build.new.call
|
|
18
|
+
Aspen::Actions::Push.new.call
|
|
19
|
+
rescue Aspen::Error => e
|
|
20
|
+
puts e.message
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
listener.start
|
|
24
|
+
sleep
|
|
25
|
+
rescue Interrupt => e
|
|
26
|
+
# FIXME: The logger calls don't ever seem to work.
|
|
27
|
+
@logger.info "Exiting..."
|
|
28
|
+
puts "\nExiting..."
|
|
29
|
+
listener.stop
|
|
30
|
+
raise SystemExit
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def using_database?
|
|
36
|
+
@options[:database]
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def warning_message
|
|
40
|
+
<<~MSG
|
|
41
|
+
⚠️ WARNING: `aspen watch` is experimental, and saving a file/rebuilding the data
|
|
42
|
+
will cause ALL OF THE DATA in the database to be DELETED and rewritten.
|
|
43
|
+
|
|
44
|
+
By proceeding, you acknowledge that the Neo4j database at TODO: host:port will
|
|
45
|
+
be dropped the next time a file in this project is saved.
|
|
46
|
+
|
|
47
|
+
Use Ctrl+C to quit.
|
|
48
|
+
|
|
49
|
+
Watching....
|
|
50
|
+
MSG
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module Adapters
|
|
3
|
+
|
|
4
|
+
class Adapter
|
|
5
|
+
attr_reader :id, :name, :ext, :renderer
|
|
6
|
+
|
|
7
|
+
def initialize(id: , name: , ext: )
|
|
8
|
+
@id = id
|
|
9
|
+
@name = name
|
|
10
|
+
@ext = ext
|
|
11
|
+
# @todo This will be buggy if we have a two-word class
|
|
12
|
+
@renderer = Kernel.const_get("Aspen::Renderers::#{@name.downcase.capitalize}Renderer")
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Registry
|
|
17
|
+
|
|
18
|
+
attr_reader :data
|
|
19
|
+
|
|
20
|
+
def initialize
|
|
21
|
+
@data ||= {
|
|
22
|
+
cypher: Adapter.new(id: :cypher, name: "Cypher", ext: '.cql' ),
|
|
23
|
+
json: Adapter.new(id: :json, name: "JSON", ext: '.json'),
|
|
24
|
+
gexf: Adapter.new(id: :gexf, name: "GEXF", ext: '.gexf'),
|
|
25
|
+
}
|
|
26
|
+
Aspen.available_formats = @data.keys
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def self.get(key)
|
|
30
|
+
# @todo There's a better design for this.
|
|
31
|
+
@@store ||= new.data
|
|
32
|
+
@@store.fetch(key)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
class Attribute
|
|
5
|
+
|
|
6
|
+
attr_reader :content, :type
|
|
7
|
+
|
|
8
|
+
def initialize(content)
|
|
9
|
+
@content = Aspen::AST::Nodes::Content.new(content)
|
|
10
|
+
@type = Aspen::AST::Nodes::Type.determine(content)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
class CustomStatement
|
|
5
|
+
|
|
6
|
+
attr_reader :content
|
|
7
|
+
|
|
8
|
+
# I wonder: Should the AST for CustomGrammar be grouped
|
|
9
|
+
# into lib/ast? Should the CustomStatement AST node contain
|
|
10
|
+
# the Bare Segments and Capture Segments, along with
|
|
11
|
+
# variables with types and labels?
|
|
12
|
+
def initialize(content)
|
|
13
|
+
@content = Aspen::AST::Nodes::Content.new(content)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
class Node
|
|
5
|
+
|
|
6
|
+
attr_reader :attribute, :label
|
|
7
|
+
|
|
8
|
+
def initialize(attribute: , label: nil)
|
|
9
|
+
@attribute = Aspen::AST::Nodes::Attribute.new(attribute)
|
|
10
|
+
@label = Aspen::AST::Nodes::Label.new(label)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def label=(content)
|
|
14
|
+
@label = Aspen::AST::Nodes::Label.new(content)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
class Statement
|
|
5
|
+
|
|
6
|
+
attr_reader :origin, :edge, :target
|
|
7
|
+
|
|
8
|
+
def initialize(origin: nil, edge: nil, target: nil)
|
|
9
|
+
@origin = origin
|
|
10
|
+
@edge = edge
|
|
11
|
+
@target = target
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
class Type
|
|
5
|
+
|
|
6
|
+
STRING = "STRING"
|
|
7
|
+
INTEGER = "INTEGER"
|
|
8
|
+
FLOAT = "FLOAT"
|
|
9
|
+
|
|
10
|
+
MATCH_FLOAT = /^([\d,]+\.\d+)$/
|
|
11
|
+
MATCH_INTEGER = /^([\d,]+)$/
|
|
12
|
+
MATCH_STRING = /^(.+)$/
|
|
13
|
+
|
|
14
|
+
def self.determine(value)
|
|
15
|
+
new(
|
|
16
|
+
case value
|
|
17
|
+
when MATCH_FLOAT then FLOAT
|
|
18
|
+
when MATCH_INTEGER then INTEGER
|
|
19
|
+
when MATCH_STRING then STRING
|
|
20
|
+
else
|
|
21
|
+
raise ArgumentError, "Could not determine a type for value:\n\t#{value.inspect}"
|
|
22
|
+
end
|
|
23
|
+
)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
attr_reader :content, :converter
|
|
27
|
+
|
|
28
|
+
def initialize(type_const)
|
|
29
|
+
@content = Aspen::AST::Nodes::Content.new(type_const)
|
|
30
|
+
@converter = get_converter(type_const)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def get_converter(type_const)
|
|
34
|
+
case type_const
|
|
35
|
+
when FLOAT then :to_f
|
|
36
|
+
when INTEGER then :to_i
|
|
37
|
+
when STRING then :to_s
|
|
38
|
+
else
|
|
39
|
+
raise ArgumentError, "Could not determine a converter method for type:\n\t#{value.inspect}"
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
data/lib/aspen/ast.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module AST
|
|
3
|
+
module Nodes
|
|
4
|
+
end
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
require 'aspen/ast/nodes/attribute'
|
|
9
|
+
require 'aspen/ast/nodes/type'
|
|
10
|
+
|
|
11
|
+
require 'aspen/ast/nodes/comment'
|
|
12
|
+
require 'aspen/ast/nodes/content'
|
|
13
|
+
require 'aspen/ast/nodes/edge'
|
|
14
|
+
require 'aspen/ast/nodes/label'
|
|
15
|
+
require 'aspen/ast/nodes/narrative'
|
|
16
|
+
require 'aspen/ast/nodes/node'
|
|
17
|
+
require 'aspen/ast/nodes/statement'
|
|
18
|
+
require 'aspen/ast/nodes/custom_statement'
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require 'csv'
|
|
2
|
+
require 'aspen/cli/commands/build_steps'
|
|
3
|
+
|
|
4
|
+
module Aspen
|
|
5
|
+
module CLI
|
|
6
|
+
module Commands
|
|
7
|
+
|
|
8
|
+
class Build < Dry::CLI::Command
|
|
9
|
+
desc "Build Aspen project"
|
|
10
|
+
|
|
11
|
+
option :batch, type: :boolean, desc: "Batching", default: true
|
|
12
|
+
|
|
13
|
+
include BuildSteps
|
|
14
|
+
|
|
15
|
+
def call(**options)
|
|
16
|
+
CheckAspenProject.new.call
|
|
17
|
+
DownloadAttachedResources.new.call
|
|
18
|
+
ConvertIntoAspen.new.call
|
|
19
|
+
main_aspen_file = CollectMainAspen.new.call
|
|
20
|
+
CompileMainAspen.new.call(main_aspen_file, options)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
require 'airtable'
|
|
2
|
+
|
|
3
|
+
module Aspen
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
module BuildSteps
|
|
7
|
+
|
|
8
|
+
class BuildStep
|
|
9
|
+
def manifest
|
|
10
|
+
return @manifest if @manifest
|
|
11
|
+
@manifest = @files.exist?('manifest.yml') ? YAML.load_file('manifest.yml') : {}
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def config
|
|
15
|
+
return @config if @config
|
|
16
|
+
@config = {}
|
|
17
|
+
Dir['config/*.yml'].each do |path|
|
|
18
|
+
key = File.basename(path, File.extname(path))
|
|
19
|
+
value = YAML.load_file(path)
|
|
20
|
+
@config[key.to_s] = value
|
|
21
|
+
end
|
|
22
|
+
@config
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def call(*)
|
|
26
|
+
@files = Dry::CLI::Utils::Files
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class CheckAspenProject < BuildStep
|
|
32
|
+
# Check if we're in an Aspen project, and stop if not.
|
|
33
|
+
def call(*)
|
|
34
|
+
super
|
|
35
|
+
unless @files.exist?('.aspen')
|
|
36
|
+
raise ArgumentError, "Must be within an Aspen project to run `build`"
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end # / CheckAspenProject
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class DownloadAttachedResources < BuildStep
|
|
43
|
+
def call(*)
|
|
44
|
+
super
|
|
45
|
+
return false unless manifest['attached']
|
|
46
|
+
check_for_save_file
|
|
47
|
+
if cache_expired?
|
|
48
|
+
download
|
|
49
|
+
update_save_file!
|
|
50
|
+
else
|
|
51
|
+
puts "----> Skipping download, cache is still good."
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def check_for_save_file
|
|
56
|
+
unless @files.exist?('build/last-download')
|
|
57
|
+
File.open('build/last-download', 'w') { |f| f << 0 }
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def cache_expired?
|
|
62
|
+
if manifest["cache_seconds"]
|
|
63
|
+
last = File.read('build/last-download').to_i # Last download time
|
|
64
|
+
time_since_last_download = (Time.now - last).to_i # Time since last download
|
|
65
|
+
threshhold = manifest['cache_seconds'].to_i # Cache threshhold
|
|
66
|
+
time_since_last_download > threshhold
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def update_save_file!
|
|
71
|
+
File.open('build/last-download', 'w') { |f| f << Time.now.to_i }
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def download
|
|
75
|
+
puts '----> Downloading attached resources'
|
|
76
|
+
manifest['attached'].each do |resource|
|
|
77
|
+
puts " > Downloading #{resource["name"]} (#{resource["source"].capitalize})"
|
|
78
|
+
case resource["source"]
|
|
79
|
+
when "airtable" then download_airtable_resource(resource)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def download_airtable_resource(resource)
|
|
85
|
+
client = Airtable::Client.new(config.dig('airtable', 'api_key'))
|
|
86
|
+
table = client.table(resource['app_id'], resource['table'])
|
|
87
|
+
out_path = "src/#{resource["name"].downcase.gsub(" ", "_")}.csv"
|
|
88
|
+
CSV.open(out_path, 'w') do |file|
|
|
89
|
+
columns = Array(resource['columns'])
|
|
90
|
+
file << columns
|
|
91
|
+
table.records.each do |record|
|
|
92
|
+
file << columns.map { |col| record[col] }
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
end # / DownloadAttachedResources
|
|
98
|
+
|
|
99
|
+
class ConvertIntoAspen < BuildStep
|
|
100
|
+
def call(*)
|
|
101
|
+
super
|
|
102
|
+
puts "----> Converting non-Aspen to Aspen (bin/convert)"
|
|
103
|
+
if @files.exist?('bin/convert')
|
|
104
|
+
unless system('bin/convert')
|
|
105
|
+
raise RuntimeError, "`bin/convert` didn't work, stopping build. See above Traceback."
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end # / ConvertIntoAspen
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class CollectMainAspen < BuildStep
|
|
113
|
+
def call(*)
|
|
114
|
+
super
|
|
115
|
+
# Grab all the grammars and Aspen source files, make into one main Aspen file.
|
|
116
|
+
puts "----> Collecting main.aspen from src/ and src/grammars"
|
|
117
|
+
|
|
118
|
+
# Clear the build/ folder
|
|
119
|
+
Dir["build/main-*"].each { |path| @files.delete(path)}
|
|
120
|
+
|
|
121
|
+
@grammars = "" # Main grammars IO
|
|
122
|
+
@discourses = "" # Main discourses IO
|
|
123
|
+
@aspens = "" # Main Aspen IO
|
|
124
|
+
|
|
125
|
+
collect_discourses
|
|
126
|
+
collect_grammars
|
|
127
|
+
collect_aspen
|
|
128
|
+
|
|
129
|
+
main_aspen = File.open("build/main-#{Time.now.to_i}.aspen", 'w') do |file|
|
|
130
|
+
file << @discourses
|
|
131
|
+
file << @grammars
|
|
132
|
+
file << Aspen::SEPARATOR + "\n"
|
|
133
|
+
file << @aspens
|
|
134
|
+
end
|
|
135
|
+
return main_aspen
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
def collect_discourses
|
|
139
|
+
Dir['src/discourses/*.aspen'].map do |path|
|
|
140
|
+
# Skip if there's an ignore: src: in the manifest, and if it matches the file path
|
|
141
|
+
next if manifest.dig("ignore", "discourses") && manifest.dig("ignore", "discourses").any? { |ignore_path| Regexp.new(ignore_path) =~ path }
|
|
142
|
+
@discourses << File.read(path)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
def collect_grammars
|
|
147
|
+
Dir['src/grammars/*.aspen'].map do |path|
|
|
148
|
+
# Skip if there's an ignore: src: in the manifest, and if it matches the file path
|
|
149
|
+
next if manifest.dig("ignore", "grammars") && manifest.dig("ignore", "grammars").any? { |ignore_path| Regexp.new(ignore_path) =~ path }
|
|
150
|
+
@grammars << File.read(path)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def collect_aspen
|
|
155
|
+
Dir['src/*.aspen'].map do |path|
|
|
156
|
+
next if manifest.dig("ignore", "src") && manifest.dig("ignore", "src").any? do |ignore_path|
|
|
157
|
+
Regexp.new(ignore_path) =~ path
|
|
158
|
+
end
|
|
159
|
+
File.read(path)
|
|
160
|
+
end.compact.each do |file|
|
|
161
|
+
if file.include?(SEPARATOR)
|
|
162
|
+
env, _sep, code = file.partition(SEPARATOR)
|
|
163
|
+
@discourses << env
|
|
164
|
+
@aspens << code
|
|
165
|
+
else
|
|
166
|
+
@aspens << file
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
end # / CollectMainAspen
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
class CompileMainAspen < BuildStep
|
|
174
|
+
|
|
175
|
+
def call(main_aspen_file, options)
|
|
176
|
+
super
|
|
177
|
+
puts "----> Compiling main Aspen file (#{main_aspen_file.path})"
|
|
178
|
+
# Compile the main Aspen file, according to manifest.yml
|
|
179
|
+
unless manifest["output"]
|
|
180
|
+
puts " Defaulting to Cypher output. To customize, set up a manifest.yml."
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
outputs = manifest["output"] ? Array(manifest["output"]) : ["Cypher"]
|
|
184
|
+
|
|
185
|
+
outputs.each do |format|
|
|
186
|
+
adapter = Aspen::Adapters::Registry.get(format.downcase.to_sym)
|
|
187
|
+
out_path = main_aspen_file.path.gsub(/\.aspen$/, '') + adapter.ext
|
|
188
|
+
|
|
189
|
+
File.open(out_path, 'w') do |file|
|
|
190
|
+
file << Aspen.compile_text(
|
|
191
|
+
File.read(main_aspen_file),
|
|
192
|
+
adapter: adapter.id,
|
|
193
|
+
batch: options[:batch]
|
|
194
|
+
)
|
|
195
|
+
end
|
|
196
|
+
puts "----> Compiled main #{adapter.name} file"
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
end # / CompileMainAspen
|
|
200
|
+
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'aspen/actions/compile'
|
|
2
|
+
|
|
3
|
+
module Aspen
|
|
4
|
+
module CLI
|
|
5
|
+
module Commands
|
|
6
|
+
|
|
7
|
+
class Compile < Dry::CLI::Command
|
|
8
|
+
desc "Compile an Aspen file to Cypher (in the same directory)"
|
|
9
|
+
|
|
10
|
+
argument :path, required: true, desc: "Aspen file to compile to Cypher"
|
|
11
|
+
|
|
12
|
+
option :database, type: :string, desc: "Database URL", aliases: ["d"]
|
|
13
|
+
# option :mode, default: "http", values: %w[http https bolt], desc: "The connection protocol"
|
|
14
|
+
|
|
15
|
+
example [
|
|
16
|
+
"path/to/file.aspen # Compiles to path/to/file.cypher",
|
|
17
|
+
"path/to/file.aspen -d http://neo4j:pass@localhost:11002 # Compiles and runs in database"
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
def call(path: , **options)
|
|
21
|
+
Aspen::Actions::Compile.new(path, options).call
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Aspen
|
|
2
|
+
module CLI
|
|
3
|
+
module Commands
|
|
4
|
+
|
|
5
|
+
module Generate
|
|
6
|
+
class Discourse < Dry::CLI::Command
|
|
7
|
+
desc "Generate a new discourse within a project"
|
|
8
|
+
def call()
|
|
9
|
+
# NO OP
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class Narrative < Dry::CLI::Command
|
|
14
|
+
desc "Generate a new narrative within a project"
|
|
15
|
+
def call()
|
|
16
|
+
# NO OP
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|