edgeway 0.1.0 → 0.2.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 +4 -4
- data/CHANGELOG.md +34 -0
- data/README.md +270 -9
- data/js/cloudflare-d1-sessions.js +68 -0
- data/js/cloudflare.js +123 -22
- data/js/cors.js +15 -16
- data/js/headers.js +16 -0
- data/js/jwt.js +1 -1
- data/lib/edgeway/application.rb +6 -3
- data/lib/edgeway/cli/build/app_config.rb +39 -0
- data/lib/edgeway/cli/build/app_files.rb +40 -0
- data/lib/edgeway/cli/build/database_config.rb +92 -0
- data/lib/edgeway/cli/build/view_compiler.rb +191 -0
- data/lib/edgeway/cli/build.rb +36 -11
- data/lib/edgeway/cli/generate.rb +174 -26
- data/lib/edgeway/cli/new.rb +1 -1
- data/lib/edgeway/cli/test.rb +4 -1
- data/lib/edgeway/cli.rb +5 -4
- data/lib/edgeway/controller/base.rb +12 -2
- data/lib/edgeway/controller/dispatcher.rb +11 -1
- data/lib/edgeway/controller/rendering.rb +24 -4
- data/lib/edgeway/db/adapter.rb +38 -0
- data/lib/edgeway/db/memory.rb +9 -2
- data/lib/edgeway/db/query_stats.rb +46 -0
- data/lib/edgeway/db/sqlite.rb +73 -9
- data/lib/edgeway/errors.rb +22 -0
- data/lib/edgeway/generators/app/templates/.github/workflows/ci.yml.tt +38 -0
- data/lib/edgeway/generators/app/templates/.ruby-version.tt +1 -0
- data/lib/edgeway/generators/app/templates/README.md.tt +6 -2
- data/lib/edgeway/generators/app/templates/app/views/layouts/application.html.erb +11 -0
- data/lib/edgeway/generators/app/templates/bin/build.tt +1 -1
- data/lib/edgeway/generators/app/templates/bin/deploy.tt +1 -1
- data/lib/edgeway/generators/app/templates/bin/test.tt +7 -0
- data/lib/edgeway/generators/app/templates/config/database.yml +17 -0
- data/lib/edgeway/generators/app/templates/mise.toml.tt +1 -1
- data/lib/edgeway/generators/app/templates/wrangler.toml.tt +7 -0
- data/lib/edgeway/http/auth/stub.rb +1 -3
- data/lib/edgeway/http/request.rb +2 -22
- data/lib/edgeway/inflector.rb +19 -0
- data/lib/edgeway/model/attributes.rb +93 -10
- data/lib/edgeway/model/base.rb +50 -5
- data/lib/edgeway/model/connection_registry.rb +25 -0
- data/lib/edgeway/model/macros.rb +5 -1
- data/lib/edgeway/model/persistence.rb +18 -11
- data/lib/edgeway/model/relation.rb +106 -56
- data/lib/edgeway/model/validations.rb +29 -23
- data/lib/edgeway/routing/route.rb +1 -6
- data/lib/edgeway/server.rb +12 -0
- data/lib/edgeway/service/cloudflare/d1.rb +39 -10
- data/lib/edgeway/test.rb +29 -8
- data/lib/edgeway/version.rb +1 -1
- data/lib/edgeway/view.rb +24 -0
- data/lib/edgeway.rb +3 -0
- metadata +17 -4
- data/lib/edgeway/cli/app_config.rb +0 -35
- data/lib/edgeway/cli/app_files.rb +0 -37
|
@@ -6,13 +6,13 @@ module Edgeway
|
|
|
6
6
|
def valid?
|
|
7
7
|
@errors = []
|
|
8
8
|
@raw.each do |col, raw|
|
|
9
|
-
@errors << "#{col}
|
|
9
|
+
@errors << "#{col} #{bad_value_text(col)}" if !raw.nil? && @attributes[col].nil?
|
|
10
10
|
end
|
|
11
11
|
|
|
12
12
|
self.class.rules.each do |rule|
|
|
13
|
-
col = rule.
|
|
13
|
+
col = rule.attribute
|
|
14
14
|
value = rule.kind == "numericality" && @raw.key?(col) ? @raw[col] : @attributes[col]
|
|
15
|
-
msg = rule.message(value)
|
|
15
|
+
msg = rule.message(value, self.class.type_of(col))
|
|
16
16
|
@errors << msg unless msg.nil?
|
|
17
17
|
end
|
|
18
18
|
|
|
@@ -26,7 +26,7 @@ module Edgeway
|
|
|
26
26
|
if @errors.empty?
|
|
27
27
|
self.class.rules.each do |rule|
|
|
28
28
|
next unless rule.kind == "uniqueness"
|
|
29
|
-
@errors << "#{rule.
|
|
29
|
+
@errors << "#{rule.attribute} has already been taken" if taken?(rule)
|
|
30
30
|
end
|
|
31
31
|
end
|
|
32
32
|
@errors.empty?
|
|
@@ -34,10 +34,14 @@ module Edgeway
|
|
|
34
34
|
|
|
35
35
|
private
|
|
36
36
|
|
|
37
|
+
def bad_value_text(column)
|
|
38
|
+
self.class.type_of(column) == :integer ? "is out of range" : "is invalid"
|
|
39
|
+
end
|
|
40
|
+
|
|
37
41
|
def taken?(rule)
|
|
38
|
-
value = @attributes[rule.
|
|
42
|
+
value = @attributes[rule.attribute]
|
|
39
43
|
return false if value.nil?
|
|
40
|
-
found = self.class.where(rule.
|
|
44
|
+
found = self.class.where(rule.attribute => value)
|
|
41
45
|
rule.scope.each { |c| found = found.where(c => @attributes[self.class.column(c)]) }
|
|
42
46
|
other = found.limit(1).to_a.first
|
|
43
47
|
!other.nil? && (new_record? || other.id != id)
|
|
@@ -45,7 +49,7 @@ module Edgeway
|
|
|
45
49
|
end
|
|
46
50
|
|
|
47
51
|
class Rule
|
|
48
|
-
attr_reader :
|
|
52
|
+
attr_reader :attribute, :kind
|
|
49
53
|
|
|
50
54
|
def self.scope_columns(options)
|
|
51
55
|
return [] if options == true
|
|
@@ -70,25 +74,25 @@ module Edgeway
|
|
|
70
74
|
end
|
|
71
75
|
|
|
72
76
|
def initialize(attribute, kind, bounds, list, pattern)
|
|
73
|
-
@
|
|
77
|
+
@attribute = attribute
|
|
74
78
|
@kind = kind
|
|
75
79
|
@bounds = bounds
|
|
76
80
|
@list = list
|
|
77
81
|
@pattern = pattern
|
|
78
82
|
end
|
|
79
83
|
|
|
80
|
-
def message(v)
|
|
84
|
+
def message(v, type = nil)
|
|
81
85
|
# Uniqueness needs a query: Validations#taken? does it.
|
|
82
86
|
return nil if @kind == "uniqueness"
|
|
83
87
|
if @kind == "presence"
|
|
84
|
-
return "#{@
|
|
88
|
+
return "#{@attribute} can't be blank" if v.nil? || blank_text?(v.to_s, type)
|
|
85
89
|
return nil
|
|
86
90
|
end
|
|
87
91
|
return nil if v.nil?
|
|
88
92
|
case @kind
|
|
89
|
-
when "length" then length_message(v.to_s.length)
|
|
90
|
-
when "inclusion" then @list.include?(v.to_s) ? nil : "#{@
|
|
91
|
-
when "format" then @pattern.match?(v.to_s) ? nil : "#{@
|
|
93
|
+
when "length" then type == :binary ? length_message(v.to_s.bytesize, "bytes") : length_message(v.to_s.length, "characters")
|
|
94
|
+
when "inclusion" then @list.include?(v.to_s) ? nil : "#{@attribute} is not included in the list"
|
|
95
|
+
when "format" then @pattern.match?(v.to_s) ? nil : "#{@attribute} is invalid"
|
|
92
96
|
when "numericality" then numericality_message(v)
|
|
93
97
|
end
|
|
94
98
|
end
|
|
@@ -97,15 +101,17 @@ module Edgeway
|
|
|
97
101
|
|
|
98
102
|
private
|
|
99
103
|
|
|
100
|
-
def
|
|
104
|
+
def blank_text?(text, type) = type == :binary ? text.empty? : text.strip.empty?
|
|
105
|
+
|
|
106
|
+
def length_message(n, unit)
|
|
101
107
|
if @bounds.key?("minimum") && n < @bounds["minimum"]
|
|
102
|
-
return "#{@
|
|
108
|
+
return "#{@attribute} is too short (minimum is #{@bounds["minimum"]} #{unit})"
|
|
103
109
|
end
|
|
104
110
|
if @bounds.key?("maximum") && n > @bounds["maximum"]
|
|
105
|
-
return "#{@
|
|
111
|
+
return "#{@attribute} is too long (maximum is #{@bounds["maximum"]} #{unit})"
|
|
106
112
|
end
|
|
107
113
|
if @bounds.key?("is") && n != @bounds["is"]
|
|
108
|
-
return "#{@
|
|
114
|
+
return "#{@attribute} is the wrong length (should be #{@bounds["is"]} #{unit})"
|
|
109
115
|
end
|
|
110
116
|
nil
|
|
111
117
|
end
|
|
@@ -114,20 +120,20 @@ module Edgeway
|
|
|
114
120
|
text = v.to_s
|
|
115
121
|
integer = text.match?(/\A[+-]?\d+\z/)
|
|
116
122
|
# Decimals and exponents too: Float#to_s writes 1.0e+20 and 1.0e-05.
|
|
117
|
-
return "#{@
|
|
118
|
-
return "#{@
|
|
123
|
+
return "#{@attribute} is not a number" unless integer || text.match?(/\A[+-]?\d+(\.\d+)?([eE][+-]?\d+)?\z/)
|
|
124
|
+
return "#{@attribute} must be an integer" if @bounds.key?("only_integer") && !integer
|
|
119
125
|
n = text.to_f
|
|
120
126
|
if @bounds.key?("greater_than") && !(n > @bounds["greater_than"])
|
|
121
|
-
return "#{@
|
|
127
|
+
return "#{@attribute} must be greater than #{@bounds["greater_than"]}"
|
|
122
128
|
end
|
|
123
129
|
if @bounds.key?("greater_than_or_equal_to") && !(n >= @bounds["greater_than_or_equal_to"])
|
|
124
|
-
return "#{@
|
|
130
|
+
return "#{@attribute} must be greater than or equal to #{@bounds["greater_than_or_equal_to"]}"
|
|
125
131
|
end
|
|
126
132
|
if @bounds.key?("less_than") && !(n < @bounds["less_than"])
|
|
127
|
-
return "#{@
|
|
133
|
+
return "#{@attribute} must be less than #{@bounds["less_than"]}"
|
|
128
134
|
end
|
|
129
135
|
if @bounds.key?("less_than_or_equal_to") && !(n <= @bounds["less_than_or_equal_to"])
|
|
130
|
-
return "#{@
|
|
136
|
+
return "#{@attribute} must be less than or equal to #{@bounds["less_than_or_equal_to"]}"
|
|
131
137
|
end
|
|
132
138
|
nil
|
|
133
139
|
end
|
|
@@ -13,12 +13,7 @@ module Edgeway
|
|
|
13
13
|
|
|
14
14
|
def self.split(path) = path.split("/").reject(&:empty?)
|
|
15
15
|
|
|
16
|
-
def controller_class_name
|
|
17
|
-
parts = @controller_path.split("/").map do |part|
|
|
18
|
-
part.split("_").map(&:capitalize).join
|
|
19
|
-
end
|
|
20
|
-
parts.join("::") + "Controller"
|
|
21
|
-
end
|
|
16
|
+
def controller_class_name = Inflector.controller_class_name(@controller_path)
|
|
22
17
|
|
|
23
18
|
def match(verb, path)
|
|
24
19
|
return nil unless verb == @verb || (verb == "HEAD" && @verb == "GET")
|
data/lib/edgeway/server.rb
CHANGED
|
@@ -7,6 +7,18 @@ module Edgeway
|
|
|
7
7
|
def self.run
|
|
8
8
|
response = Edgeway.application.call(HTTP::Request.from_env)
|
|
9
9
|
print response.to_cgi
|
|
10
|
+
log_query_stats
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.log_query_stats
|
|
14
|
+
Model.database_names.each { |name| log_database_stats(name) }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.log_database_stats(name)
|
|
18
|
+
stats = Model.query_stats(name)
|
|
19
|
+
return unless stats.queries > 0
|
|
20
|
+
prefix = name == Model::PRIMARY ? "" : "database=#{name} "
|
|
21
|
+
Edgeway.logger.info(prefix + stats.log_text)
|
|
10
22
|
end
|
|
11
23
|
end
|
|
12
24
|
end
|
|
@@ -7,18 +7,29 @@ require_relative "../../db/adapter"
|
|
|
7
7
|
module Edgeway
|
|
8
8
|
module Cloudflare
|
|
9
9
|
# The imports as Ruby methods: d1_query and d1_execute take the binding
|
|
10
|
-
# name, the SQL and the binds (JSON text);
|
|
10
|
+
# name, the SQL and the binds (JSON text); d1_exec takes the binding name
|
|
11
|
+
# and SQL text of one or more statements; d1_batch takes the binding name
|
|
11
12
|
# and the statements ([[sql, binds], ...], JSON text), and answer the result as JSON
|
|
12
13
|
# text:
|
|
13
14
|
#
|
|
14
|
-
# d1_query {"rows": [...]}
|
|
15
|
-
# d1_execute {"changes": n, "last_insert_id": id}
|
|
16
|
-
#
|
|
15
|
+
# d1_query {"rows": [...], "meta": META}
|
|
16
|
+
# d1_execute {"changes": n, "last_insert_id": id, "meta": META}
|
|
17
|
+
# d1_exec {"count": n, "meta": META}
|
|
18
|
+
# d1_batch {"results": [{"changes": n, "last_insert_id": id}, ...], "meta": META}
|
|
19
|
+
# META {"rows_read": n, "rows_written": n, "duration": ms,
|
|
20
|
+
# "served_by_region": "...", "served_by_primary": bool}
|
|
21
|
+
# (a batch's is the sum over its statements)
|
|
22
|
+
# BLOB a bind or a column value that is a BLOB is {"$blob": "<base64>"}
|
|
23
|
+
# (raw bytes can't cross: JSON text holds no arbitrary bytes)
|
|
24
|
+
# last_insert_id is a number; a rowid beyond 32 bits is a Bignum here,
|
|
25
|
+
# which last_insert_id_of turns into its decimal String
|
|
17
26
|
# any {"error": "message"} when D1 failed
|
|
27
|
+
# any {"error": "message", "unavailable": true} when it failed
|
|
28
|
+
# in a transient way (DatabaseUnavailable)
|
|
18
29
|
#
|
|
19
|
-
# An import can't hand a string to wasm, so a call is two:
|
|
20
|
-
# runs the statement and answers the byte length of its result, and
|
|
21
|
-
# take(buf) copies the result into buf. JS wraps
|
|
30
|
+
# An import can't hand a string to wasm, so a call is two: each import
|
|
31
|
+
# runs the statement(s) and answers the byte length of its result, and
|
|
32
|
+
# take(buf) copies the result into buf. JS wraps each import in
|
|
22
33
|
# WebAssembly.Suspending: the D1 Promise is awaited with the wasm stack
|
|
23
34
|
# suspended (JSPI), so they read like blocking calls here.
|
|
24
35
|
module D1Imports
|
|
@@ -29,6 +40,7 @@ module Edgeway
|
|
|
29
40
|
|
|
30
41
|
D1_IMPORT("query") int js_d1_query(const char *binding, const char *sql, const char *binds);
|
|
31
42
|
D1_IMPORT("execute") int js_d1_execute(const char *binding, const char *sql, const char *binds);
|
|
43
|
+
D1_IMPORT("exec") int js_d1_exec(const char *binding, const char *sql);
|
|
32
44
|
D1_IMPORT("batch") int js_d1_batch(const char *binding, const char *statements);
|
|
33
45
|
D1_IMPORT("take") void js_d1_take(char *buf);
|
|
34
46
|
|
|
@@ -58,12 +70,17 @@ module Edgeway
|
|
|
58
70
|
return take_result(js_d1_execute(binding, sql, binds));
|
|
59
71
|
}
|
|
60
72
|
|
|
73
|
+
const char *d1_exec(const char *binding, const char *sql) {
|
|
74
|
+
return take_result(js_d1_exec(binding, sql));
|
|
75
|
+
}
|
|
76
|
+
|
|
61
77
|
const char *d1_batch(const char *binding, const char *statements) {
|
|
62
78
|
return take_result(js_d1_batch(binding, statements));
|
|
63
79
|
}
|
|
64
80
|
C
|
|
65
81
|
ffi_func :d1_query, [:str, :str, :str], :str
|
|
66
82
|
ffi_func :d1_execute, [:str, :str, :str], :str
|
|
83
|
+
ffi_func :d1_exec, [:str, :str], :str
|
|
67
84
|
ffi_func :d1_batch, [:str, :str], :str
|
|
68
85
|
end
|
|
69
86
|
|
|
@@ -74,26 +91,38 @@ module Edgeway
|
|
|
74
91
|
end
|
|
75
92
|
|
|
76
93
|
def select_all(sql, binds)
|
|
94
|
+
check_binds(binds)
|
|
77
95
|
result(D1Imports.d1_query(@binding_name, sql, JSON.generate(binds)))["rows"]
|
|
78
96
|
end
|
|
79
97
|
|
|
80
98
|
def execute(sql, binds)
|
|
99
|
+
check_binds(binds)
|
|
81
100
|
res = result(D1Imports.d1_execute(@binding_name, sql, JSON.generate(binds)))
|
|
82
|
-
|
|
101
|
+
write_result(res)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def exec(sql)
|
|
105
|
+
result(D1Imports.d1_exec(@binding_name, sql))
|
|
83
106
|
end
|
|
84
107
|
|
|
85
108
|
# One D1 batch(): the statements run as a single transaction, so any
|
|
86
109
|
# failure rolls all of them back.
|
|
87
110
|
def batch(statements)
|
|
111
|
+
statements.each { |s| check_binds(s[1]) }
|
|
88
112
|
res = result(D1Imports.d1_batch(@binding_name, JSON.generate(statements)))
|
|
89
|
-
res["results"].map { |
|
|
113
|
+
res["results"].map { |entry| write_result(entry) }
|
|
90
114
|
end
|
|
91
115
|
|
|
92
116
|
private
|
|
93
117
|
|
|
118
|
+
def write_result(entry)
|
|
119
|
+
{ "changes" => entry["changes"].to_i, "last_insert_id" => last_insert_id_of(entry["last_insert_id"]) }
|
|
120
|
+
end
|
|
121
|
+
|
|
94
122
|
def result(json)
|
|
95
123
|
res = JSON.parse(json)
|
|
96
|
-
|
|
124
|
+
raise_error_result(res, "d1: ") if res.key?("error")
|
|
125
|
+
record_meta(res["meta"]) if res.key?("meta")
|
|
97
126
|
res
|
|
98
127
|
end
|
|
99
128
|
end
|
data/lib/edgeway/test.rb
CHANGED
|
@@ -28,19 +28,40 @@ module Edgeway
|
|
|
28
28
|
# What a model test needs: a fresh in-memory SQLite database per test
|
|
29
29
|
# (SQLiteAdapter), with the app's db/migrations applied in file name order,
|
|
30
30
|
# so a test can check what was stored (Todo.find, db.select_all) and read
|
|
31
|
-
# db.log for the SQL.
|
|
32
|
-
#
|
|
31
|
+
# db.log for the SQL. A database under databases: in config/database.yml
|
|
32
|
+
# gets its own, with db/NAME/migrations, read with db(:name). Edgeway's own
|
|
33
|
+
# tests use MemoryAdapter instead, which stores nothing.
|
|
33
34
|
class UnitTest < SpinelTest::Unit::TestCase
|
|
34
|
-
attr_reader :db
|
|
35
|
-
|
|
36
35
|
def setup
|
|
37
|
-
@
|
|
38
|
-
|
|
39
|
-
Edgeway::
|
|
36
|
+
@databases = {}
|
|
37
|
+
open_database(Edgeway::Model::PRIMARY, "db/migrations")
|
|
38
|
+
Edgeway::TEST_DATABASES.each { |name| open_extra_database(name) }
|
|
40
39
|
end
|
|
41
40
|
|
|
42
41
|
def teardown
|
|
43
|
-
@
|
|
42
|
+
@databases.each do |name, database|
|
|
43
|
+
database.close
|
|
44
|
+
Edgeway::Model.disconnect(name)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def db(name = Edgeway::Model::PRIMARY) = @databases.fetch(name.to_s)
|
|
49
|
+
|
|
50
|
+
private
|
|
51
|
+
|
|
52
|
+
def open_extra_database(name)
|
|
53
|
+
migrations = "db/#{name}/migrations"
|
|
54
|
+
unless File.directory?(File.join(Edgeway::TEST_APP_ROOT, migrations))
|
|
55
|
+
raise Edgeway::ConfigurationError, "database #{name} (config/database.yml) has no #{migrations} directory"
|
|
56
|
+
end
|
|
57
|
+
open_database(name, migrations)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def open_database(name, migrations)
|
|
61
|
+
database = Edgeway::DB::SQLiteAdapter.new(":memory:")
|
|
62
|
+
database.apply_migrations(File.join(Edgeway::TEST_APP_ROOT, migrations))
|
|
63
|
+
Edgeway::Model.connect(name, database)
|
|
64
|
+
@databases[name] = database
|
|
44
65
|
end
|
|
45
66
|
end
|
|
46
67
|
|
data/lib/edgeway/version.rb
CHANGED
data/lib/edgeway/view.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
module Edgeway
|
|
2
|
+
module View
|
|
3
|
+
ESCAPES = { "&" => "&", "<" => "<", ">" => ">", '"' => """, "'" => "'" }
|
|
4
|
+
|
|
5
|
+
class SafeHTML
|
|
6
|
+
def initialize(text)
|
|
7
|
+
@text = text.to_s.dup
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def to_s = @text
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self.escape(obj)
|
|
14
|
+
return "" if obj.nil?
|
|
15
|
+
return obj.to_s if obj.is_a?(SafeHTML)
|
|
16
|
+
s = obj.to_s
|
|
17
|
+
s.gsub(/[&<>"']/, ESCAPES)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class String
|
|
23
|
+
def html_safe = Edgeway::View::SafeHTML.new(self)
|
|
24
|
+
end
|
data/lib/edgeway.rb
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
require "json"
|
|
2
2
|
require "time"
|
|
3
|
+
require "uri"
|
|
3
4
|
require_relative "edgeway/version"
|
|
4
5
|
require_relative "edgeway/errors"
|
|
5
6
|
require_relative "edgeway/inflector"
|
|
7
|
+
require_relative "edgeway/view"
|
|
6
8
|
require_relative "edgeway/config"
|
|
7
9
|
require_relative "edgeway/logger"
|
|
8
10
|
require_relative "edgeway/http/response"
|
|
@@ -13,6 +15,7 @@ require_relative "edgeway/routing/route"
|
|
|
13
15
|
require_relative "edgeway/routing/route_set"
|
|
14
16
|
require_relative "edgeway/routing/mapper"
|
|
15
17
|
require_relative "edgeway/db/adapter"
|
|
18
|
+
require_relative "edgeway/model/connection_registry"
|
|
16
19
|
require_relative "edgeway/model/attributes"
|
|
17
20
|
require_relative "edgeway/model/validations"
|
|
18
21
|
require_relative "edgeway/model/macros"
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: edgeway
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuji Yaginuma
|
|
@@ -16,21 +16,26 @@ executables:
|
|
|
16
16
|
extensions: []
|
|
17
17
|
extra_rdoc_files: []
|
|
18
18
|
files:
|
|
19
|
+
- CHANGELOG.md
|
|
19
20
|
- LICENSE.txt
|
|
20
21
|
- README.md
|
|
21
22
|
- exe/edgeway
|
|
22
23
|
- js/cgi.js
|
|
24
|
+
- js/cloudflare-d1-sessions.js
|
|
23
25
|
- js/cloudflare.js
|
|
24
26
|
- js/cors.js
|
|
27
|
+
- js/headers.js
|
|
25
28
|
- js/jwt.js
|
|
26
29
|
- js/runtime.js
|
|
27
30
|
- js/wasi-shim.js
|
|
28
31
|
- lib/edgeway.rb
|
|
29
32
|
- lib/edgeway/application.rb
|
|
30
33
|
- lib/edgeway/cli.rb
|
|
31
|
-
- lib/edgeway/cli/app_config.rb
|
|
32
|
-
- lib/edgeway/cli/app_files.rb
|
|
33
34
|
- lib/edgeway/cli/build.rb
|
|
35
|
+
- lib/edgeway/cli/build/app_config.rb
|
|
36
|
+
- lib/edgeway/cli/build/app_files.rb
|
|
37
|
+
- lib/edgeway/cli/build/database_config.rb
|
|
38
|
+
- lib/edgeway/cli/build/view_compiler.rb
|
|
34
39
|
- lib/edgeway/cli/generate.rb
|
|
35
40
|
- lib/edgeway/cli/new.rb
|
|
36
41
|
- lib/edgeway/cli/package_manager.rb
|
|
@@ -47,17 +52,22 @@ files:
|
|
|
47
52
|
- lib/edgeway/controller/rescuable.rb
|
|
48
53
|
- lib/edgeway/db/adapter.rb
|
|
49
54
|
- lib/edgeway/db/memory.rb
|
|
55
|
+
- lib/edgeway/db/query_stats.rb
|
|
50
56
|
- lib/edgeway/db/sqlite.rb
|
|
51
57
|
- lib/edgeway/errors.rb
|
|
58
|
+
- lib/edgeway/generators/app/templates/.github/workflows/ci.yml.tt
|
|
52
59
|
- lib/edgeway/generators/app/templates/.gitignore
|
|
60
|
+
- lib/edgeway/generators/app/templates/.ruby-version.tt
|
|
53
61
|
- lib/edgeway/generators/app/templates/Gemfile.tt
|
|
54
62
|
- lib/edgeway/generators/app/templates/README.md.tt
|
|
55
63
|
- lib/edgeway/generators/app/templates/app/controllers/application_controller.rb
|
|
56
64
|
- lib/edgeway/generators/app/templates/app/controllers/health_controller.rb
|
|
57
65
|
- lib/edgeway/generators/app/templates/app/models/application_model.rb
|
|
66
|
+
- lib/edgeway/generators/app/templates/app/views/layouts/application.html.erb
|
|
58
67
|
- lib/edgeway/generators/app/templates/bin/build.tt
|
|
59
68
|
- lib/edgeway/generators/app/templates/bin/deploy.tt
|
|
60
69
|
- lib/edgeway/generators/app/templates/bin/setup.tt
|
|
70
|
+
- lib/edgeway/generators/app/templates/bin/test.tt
|
|
61
71
|
- lib/edgeway/generators/app/templates/config/database.yml
|
|
62
72
|
- lib/edgeway/generators/app/templates/config/routes.rb
|
|
63
73
|
- lib/edgeway/generators/app/templates/db/migrations/.keep
|
|
@@ -77,6 +87,7 @@ files:
|
|
|
77
87
|
- lib/edgeway/logger.rb
|
|
78
88
|
- lib/edgeway/model/attributes.rb
|
|
79
89
|
- lib/edgeway/model/base.rb
|
|
90
|
+
- lib/edgeway/model/connection_registry.rb
|
|
80
91
|
- lib/edgeway/model/macros.rb
|
|
81
92
|
- lib/edgeway/model/persistence.rb
|
|
82
93
|
- lib/edgeway/model/relation.rb
|
|
@@ -89,11 +100,13 @@ files:
|
|
|
89
100
|
- lib/edgeway/service/cloudflare/jwt.rb
|
|
90
101
|
- lib/edgeway/test.rb
|
|
91
102
|
- lib/edgeway/version.rb
|
|
103
|
+
- lib/edgeway/view.rb
|
|
92
104
|
homepage: https://github.com/y-yagi/edgeway
|
|
93
105
|
licenses:
|
|
94
106
|
- MIT
|
|
95
107
|
metadata:
|
|
96
108
|
source_code_uri: https://github.com/y-yagi/edgeway
|
|
109
|
+
changelog_uri: https://github.com/y-yagi/edgeway/blob/main/CHANGELOG.md
|
|
97
110
|
rubygems_mfa_required: 'true'
|
|
98
111
|
rdoc_options: []
|
|
99
112
|
require_paths:
|
|
@@ -109,7 +122,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
109
122
|
- !ruby/object:Gem::Version
|
|
110
123
|
version: '0'
|
|
111
124
|
requirements: []
|
|
112
|
-
rubygems_version: 4.0.
|
|
125
|
+
rubygems_version: 4.0.16
|
|
113
126
|
specification_version: 4
|
|
114
127
|
summary: A library for Ruby compiled by Spinel to wasm on edge workers.
|
|
115
128
|
test_files: []
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Edgeway
|
|
4
|
-
module CLI
|
|
5
|
-
class AppConfig
|
|
6
|
-
# adapter: => the file build/main.rb requires for it.
|
|
7
|
-
ADAPTERS = {
|
|
8
|
-
"d1" => "edgeway/service/cloudflare/d1",
|
|
9
|
-
"memory" => "edgeway/db/memory"
|
|
10
|
-
}.freeze
|
|
11
|
-
|
|
12
|
-
attr_reader :env, :adapter, :binding_name
|
|
13
|
-
|
|
14
|
-
def self.load(root, env)
|
|
15
|
-
path = File.join(root, "config", "database.yml")
|
|
16
|
-
raise Error, "no config/database.yml in #{root} (is this an Edgeway app?)" unless File.exist?(path)
|
|
17
|
-
|
|
18
|
-
section = (YAML.safe_load_file(path) || {})[env]
|
|
19
|
-
raise Error, "config/database.yml has no #{env} section" unless section.is_a?(Hash)
|
|
20
|
-
|
|
21
|
-
new(env, section["adapter"].to_s, (section["binding"] || "DB").to_s)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def initialize(env, adapter, binding_name)
|
|
25
|
-
raise Error, "unknown database adapter #{adapter.inspect} (one of #{ADAPTERS.keys.join(", ")})" unless ADAPTERS.key?(adapter)
|
|
26
|
-
|
|
27
|
-
@env = env
|
|
28
|
-
@adapter = adapter
|
|
29
|
-
@binding_name = binding_name
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def adapter_file = ADAPTERS.fetch(@adapter)
|
|
33
|
-
end
|
|
34
|
-
end
|
|
35
|
-
end
|
|
@@ -1,37 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Edgeway
|
|
4
|
-
module CLI
|
|
5
|
-
class AppFiles
|
|
6
|
-
def initialize(root)
|
|
7
|
-
@root = root
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def require_lines
|
|
11
|
-
lines = []
|
|
12
|
-
app = File.join(@root, "app")
|
|
13
|
-
dirs = Dir.exist?(app) ? Dir.children(app).sort.select { |d| File.directory?(File.join(app, d)) } : []
|
|
14
|
-
dirs.each do |d|
|
|
15
|
-
next if %w[models controllers].include?(d)
|
|
16
|
-
|
|
17
|
-
lines.concat(requires("app/#{d}"))
|
|
18
|
-
end
|
|
19
|
-
lines.concat(requires("app/models"))
|
|
20
|
-
lines.concat(requires("app/controllers"))
|
|
21
|
-
lines << 'require_relative "../config/routes"'
|
|
22
|
-
lines
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# require_relative lines for the .rb files under dir, application_*.rb first.
|
|
26
|
-
def requires(dir)
|
|
27
|
-
full = File.join(@root, dir)
|
|
28
|
-
return [] unless Dir.exist?(full)
|
|
29
|
-
|
|
30
|
-
files = Dir.glob("**/*.rb", base: full).map { |f| File.join(dir, f) }
|
|
31
|
-
first, rest = files.partition { |f| File.basename(f).start_with?("application_") }
|
|
32
|
-
# String#dump quotes a path with spaces, quotes or "#{" safely.
|
|
33
|
-
(first.sort + rest.sort).map { |f| "require_relative #{"../#{f.delete_suffix(".rb")}".dump}" }
|
|
34
|
-
end
|
|
35
|
-
end
|
|
36
|
-
end
|
|
37
|
-
end
|