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.
Files changed (56) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +34 -0
  3. data/README.md +270 -9
  4. data/js/cloudflare-d1-sessions.js +68 -0
  5. data/js/cloudflare.js +123 -22
  6. data/js/cors.js +15 -16
  7. data/js/headers.js +16 -0
  8. data/js/jwt.js +1 -1
  9. data/lib/edgeway/application.rb +6 -3
  10. data/lib/edgeway/cli/build/app_config.rb +39 -0
  11. data/lib/edgeway/cli/build/app_files.rb +40 -0
  12. data/lib/edgeway/cli/build/database_config.rb +92 -0
  13. data/lib/edgeway/cli/build/view_compiler.rb +191 -0
  14. data/lib/edgeway/cli/build.rb +36 -11
  15. data/lib/edgeway/cli/generate.rb +174 -26
  16. data/lib/edgeway/cli/new.rb +1 -1
  17. data/lib/edgeway/cli/test.rb +4 -1
  18. data/lib/edgeway/cli.rb +5 -4
  19. data/lib/edgeway/controller/base.rb +12 -2
  20. data/lib/edgeway/controller/dispatcher.rb +11 -1
  21. data/lib/edgeway/controller/rendering.rb +24 -4
  22. data/lib/edgeway/db/adapter.rb +38 -0
  23. data/lib/edgeway/db/memory.rb +9 -2
  24. data/lib/edgeway/db/query_stats.rb +46 -0
  25. data/lib/edgeway/db/sqlite.rb +73 -9
  26. data/lib/edgeway/errors.rb +22 -0
  27. data/lib/edgeway/generators/app/templates/.github/workflows/ci.yml.tt +38 -0
  28. data/lib/edgeway/generators/app/templates/.ruby-version.tt +1 -0
  29. data/lib/edgeway/generators/app/templates/README.md.tt +6 -2
  30. data/lib/edgeway/generators/app/templates/app/views/layouts/application.html.erb +11 -0
  31. data/lib/edgeway/generators/app/templates/bin/build.tt +1 -1
  32. data/lib/edgeway/generators/app/templates/bin/deploy.tt +1 -1
  33. data/lib/edgeway/generators/app/templates/bin/test.tt +7 -0
  34. data/lib/edgeway/generators/app/templates/config/database.yml +17 -0
  35. data/lib/edgeway/generators/app/templates/mise.toml.tt +1 -1
  36. data/lib/edgeway/generators/app/templates/wrangler.toml.tt +7 -0
  37. data/lib/edgeway/http/auth/stub.rb +1 -3
  38. data/lib/edgeway/http/request.rb +2 -22
  39. data/lib/edgeway/inflector.rb +19 -0
  40. data/lib/edgeway/model/attributes.rb +93 -10
  41. data/lib/edgeway/model/base.rb +50 -5
  42. data/lib/edgeway/model/connection_registry.rb +25 -0
  43. data/lib/edgeway/model/macros.rb +5 -1
  44. data/lib/edgeway/model/persistence.rb +18 -11
  45. data/lib/edgeway/model/relation.rb +106 -56
  46. data/lib/edgeway/model/validations.rb +29 -23
  47. data/lib/edgeway/routing/route.rb +1 -6
  48. data/lib/edgeway/server.rb +12 -0
  49. data/lib/edgeway/service/cloudflare/d1.rb +39 -10
  50. data/lib/edgeway/test.rb +29 -8
  51. data/lib/edgeway/version.rb +1 -1
  52. data/lib/edgeway/view.rb +24 -0
  53. data/lib/edgeway.rb +3 -0
  54. metadata +17 -4
  55. data/lib/edgeway/cli/app_config.rb +0 -35
  56. 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} is out of range" if !raw.nil? && @attributes[col].nil?
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.column
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.column} has already been taken" if taken?(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.column]
42
+ value = @attributes[rule.attribute]
39
43
  return false if value.nil?
40
- found = self.class.where(rule.column => value)
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 :column, :kind
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
- @column = attribute
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 "#{@column} can't be blank" if v.nil? || v.to_s.strip.empty?
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 : "#{@column} is not included in the list"
91
- when "format" then @pattern.match?(v.to_s) ? nil : "#{@column} is invalid"
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 length_message(n)
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 "#{@column} is too short (minimum is #{@bounds["minimum"]} characters)"
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 "#{@column} is too long (maximum is #{@bounds["maximum"]} characters)"
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 "#{@column} is the wrong length (should be #{@bounds["is"]} characters)"
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 "#{@column} is not a number" unless integer || text.match?(/\A[+-]?\d+(\.\d+)?([eE][+-]?\d+)?\z/)
118
- return "#{@column} must be an integer" if @bounds.key?("only_integer") && !integer
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 "#{@column} must be greater than #{@bounds["greater_than"]}"
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 "#{@column} must be greater than or equal to #{@bounds["greater_than_or_equal_to"]}"
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 "#{@column} must be less than #{@bounds["less_than"]}"
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 "#{@column} must be less than or equal to #{@bounds["less_than_or_equal_to"]}"
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")
@@ -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); d1_batch takes the binding name
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
- # d1_batch {"results": [{"changes": n, "last_insert_id": id}, ...]}
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: query/execute
20
- # runs the statement and answers the byte length of its result, and
21
- # take(buf) copies the result into buf. JS wraps query and execute in
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
- { "changes" => res["changes"].to_i, "last_insert_id" => res["last_insert_id"].to_i }
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 { |r| { "changes" => r["changes"].to_i, "last_insert_id" => r["last_insert_id"].to_i } }
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
- fail_with("d1: #{res["error"]}") if res.key?("error")
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. Edgeway's own tests use MemoryAdapter instead, which
32
- # stores nothing.
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
- @db = Edgeway::DB::SQLiteAdapter.new(":memory:")
38
- @db.apply_migrations(File.join(Edgeway::TEST_APP_ROOT, "db/migrations"))
39
- Edgeway::Model.connection = @db
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
- @db.close
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
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Edgeway
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -0,0 +1,24 @@
1
+ module Edgeway
2
+ module View
3
+ ESCAPES = { "&" => "&amp;", "<" => "&lt;", ">" => "&gt;", '"' => "&quot;", "'" => "&#39;" }
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.1.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.17
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