fresco 0.0.1

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 (48) hide show
  1. checksums.yaml +7 -0
  2. data/exe/fresco +3 -0
  3. data/lib/fresco/application.rb +12 -0
  4. data/lib/fresco/cli/build.rb +682 -0
  5. data/lib/fresco/cli/dev.rb +17 -0
  6. data/lib/fresco/cli/dev_loop.rb +815 -0
  7. data/lib/fresco/cli/new.rb +120 -0
  8. data/lib/fresco/cli/release.rb +76 -0
  9. data/lib/fresco/cli.rb +56 -0
  10. data/lib/fresco/database_config.rb +34 -0
  11. data/lib/fresco/generators/app/Gemfile.tt +18 -0
  12. data/lib/fresco/generators/app/README.md.tt +32 -0
  13. data/lib/fresco/generators/app/app/action.rb.tt +20 -0
  14. data/lib/fresco/generators/app/app/actions/root_path.rb.tt +5 -0
  15. data/lib/fresco/generators/app/app/views/layouts/application.html.erb +29 -0
  16. data/lib/fresco/generators/app/app/views/root_path.html.erb +8 -0
  17. data/lib/fresco/generators/app/app.rb.tt +15 -0
  18. data/lib/fresco/generators/app/bin/build +2 -0
  19. data/lib/fresco/generators/app/bin/dev +2 -0
  20. data/lib/fresco/generators/app/bin/release +2 -0
  21. data/lib/fresco/generators/app/config/app.rb.tt +26 -0
  22. data/lib/fresco/generators/app/config/database.rb +17 -0
  23. data/lib/fresco/generators/app/config/routes.rb +11 -0
  24. data/lib/fresco/generators/app/db/schema.rb +14 -0
  25. data/lib/fresco/generators/app/public/404.html +87 -0
  26. data/lib/fresco/generators/app/public/500.html +84 -0
  27. data/lib/fresco/migration_builder.rb +55 -0
  28. data/lib/fresco/model_builder.rb +54 -0
  29. data/lib/fresco/paths.rb +20 -0
  30. data/lib/fresco/router.rb +67 -0
  31. data/lib/fresco/runtime/boot.rb +34 -0
  32. data/lib/fresco/runtime/db_postgres.rb +403 -0
  33. data/lib/fresco/runtime/db_sqlite.rb +495 -0
  34. data/lib/fresco/runtime/http.c +456 -0
  35. data/lib/fresco/runtime/postgres.c +339 -0
  36. data/lib/fresco/runtime/runtime.rb +1810 -0
  37. data/lib/fresco/runtime/sqlite.c +220 -0
  38. data/lib/fresco/runtime/welcome.rb +152 -0
  39. data/lib/fresco/schema_builder.rb +71 -0
  40. data/lib/fresco/templates/dispatch.rb.erb +32 -0
  41. data/lib/fresco/templates/layout_dispatch.rb.erb +16 -0
  42. data/lib/fresco/templates/manifest.rb.erb +5 -0
  43. data/lib/fresco/templates/migrations.rb.erb +152 -0
  44. data/lib/fresco/templates/model.rb.erb +223 -0
  45. data/lib/fresco/templates/view.rb.erb +5 -0
  46. data/lib/fresco/version.rb +3 -0
  47. data/lib/fresco.rb +61 -0
  48. metadata +115 -0
@@ -0,0 +1,223 @@
1
+ # Fresco: <%= model_name %> model. Auto-generated by bin/build from
2
+ # db/schema.rb + app/models/<%= file_stem %>.rb. Do not edit.
3
+ #
4
+ # Source of truth lives in lib/templates/model.rb.erb; per-build
5
+ # SQL strings are interpolated for the configured adapter
6
+ # (<%= adapter %>) below.
7
+ #
8
+ # Spinel-shape contract for every generated method:
9
+ # - Positional + defaulted params everywhere; no required kwargs.
10
+ # - Attribute readers return one type per column (no nullable
11
+ # widening — nullable cols return the zero value, matching the
12
+ # Tep / Fresco convention from M1).
13
+ # - Finders return Array<<%= model_name %>>. The Array is seeded
14
+ # with a fresh instance and immediately cleared so Spinel pins
15
+ # it to the typed Array class on assignment.
16
+ # - .find always returns a <%= model_name %> instance. Missing rows
17
+ # come back with `found?` false rather than `nil` — return-type
18
+ # unions widen the whole dispatch chain to RbVal.
19
+ # - Prepared-statement cache (M6): every method uses
20
+ # `Fresco::Db.cached_prepare` and DOES NOT call `finalize`. The
21
+ # cursor lives for the process lifetime; subsequent calls reset
22
+ # instead of re-preparing. Ad-hoc handler code outside the model
23
+ # layer still uses `prepare` + `finalize` as before.
24
+
25
+ class <%= model_name %>
26
+ <% columns.each do |c| -%>
27
+ attr_reader :<%= c[:name] %>
28
+ <% end -%>
29
+ attr_reader :found
30
+
31
+ # Positional ctor — kwargs without defaults collapse to mrb_int
32
+ # under Spinel (see [[spinel_initialize_kwargs]]), so we default
33
+ # every param to the zero value of its column type. The `found`
34
+ # flag distinguishes a hydrated row from a default-constructed
35
+ # placeholder returned by `.find` when the id misses.
36
+ def initialize(<%= ctor_params %>)
37
+ <% columns.each do |c| -%>
38
+ @<%= c[:name] %> = <%= c[:name] %>
39
+ <% end -%>
40
+ @found = found
41
+ end
42
+
43
+ def found?
44
+ @found
45
+ end
46
+
47
+ # ---- Class-level finders ----------------------------------------
48
+
49
+ # Single-row lookup by primary key. Returns a <%= model_name %>
50
+ # instance; check `.found?` to distinguish hit from miss.
51
+ def self.find(<%= pk_param %>)
52
+ cid = Fresco::Db.cached_prepare("<%= select_by_pk_sql %>")
53
+ if cid < 0
54
+ return <%= model_name %>.new
55
+ end
56
+ Fresco::Db.bind_int(cid, 1, <%= pk_name %>, "<%= pk_name %>")
57
+ if Fresco::Db.step(cid) == 1
58
+ return <%= model_name %>.new(<%= row_ctor_args %>, true)
59
+ end
60
+ <%= model_name %>.new
61
+ end
62
+
63
+ # Fetch every row in <%= table_name %>. Ordered by primary key for
64
+ # deterministic output across calls.
65
+ def self.all
66
+ out = [<%= model_name %>.new]
67
+ out.delete_at(0)
68
+ cid = Fresco::Db.cached_prepare("<%= select_all_sql %>")
69
+ return out if cid < 0
70
+ while Fresco::Db.step(cid) == 1
71
+ out.push(<%= model_name %>.new(<%= row_ctor_args %>, true))
72
+ end
73
+ out
74
+ end
75
+
76
+ <% finders.each do |f| -%>
77
+ # `where_<%= f[:name] %>` — generated because `finder :<%= f[:name] %>`
78
+ # was declared in app/models/<%= file_stem %>.rb. Returns every
79
+ # matching row as Array<<%= model_name %>>.
80
+ def self.where_<%= f[:name] %>(<%= f[:name] %> = <%= f[:default] %>)
81
+ out = [<%= model_name %>.new]
82
+ out.delete_at(0)
83
+ cid = Fresco::Db.cached_prepare("<%= f[:sql] %>")
84
+ return out if cid < 0
85
+ <%= f[:bind_call] %>
86
+ while Fresco::Db.step(cid) == 1
87
+ out.push(<%= model_name %>.new(<%= row_ctor_args %>, true))
88
+ end
89
+ out
90
+ end
91
+
92
+ # `count_where_<%= f[:name] %>` — row count for the same predicate
93
+ # as where_<%= f[:name] %>. Cheaper than materialising the full
94
+ # Array when you only need the size.
95
+ def self.count_where_<%= f[:name] %>(<%= f[:name] %> = <%= f[:default] %>)
96
+ cid = Fresco::Db.cached_prepare("<%= f[:count_sql] %>")
97
+ return 0 if cid < 0
98
+ <%= f[:bind_call] %>
99
+ n = 0
100
+ if Fresco::Db.step(cid) == 1
101
+ n = Fresco::Db.col_int(cid, 0)
102
+ end
103
+ n
104
+ end
105
+
106
+ <% if f[:unique] -%>
107
+ # `find_by_<%= f[:name] %>` — emitted because the column is declared
108
+ # `index: :unique` in db/schema.rb. Same single-row contract as
109
+ # `find`: always returns a <%= model_name %> instance; check
110
+ # `.found?` to distinguish hit from miss.
111
+ def self.find_by_<%= f[:name] %>(<%= f[:name] %> = <%= f[:default] %>)
112
+ cid = Fresco::Db.cached_prepare("<%= f[:sql] %>")
113
+ if cid < 0
114
+ return <%= model_name %>.new
115
+ end
116
+ <%= f[:bind_call] %>
117
+ if Fresco::Db.step(cid) == 1
118
+ return <%= model_name %>.new(<%= row_ctor_args %>, true)
119
+ end
120
+ <%= model_name %>.new
121
+ end
122
+
123
+ <% end -%>
124
+ <% end -%>
125
+
126
+ # Total row count for the table. Returns 0 on a fresh DB or if the
127
+ # connection isn't usable.
128
+ def self.count
129
+ cid = Fresco::Db.cached_prepare("<%= count_sql %>")
130
+ return 0 if cid < 0
131
+ n = 0
132
+ if Fresco::Db.step(cid) == 1
133
+ n = Fresco::Db.col_int(cid, 0)
134
+ end
135
+ n
136
+ end
137
+
138
+
139
+ # `insert(<%= insert_kwarg_signature %>)` — class method to create a
140
+ # new row. Returns the new primary-key id (or -1 on failure). The
141
+ # ergonomic kwargs form converts to positional internally so the
142
+ # underlying call stays monomorphic.
143
+ def self.insert(<%= insert_kwargs %>)
144
+ <% validators.each do |v| -%>
145
+ # validates :<%= v[:column] %>, presence: true
146
+ return -1 if <%= v[:guard] %>
147
+ <% end -%>
148
+ cid = Fresco::Db.cached_prepare("<%= insert_sql %>")
149
+ return -1 if cid < 0
150
+ <% insert_bind_lines.each do |line| -%>
151
+ <%= line %>
152
+ <% end -%>
153
+ <% if adapter == :postgres -%>
154
+ new_id = -1
155
+ if Fresco::Db.step(cid) == 1
156
+ new_id = Fresco::Db.col_int(cid, 0)
157
+ end
158
+ new_id
159
+ <% else -%>
160
+ Fresco::Db.step(cid)
161
+ Fresco::Db.last_rowid
162
+ <% end -%>
163
+ end
164
+
165
+ # `Model.query(sql, [bindings])` — escape hatch for one-off
166
+ # queries the codegen doesn't cover. Bindings is an Array of Str;
167
+ # bind_int callers should `.to_s` first. Returns Array<<%= model_name %>>.
168
+ # Each row materialises through the same ctor as the generated
169
+ # finders, so column count + order must match the SELECT.
170
+ #
171
+ # The escape hatch uses `prepare` + `finalize` rather than the
172
+ # cache — query text is caller-supplied and may be unique per call,
173
+ # so caching it would just fill the slot table with cold entries.
174
+ def self.query(sql = "", bindings = [""])
175
+ out = [<%= model_name %>.new]
176
+ out.delete_at(0)
177
+ cid = Fresco::Db.prepare(sql)
178
+ return out if cid < 0
179
+ i = 0
180
+ while i < bindings.length
181
+ Fresco::Db.bind_str(cid, i + 1, bindings[i])
182
+ i += 1
183
+ end
184
+ while Fresco::Db.step(cid) == 1
185
+ out.push(<%= model_name %>.new(<%= row_ctor_args %>, true))
186
+ end
187
+ Fresco::Db.finalize(cid)
188
+ out
189
+ end
190
+
191
+ # ---- Instance methods -------------------------------------------
192
+
193
+ # `user.update(<%= update_kwarg_signature %>)` — overwrite every
194
+ # column on the row matching @<%= pk_name %>. Returns true on
195
+ # success. Caller-supplied values fully replace existing ones;
196
+ # there is no partial-update form in M3 (each generated finder
197
+ # needs static SQL — see [[spinel_poly_hash_aset]]).
198
+ def update(<%= update_kwargs %>)
199
+ <% validators.each do |v| -%>
200
+ # validates :<%= v[:column] %>, presence: true
201
+ return false if <%= v[:guard] %>
202
+ <% end -%>
203
+ cid = Fresco::Db.cached_prepare("<%= update_sql %>")
204
+ return false if cid < 0
205
+ <% update_bind_lines.each do |line| -%>
206
+ <%= line %>
207
+ <% end -%>
208
+ Fresco::Db.bind_int(cid, <%= update_bind_lines.length + 1 %>, @<%= pk_name %>, "<%= pk_name %>")
209
+ Fresco::Db.step(cid)
210
+ true
211
+ end
212
+
213
+ # `user.delete` — remove the row matching @<%= pk_name %>. Returns
214
+ # true on success. No cascade; foreign-key cascades are an M4+
215
+ # schema concern.
216
+ def delete
217
+ cid = Fresco::Db.cached_prepare("<%= delete_sql %>")
218
+ return false if cid < 0
219
+ Fresco::Db.bind_int(cid, 1, @<%= pk_name %>, "<%= pk_name %>")
220
+ Fresco::Db.step(cid)
221
+ true
222
+ end
223
+ end
@@ -0,0 +1,5 @@
1
+ # Fresco: compiled view <%= rel %>. Auto-generated by bin/build; do not edit.
2
+
3
+ def render_<%= name %><%= signature %>
4
+ <%= src %>
5
+ end
@@ -0,0 +1,3 @@
1
+ module Fresco
2
+ VERSION = "0.0.1"
3
+ end
data/lib/fresco.rb ADDED
@@ -0,0 +1,61 @@
1
+ # Fresco: build-time entry point. Required by `fresco build` before
2
+ # config/routes.rb is evaluated. The DSL captured here is read back
3
+ # out as data for codegen — nothing in this file runs at runtime,
4
+ # so it can use ordinary Ruby (instance_eval, blocks, etc.) without
5
+ # the Spinel-subset constraints user code has to live within.
6
+ #
7
+ # Apps consume the gem (`require "fresco"`) and never load the internal
8
+ # files directly; everything below is the public DSL surface.
9
+
10
+ require "fresco/version"
11
+ require "fresco/paths"
12
+
13
+ module Fresco
14
+ def self.app
15
+ @app ||= Application.new
16
+ end
17
+
18
+ # --- Build-time naming helpers ----------------------------------
19
+ #
20
+ # File paths under app/actions/ map to Ruby constants by segment:
21
+ # "users/show" → "Users::Show", "admin/api/sessions/new" →
22
+ # "Admin::Api::Sessions::New". Each path segment is split on
23
+ # underscores and CamelCased; "::" joins the segments back.
24
+ #
25
+ # `const_set_path` exists because `Object.const_set` only accepts
26
+ # a bare name — passing "Users::Show" raises. We walk the
27
+ # namespace, creating Module shells where missing, then set the
28
+ # leaf constant on the resolved parent.
29
+
30
+ def self.path_to_class(file, root)
31
+ rel = file.sub(%r{\A#{Regexp.escape(root)}/?}, "").sub(/\.rb\z/, "")
32
+ rel.split("/").map { |seg| seg.split("_").map(&:capitalize).join }.join("::")
33
+ end
34
+
35
+ def self.class_to_path(name)
36
+ name.split("::").map { |seg| seg.gsub(/([a-z0-9])([A-Z])/, '\1_\2').downcase }.join("/")
37
+ end
38
+
39
+ def self.const_set_path(path, value)
40
+ parts = path.split("::")
41
+ parent = parts[0...-1].inject(Object) do |mod, seg|
42
+ if mod.const_defined?(seg, false)
43
+ mod.const_get(seg, false)
44
+ else
45
+ mod.const_set(seg, Module.new)
46
+ end
47
+ end
48
+ parent.const_set(parts.last, value) unless parent.const_defined?(parts.last, false)
49
+ end
50
+
51
+ def self.const_get_path(path)
52
+ path.split("::").inject(Object) { |mod, seg| mod.const_get(seg, false) }
53
+ end
54
+ end
55
+
56
+ require "fresco/application"
57
+ require "fresco/router"
58
+ require "fresco/database_config"
59
+ require "fresco/schema_builder"
60
+ require "fresco/model_builder"
61
+ require "fresco/migration_builder"
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fresco
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Andrea Fomera
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: herb
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - ">="
24
+ - !ruby/object:Gem::Version
25
+ version: '0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: base64
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ description: Build-time DSL (routes, schema, models, migrations), codegen pipeline,
41
+ CRuby dev loop, and a `fresco new` generator. Targets Spinel for release builds.
42
+ email:
43
+ - afomera@hey.com
44
+ executables:
45
+ - fresco
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - exe/fresco
50
+ - lib/fresco.rb
51
+ - lib/fresco/application.rb
52
+ - lib/fresco/cli.rb
53
+ - lib/fresco/cli/build.rb
54
+ - lib/fresco/cli/dev.rb
55
+ - lib/fresco/cli/dev_loop.rb
56
+ - lib/fresco/cli/new.rb
57
+ - lib/fresco/cli/release.rb
58
+ - lib/fresco/database_config.rb
59
+ - lib/fresco/generators/app/Gemfile.tt
60
+ - lib/fresco/generators/app/README.md.tt
61
+ - lib/fresco/generators/app/app.rb.tt
62
+ - lib/fresco/generators/app/app/action.rb.tt
63
+ - lib/fresco/generators/app/app/actions/root_path.rb.tt
64
+ - lib/fresco/generators/app/app/views/layouts/application.html.erb
65
+ - lib/fresco/generators/app/app/views/root_path.html.erb
66
+ - lib/fresco/generators/app/bin/build
67
+ - lib/fresco/generators/app/bin/dev
68
+ - lib/fresco/generators/app/bin/release
69
+ - lib/fresco/generators/app/config/app.rb.tt
70
+ - lib/fresco/generators/app/config/database.rb
71
+ - lib/fresco/generators/app/config/routes.rb
72
+ - lib/fresco/generators/app/db/schema.rb
73
+ - lib/fresco/generators/app/public/404.html
74
+ - lib/fresco/generators/app/public/500.html
75
+ - lib/fresco/migration_builder.rb
76
+ - lib/fresco/model_builder.rb
77
+ - lib/fresco/paths.rb
78
+ - lib/fresco/router.rb
79
+ - lib/fresco/runtime/boot.rb
80
+ - lib/fresco/runtime/db_postgres.rb
81
+ - lib/fresco/runtime/db_sqlite.rb
82
+ - lib/fresco/runtime/http.c
83
+ - lib/fresco/runtime/postgres.c
84
+ - lib/fresco/runtime/runtime.rb
85
+ - lib/fresco/runtime/sqlite.c
86
+ - lib/fresco/runtime/welcome.rb
87
+ - lib/fresco/schema_builder.rb
88
+ - lib/fresco/templates/dispatch.rb.erb
89
+ - lib/fresco/templates/layout_dispatch.rb.erb
90
+ - lib/fresco/templates/manifest.rb.erb
91
+ - lib/fresco/templates/migrations.rb.erb
92
+ - lib/fresco/templates/model.rb.erb
93
+ - lib/fresco/templates/view.rb.erb
94
+ - lib/fresco/version.rb
95
+ licenses:
96
+ - MIT
97
+ metadata: {}
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: 3.0.0
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubygems_version: 4.0.10
113
+ specification_version: 4
114
+ summary: Fresco — an application framework that compiles to a static binary via Spinel.
115
+ test_files: []