hamster-web 0.1.0.pre.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/LICENSE.txt +21 -0
- data/README.md +61 -0
- data/exe/hamster +24 -0
- data/lib/hamster/build/manifest.rb +53 -0
- data/lib/hamster/build/postgres_compatibility.rb +38 -0
- data/lib/hamster/build/query_accessors.rb +73 -0
- data/lib/hamster/build/view_compiler.rb +239 -0
- data/lib/hamster/cli.rb +166 -0
- data/lib/hamster/console.rb +34 -0
- data/lib/hamster/data/persistence.rb +152 -0
- data/lib/hamster/data/query.rb +106 -0
- data/lib/hamster/data/read/compiler.rb +101 -0
- data/lib/hamster/data/read/decoder.rb +101 -0
- data/lib/hamster/data/read/nodes.rb +118 -0
- data/lib/hamster/data/read/relation.rb +198 -0
- data/lib/hamster/data/read/schema.rb +40 -0
- data/lib/hamster/data/read/validator.rb +273 -0
- data/lib/hamster/data/record.rb +81 -0
- data/lib/hamster/data/table.rb +90 -0
- data/lib/hamster/data.rb +21 -0
- data/lib/hamster/database/admin.rb +39 -0
- data/lib/hamster/database/configuration.rb +84 -0
- data/lib/hamster/database/connection.rb +168 -0
- data/lib/hamster/database/database.rb +92 -0
- data/lib/hamster/database/errors.rb +21 -0
- data/lib/hamster/database/pool.rb +98 -0
- data/lib/hamster/database/postgres.rb +66 -0
- data/lib/hamster/database/result.rb +30 -0
- data/lib/hamster/schema_validation.rb +12 -0
- data/lib/hamster/web/action/base.rb +39 -0
- data/lib/hamster/web/application.rb +23 -0
- data/lib/hamster/web/context.rb +25 -0
- data/lib/hamster/web/html.rb +80 -0
- data/lib/hamster/web/http.rb +34 -0
- data/lib/hamster/web/instrumentation.rb +28 -0
- data/lib/hamster/web/router.rb +27 -0
- data/lib/hamster/web/routing/base.rb +131 -0
- data/lib/hamster/web/version.rb +7 -0
- data/lib/hamster/web/view/form.rb +119 -0
- data/lib/hamster/web/view.rb +196 -0
- data/lib/hamster/web.rb +13 -0
- data/templates/app/.gitignore +5 -0
- data/templates/app/AGENTS.md +8 -0
- data/templates/app/Gemfile +3 -0
- data/templates/app/README.md +32 -0
- data/templates/app/app/routes/home/index/action.rb +14 -0
- data/templates/app/app/routes/home/index/view.html.erb +2 -0
- data/templates/app/app/routes/mappings.rb +8 -0
- data/templates/app/app/views/layouts/application.html.erb +3 -0
- data/templates/app/bin/hamster +4 -0
- data/templates/app/bin/server +8 -0
- data/templates/app/config/application.rb +23 -0
- data/templates/app/config/commands.rb +16 -0
- data/templates/app/config/database.rb +20 -0
- data/templates/app/config/hamster.json +1 -0
- data/templates/app/db/migrations/.keep +0 -0
- data/templates/app/spin.lock +6 -0
- data/templates/app/spin.toml +2 -0
- metadata +133 -0
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Hamster
|
|
4
|
+
module View
|
|
5
|
+
class Error < StandardError; end
|
|
6
|
+
|
|
7
|
+
def self.configure(site_name:, routes:)
|
|
8
|
+
@site_name = site_name
|
|
9
|
+
@routes = routes
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def self.route_mapping
|
|
13
|
+
@routes
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
class Context
|
|
17
|
+
def initialize(name = nil, locals = nil)
|
|
18
|
+
@name = name
|
|
19
|
+
@locals = locals
|
|
20
|
+
@slots = {}
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def fetch(*args)
|
|
24
|
+
locals, template, name, line = if args.length == 2
|
|
25
|
+
[@locals, @name, args[0], args[1]]
|
|
26
|
+
else
|
|
27
|
+
args
|
|
28
|
+
end
|
|
29
|
+
key = name.to_sym
|
|
30
|
+
return locals[key] if locals.key?(key)
|
|
31
|
+
return locals[name] if locals.key?(name)
|
|
32
|
+
|
|
33
|
+
raise Error, "#{template}:#{line}: missing local #{name}"
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def content_for(name, fragment)
|
|
37
|
+
key = name.to_s
|
|
38
|
+
@slots[key] = HTML.join([@slots[key], fragment])
|
|
39
|
+
nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def slot(name)
|
|
43
|
+
@slots[name.to_s] || HTML::Fragment.new("")
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
URL_ATTRIBUTES = %w[href src action formaction].freeze
|
|
48
|
+
TOKEN = /<%=(.*?)%>/m
|
|
49
|
+
NAME = /\A[a-z][a-z0-9_]*(?:\/[a-z][a-z0-9_]*)*\z/
|
|
50
|
+
LOCAL = /\A[a-z][a-z0-9_]*\z/
|
|
51
|
+
|
|
52
|
+
module_function
|
|
53
|
+
|
|
54
|
+
def render_compiled(name, locals = {}, layout: "layouts/application")
|
|
55
|
+
context = Context.new
|
|
56
|
+
body = CompiledViews.render(name, context, locals)
|
|
57
|
+
return body if layout.nil?
|
|
58
|
+
|
|
59
|
+
context.content_for("body", body)
|
|
60
|
+
title = context.slot("title")
|
|
61
|
+
site_name = @site_name || "Hamster"
|
|
62
|
+
full_title = title.to_s.empty? ? site_name : HTML.join([title, " · ", site_name])
|
|
63
|
+
CompiledViews.render(layout, context, title: full_title)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def append_value(output, value, name, line, position)
|
|
67
|
+
validate_url!(value, name, line) if URL_ATTRIBUTES.include?(position)
|
|
68
|
+
value = HTML.escape(value.to_s) if position.is_a?(String) && value.instance_of?(HTML::Fragment)
|
|
69
|
+
output << HTML.escape(value).to_s
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def instrument_compiled(name)
|
|
73
|
+
kind = name.start_with?("layouts/") ? "layout" : name.include?("/shared/") ? "partial" : "template"
|
|
74
|
+
Hamster::Instrumentation.instrument("view.render", attributes: {"view.template" => name, "view.kind" => kind}) { yield }
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def prepare!(sources)
|
|
78
|
+
@templates = compile(sources)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def use_compiled!
|
|
82
|
+
@templates = nil
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def validate!(sources)
|
|
86
|
+
compile(sources)
|
|
87
|
+
true
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def compile(sources)
|
|
91
|
+
prepared = {}
|
|
92
|
+
sources.each do |name, source|
|
|
93
|
+
raise Error, "invalid template name #{name}" unless name.match?(NAME)
|
|
94
|
+
instructions = []
|
|
95
|
+
cursor = 0
|
|
96
|
+
source.to_enum(:scan, TOKEN).each do
|
|
97
|
+
match = Regexp.last_match
|
|
98
|
+
prefix = source[0...match.begin(0)]
|
|
99
|
+
literal = source[cursor...match.begin(0)]
|
|
100
|
+
instructions << [:literal, literal.freeze] unless literal.empty?
|
|
101
|
+
line = prefix.count("\n") + 1
|
|
102
|
+
location = "#{name}:#{line}"
|
|
103
|
+
context = position(prefix.gsub(TOKEN, ""))
|
|
104
|
+
expression = match[1].strip
|
|
105
|
+
unless expression.match?(LOCAL)
|
|
106
|
+
raise Error, "#{location}: only <%= local %> is supported; compose partials in Ruby (got #{expression})"
|
|
107
|
+
end
|
|
108
|
+
raise Error, "#{location}: dynamic script or style content" if context == :script
|
|
109
|
+
raise Error, "#{location}: substitution must be in text or a quoted attribute" if context == :tag
|
|
110
|
+
if context.is_a?(String)
|
|
111
|
+
raise Error, "#{location}: dynamic event or style attribute" if context.start_with?("on") || context == "style" || context == "srcdoc"
|
|
112
|
+
end
|
|
113
|
+
instructions << [:value, expression.freeze, context, line].freeze
|
|
114
|
+
cursor = match.end(0)
|
|
115
|
+
end
|
|
116
|
+
tail = source[cursor..-1]
|
|
117
|
+
instructions << [:literal, tail.freeze] unless tail.empty?
|
|
118
|
+
unsupported = source.index(/<%(?!\=)/)
|
|
119
|
+
if unsupported
|
|
120
|
+
invalid_line = source[0...unsupported].count("\n") + 1
|
|
121
|
+
raise Error, "#{name}:#{invalid_line}: unsupported ERB tag; only <%= local %> is supported"
|
|
122
|
+
end
|
|
123
|
+
if tail.include?("<%")
|
|
124
|
+
invalid_line = source[0...cursor].count("\n") + tail[0...tail.index("<%")].count("\n") + 1
|
|
125
|
+
raise Error, "#{name}:#{invalid_line}: unterminated or unsupported ERB tag"
|
|
126
|
+
end
|
|
127
|
+
prepared[name.freeze] = instructions.freeze
|
|
128
|
+
end
|
|
129
|
+
prepared.freeze
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def position(prefix)
|
|
133
|
+
script_tag = prefix.scan(%r{</?(?:script|style)\b[^>]*>}i).last
|
|
134
|
+
return :script if script_tag && !script_tag.start_with?("</")
|
|
135
|
+
|
|
136
|
+
open = prefix.rindex("<")
|
|
137
|
+
close = prefix.rindex(">")
|
|
138
|
+
if open && (!close || open > close)
|
|
139
|
+
tag = prefix[open..-1]
|
|
140
|
+
match = tag.match(/([a-zA-Z][a-zA-Z0-9:-]*)\s*=\s*(["'])[^"']*\z/)
|
|
141
|
+
return match[1].downcase if match
|
|
142
|
+
return :tag
|
|
143
|
+
end
|
|
144
|
+
:text
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def render(name, locals = {}, kind = nil, **options)
|
|
148
|
+
if RUBY_ENGINE == "ruby"
|
|
149
|
+
return render_interpreted(name, locals, kind, **options) unless @templates.nil?
|
|
150
|
+
end
|
|
151
|
+
layout = options.delete(:layout) { "layouts/application" }
|
|
152
|
+
render_compiled(name, locals.merge(options), layout: layout)
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
def render_interpreted(name, locals = {}, kind = nil, **options)
|
|
156
|
+
if @templates.nil? && defined?(Hamster::CompiledViews)
|
|
157
|
+
layout = options.delete(:layout) { "layouts/application" }
|
|
158
|
+
locals = locals.merge(options)
|
|
159
|
+
return render_compiled(name, locals, layout: layout)
|
|
160
|
+
end
|
|
161
|
+
locals = locals.merge(options) unless options.empty?
|
|
162
|
+
instructions = @templates && @templates[name]
|
|
163
|
+
raise Error, "missing template #{name}" unless instructions
|
|
164
|
+
|
|
165
|
+
work = proc do
|
|
166
|
+
context = Context.new(name, locals)
|
|
167
|
+
output = instructions.map do |instruction|
|
|
168
|
+
case instruction[0]
|
|
169
|
+
when :literal then instruction[1]
|
|
170
|
+
when :value
|
|
171
|
+
value = context.fetch(instruction[1], instruction[3])
|
|
172
|
+
validate_url!(value, name, instruction[3]) if URL_ATTRIBUTES.include?(instruction[2])
|
|
173
|
+
if instruction[2].is_a?(String) && value.instance_of?(HTML::Fragment)
|
|
174
|
+
HTML.escape(value.to_s).to_s
|
|
175
|
+
else
|
|
176
|
+
HTML.escape(value).to_s
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end.join
|
|
180
|
+
HTML::Fragment.new(output)
|
|
181
|
+
end
|
|
182
|
+
kind ||= name.start_with?("layouts/") ? "layout" : "template"
|
|
183
|
+
Hamster::Instrumentation.instrument("view.render", attributes: {"view.template" => name, "view.kind" => kind}) { work.call }
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def validate_url!(value, name, line)
|
|
187
|
+
url = value.to_s
|
|
188
|
+
raise Error, "#{name}:#{line}: unsafe URL" unless url.start_with?("/") && !url.start_with?("//") && !url.match?(/[\x00-\x20\\<>"']/)
|
|
189
|
+
end
|
|
190
|
+
|
|
191
|
+
def path(name, params = {}, query: {}, **keywords)
|
|
192
|
+
raise Error, "route mapping is not configured" unless @routes
|
|
193
|
+
@routes.path(name, params, query: query, **keywords)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
data/lib/hamster/web.rb
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "web/version"
|
|
4
|
+
require_relative "web/instrumentation"
|
|
5
|
+
require_relative "web/routing/base"
|
|
6
|
+
require_relative "web/view"
|
|
7
|
+
require_relative "web/html"
|
|
8
|
+
require_relative "web/view/form"
|
|
9
|
+
require_relative "web/http"
|
|
10
|
+
require_relative "web/action/base"
|
|
11
|
+
require_relative "web/context"
|
|
12
|
+
require_relative "web/router"
|
|
13
|
+
require_relative "web/application"
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Application conventions
|
|
2
|
+
|
|
3
|
+
- Keep one action per endpoint directory with a sibling view.html.erb.
|
|
4
|
+
- Declare routes in app/routes/mappings.rb.
|
|
5
|
+
- App Ruby files load through config/manifest.rb; do not add local requires.
|
|
6
|
+
- Run bin/hamster manifest after editing application Ruby files or templates.
|
|
7
|
+
- Run bin/hamster manifest:check before building.
|
|
8
|
+
- Keep build-time code out of the Spinel runtime require chain.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# __APP_NAME__
|
|
2
|
+
|
|
3
|
+
Run `bin/hamster setup` to install gems and vendor the locked Spin PostgreSQL driver.
|
|
4
|
+
Spin and Spinel must already be installed. Configure DATABASE_USERNAME,
|
|
5
|
+
DATABASE_PASSWORD, and optionally DATABASE_HOST and DATABASE_PORT for your local
|
|
6
|
+
PostgreSQL server. DATABASE_NAME defaults to __APP_NAME___development.
|
|
7
|
+
|
|
8
|
+
Setup and build apply three string-copy compatibility fixes to the pinned
|
|
9
|
+
pg client for Spinel. The fixes are repeatable after spin vendor and reject
|
|
10
|
+
an unknown driver revision; avoid editing vendored driver sources manually.
|
|
11
|
+
|
|
12
|
+
Run `bin/hamster db:create`, `bin/hamster db:migrate`, `bin/hamster build`,
|
|
13
|
+
then `bin/hamster serve`. Build does not require a database connection.
|
|
14
|
+
An empty migration directory is valid; db:migrate initializes migration tracking.
|
|
15
|
+
The server listens on http://127.0.0.1:4000 by default; configure HOST and PORT
|
|
16
|
+
to override it. During framework development, install the local hamster-web
|
|
17
|
+
gem before running setup.
|
|
18
|
+
|
|
19
|
+
Run a deployed binary from the application root or set HAMSTER_APP_ROOT.
|
|
20
|
+
Include db/migrations alongside the binary. PostgreSQL TLS is not supported
|
|
21
|
+
by the current driver integration.
|
|
22
|
+
|
|
23
|
+
Use `bin/hamster console` (alias `c`) for an IRB session with app, application,
|
|
24
|
+
and database variables and a connection scope for record writes and queries.
|
|
25
|
+
Use `bin/hamster db:validate` to check model table/column declarations against
|
|
26
|
+
PostgreSQL without applying migrations. These commands use config/commands.rb,
|
|
27
|
+
which is deliberately excluded from the compiled server's boot chain.
|
|
28
|
+
|
|
29
|
+
Each endpoint has an action.rb and optional view.html.erb in app/routes/.
|
|
30
|
+
Declare endpoints centrally in app/routes/mappings.rb. Run
|
|
31
|
+
`bin/hamster manifest` after changing Ruby sources or templates and commit
|
|
32
|
+
the generated manifest and view registry with your source changes.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
require "hamster/web"
|
|
3
|
+
require_relative "database"
|
|
4
|
+
require_relative "manifest"
|
|
5
|
+
|
|
6
|
+
module __APP_MODULE__
|
|
7
|
+
def self.build
|
|
8
|
+
Hamster::View.configure(site_name: "__APP_NAME__", routes: Routes::Mappings)
|
|
9
|
+
database = Hamster::Database.new(database_configuration, migrations_path: File.join(root, "db/migrations"))
|
|
10
|
+
begin
|
|
11
|
+
database.verify!
|
|
12
|
+
Hamster::Application.new(routes: Routes::Mappings, database: database)
|
|
13
|
+
rescue StandardError
|
|
14
|
+
database.shutdown
|
|
15
|
+
raise
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def self.validate_queries!
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
Hamster::View.configure(site_name: "__APP_NAME__", routes: __APP_MODULE__::Routes::Mappings)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
# CRuby command adapter; keep it out of the compiled server require chain.
|
|
3
|
+
require_relative "application"
|
|
4
|
+
|
|
5
|
+
module Hamster
|
|
6
|
+
module ApplicationCommands
|
|
7
|
+
def self.build
|
|
8
|
+
__APP_MODULE__.build
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def self.database
|
|
12
|
+
Hamster::Database.new(__APP_MODULE__.database_configuration,
|
|
13
|
+
migrations_path: File.join(__APP_MODULE__.root, "db/migrations"))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
if RUBY_ENGINE == "ruby"
|
|
4
|
+
$LOAD_PATH.unshift(File.expand_path("../vendor/packages/pg-0.2.0", __dir__))
|
|
5
|
+
end
|
|
6
|
+
require "hamster/database/postgres"
|
|
7
|
+
|
|
8
|
+
module __APP_MODULE__
|
|
9
|
+
def self.root
|
|
10
|
+
if RUBY_ENGINE == "ruby"
|
|
11
|
+
File.expand_path("..", __dir__)
|
|
12
|
+
else
|
|
13
|
+
ENV.fetch("HAMSTER_APP_ROOT", Dir.pwd)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def self.database_configuration
|
|
18
|
+
Hamster::Database::Configuration.from_env(app_name: "__APP_NAME__")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"name":"__APP_NAME__","namespace":"__APP_MODULE__"}
|
|
File without changes
|
metadata
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: hamster-web
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0.pre.2
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Dillon Wyatt
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: spinel-spinner
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - '='
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: 0.1.0.pre.1
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - '='
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: 0.1.0.pre.1
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: irb
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - ">="
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.13'
|
|
33
|
+
- - "<"
|
|
34
|
+
- !ruby/object:Gem::Version
|
|
35
|
+
version: '2'
|
|
36
|
+
type: :runtime
|
|
37
|
+
prerelease: false
|
|
38
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
39
|
+
requirements:
|
|
40
|
+
- - ">="
|
|
41
|
+
- !ruby/object:Gem::Version
|
|
42
|
+
version: '1.13'
|
|
43
|
+
- - "<"
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '2'
|
|
46
|
+
description: Hamster provides a compiled Spinel web runtime and a CLI for generating
|
|
47
|
+
applications.
|
|
48
|
+
email:
|
|
49
|
+
- dostaglou@gmail.com
|
|
50
|
+
executables:
|
|
51
|
+
- hamster
|
|
52
|
+
extensions: []
|
|
53
|
+
extra_rdoc_files: []
|
|
54
|
+
files:
|
|
55
|
+
- LICENSE.txt
|
|
56
|
+
- README.md
|
|
57
|
+
- exe/hamster
|
|
58
|
+
- lib/hamster/build/manifest.rb
|
|
59
|
+
- lib/hamster/build/postgres_compatibility.rb
|
|
60
|
+
- lib/hamster/build/query_accessors.rb
|
|
61
|
+
- lib/hamster/build/view_compiler.rb
|
|
62
|
+
- lib/hamster/cli.rb
|
|
63
|
+
- lib/hamster/console.rb
|
|
64
|
+
- lib/hamster/data.rb
|
|
65
|
+
- lib/hamster/data/persistence.rb
|
|
66
|
+
- lib/hamster/data/query.rb
|
|
67
|
+
- lib/hamster/data/read/compiler.rb
|
|
68
|
+
- lib/hamster/data/read/decoder.rb
|
|
69
|
+
- lib/hamster/data/read/nodes.rb
|
|
70
|
+
- lib/hamster/data/read/relation.rb
|
|
71
|
+
- lib/hamster/data/read/schema.rb
|
|
72
|
+
- lib/hamster/data/read/validator.rb
|
|
73
|
+
- lib/hamster/data/record.rb
|
|
74
|
+
- lib/hamster/data/table.rb
|
|
75
|
+
- lib/hamster/database/admin.rb
|
|
76
|
+
- lib/hamster/database/configuration.rb
|
|
77
|
+
- lib/hamster/database/connection.rb
|
|
78
|
+
- lib/hamster/database/database.rb
|
|
79
|
+
- lib/hamster/database/errors.rb
|
|
80
|
+
- lib/hamster/database/pool.rb
|
|
81
|
+
- lib/hamster/database/postgres.rb
|
|
82
|
+
- lib/hamster/database/result.rb
|
|
83
|
+
- lib/hamster/schema_validation.rb
|
|
84
|
+
- lib/hamster/web.rb
|
|
85
|
+
- lib/hamster/web/action/base.rb
|
|
86
|
+
- lib/hamster/web/application.rb
|
|
87
|
+
- lib/hamster/web/context.rb
|
|
88
|
+
- lib/hamster/web/html.rb
|
|
89
|
+
- lib/hamster/web/http.rb
|
|
90
|
+
- lib/hamster/web/instrumentation.rb
|
|
91
|
+
- lib/hamster/web/router.rb
|
|
92
|
+
- lib/hamster/web/routing/base.rb
|
|
93
|
+
- lib/hamster/web/version.rb
|
|
94
|
+
- lib/hamster/web/view.rb
|
|
95
|
+
- lib/hamster/web/view/form.rb
|
|
96
|
+
- templates/app/.gitignore
|
|
97
|
+
- templates/app/AGENTS.md
|
|
98
|
+
- templates/app/Gemfile
|
|
99
|
+
- templates/app/README.md
|
|
100
|
+
- templates/app/app/routes/home/index/action.rb
|
|
101
|
+
- templates/app/app/routes/home/index/view.html.erb
|
|
102
|
+
- templates/app/app/routes/mappings.rb
|
|
103
|
+
- templates/app/app/views/layouts/application.html.erb
|
|
104
|
+
- templates/app/bin/hamster
|
|
105
|
+
- templates/app/bin/server
|
|
106
|
+
- templates/app/config/application.rb
|
|
107
|
+
- templates/app/config/commands.rb
|
|
108
|
+
- templates/app/config/database.rb
|
|
109
|
+
- templates/app/config/hamster.json
|
|
110
|
+
- templates/app/db/migrations/.keep
|
|
111
|
+
- templates/app/spin.lock
|
|
112
|
+
- templates/app/spin.toml
|
|
113
|
+
licenses:
|
|
114
|
+
- MIT
|
|
115
|
+
metadata: {}
|
|
116
|
+
rdoc_options: []
|
|
117
|
+
require_paths:
|
|
118
|
+
- lib
|
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - ">="
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '3.3'
|
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - ">="
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '0'
|
|
129
|
+
requirements: []
|
|
130
|
+
rubygems_version: 4.0.23
|
|
131
|
+
specification_version: 4
|
|
132
|
+
summary: A Spinel web framework and application generator
|
|
133
|
+
test_files: []
|