mk_framework 0.2.2 → 0.2.3

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 116dd1b29e32f3a469fc2806aab54c975373ea40188a91025e89d4ba2a3a45cd
4
- data.tar.gz: 38ba4c1925e8be343c384e527d17ec0509082f24e4ddbd891f016cdf1ea5f1f4
3
+ metadata.gz: c68f913eebaf1aa8a9b4d423b528e4720a6508b5f0f130c03b15d039458ec6d1
4
+ data.tar.gz: be858c42ce6d158eb5e658bb9a7e20ec9a775795f0f5956b03100ee733dd4c2f
5
5
  SHA512:
6
- metadata.gz: b1756e15b4f7ae6c8c5dffdd121ecbd632a264ab7a539cb99b9b2f7158cd83cf57c436f1a03da46bc156ad7f0a0e10dc7575982f4e934e64749cffdf617c3294
7
- data.tar.gz: 5766d0fd1acd60b0ff39868145c196b07afdb758e11319d7a5c9c75c4f0e27f65bcb70aa0add146818956237324d08a97b37a30c67ad7c2aaa4de67e441fdb9d
6
+ metadata.gz: 79a22c1dcd019cfcb8ad3653a9fdd84c4246de7cdc95faa21132be72dc1306285c49bc5a37f29a3bdd64342718024ac126fcf33e7fe38dcdbde673dca3e73536
7
+ data.tar.gz: f42544ff916968bf3cfc5152b9ed3cd866050b301407e5f1b840637db4b73226f1a3a604b36102b0195b8a9e584c4e7ab90cb590afee744bf9462b1b0ac79538
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.3 — 2026-09-12
4
+
5
+ - Use `rack-test` and `Rack::Test::Methods` in generated request specs.
6
+ - Simplify generated update specs to use PUT and serialize request bodies with
7
+ `.to_json`.
8
+ - Format generated Ruby hashes with spaces inside nonempty braces.
9
+
3
10
  ## 0.2.2 — 2026-09-08
4
11
 
5
12
  - Generate full CRUD resources: index, show, create, update (PATCH and PUT), and
data/README.md CHANGED
@@ -21,14 +21,14 @@ require `mk_framework/sequel`.
21
21
  ## Install
22
22
 
23
23
  ```sh
24
- gem install mk_framework -v 0.2.2
24
+ gem install mk_framework -v 0.2.3
25
25
  ```
26
26
 
27
27
  Or add it to your application's Gemfile:
28
28
 
29
29
  ```ruby
30
30
  source 'https://rubygems.org'
31
- gem 'mk_framework', '~> 0.2.2'
31
+ gem 'mk_framework', '~> 0.2.3'
32
32
  ```
33
33
 
34
34
  Run `bundle install`. Add `sequel` and your database driver if you use
@@ -36,11 +36,11 @@ Run `bundle install`. Add `sequel` and your database driver if you use
36
36
 
37
37
  ## Generate an app with `mk_frame_init`
38
38
 
39
- Version 0.2.2 generates full CRUD apps with the `mk_frame_init` executable. Install the gem, then run
39
+ Version 0.2.3 generates full CRUD apps with the `mk_frame_init` executable. Install the gem, then run
40
40
  it from the parent directory where you want your new app:
41
41
 
42
42
  ```sh
43
- gem install mk_framework -v 0.2.2
43
+ gem install mk_framework -v 0.2.3
44
44
  mk_frame_init
45
45
  ```
46
46
 
@@ -632,7 +632,7 @@ bundle exec rake
632
632
  bundle exec rake build
633
633
  ```
634
634
 
635
- The gem is written to `pkg/mk_framework-0.2.2.gem`. See
635
+ The gem is written to `pkg/mk_framework-0.2.3.gem`. See
636
636
  [deployment](docs/deployment.md) for migrations, connections, authentication,
637
637
  timeouts, logging, and release verification, and [upgrading](docs/upgrading.md)
638
638
  for changes from the prototype. CI runs on Ruby 4.0 on Linux.
@@ -12,7 +12,7 @@ Each action has its own controller and handler.
12
12
  | DELETE | `/<%= config.resource %>/:id` | delete |
13
13
 
14
14
  Create returns 201; the other actions return 200. Index returns
15
- `{<%= config.resource %>: [...]}`; member actions return `{<%= config.model_name %>: {...}}`,
15
+ `{ <%= config.resource %>: [...] }`; member actions return `{ <%= config.model_name %>: { ... } }`,
16
16
  including the deleted record for DELETE. Missing records return 404.
17
17
 
18
18
  ## Setup
@@ -7,9 +7,9 @@ module <%= config.namespace %>
7
7
  r.response.status = 201
8
8
  <% end -%>
9
9
  <% if action == 'index' -%>
10
- {<%= config.resource %>: model.map { |record| record.slice(*<%= config.model_class %>.public_attributes_list) }}
10
+ { <%= config.resource %>: model.map { |record| record.slice(*<%= config.model_class %>.public_attributes_list) } }
11
11
  <% else -%>
12
- {<%= config.model_name %>: model.slice(*<%= config.model_class %>.public_attributes_list)}
12
+ { <%= config.model_name %>: model.slice(*<%= config.model_class %>.public_attributes_list) }
13
13
  <% end -%>
14
14
  end
15
15
  end
@@ -3,105 +3,111 @@
3
3
  require_relative '../spec_helper'
4
4
 
5
5
  RSpec.describe '<%= config.model_class %> CRUD' do
6
- let(:client) { Rack::MockRequest.new(Rack::Builder.parse_file(File.join(<%= config.namespace %>::ROOT, 'config.ru'))) }
7
- let(:attributes) { JSON.parse(<%= JSON.generate(config.example).inspect %>) }
6
+ include Rack::Test::Methods
8
7
 
9
- before { <%= config.namespace %>::<%= config.model_class %>.dataset.delete }
8
+ let(:app) { Rack::Builder.parse_file(File.join(<%= config.namespace %>::ROOT, 'config.ru')) }
9
+ let(:attributes) { JSON.parse(<%= config.example.to_json.inspect %>) }
10
+
11
+ before do
12
+ <%= config.namespace %>::<%= config.model_class %>.dataset.delete
13
+ header 'CONTENT_TYPE', 'application/json'
14
+ end
10
15
 
11
16
  it 'persists permitted fields and returns a 201 response through the Rack entrypoint' do
12
- response = client.post('/<%= config.resource %>', input: JSON.generate(attributes.merge('id' => 999)),
13
- 'CONTENT_TYPE' => 'application/json')
17
+ post('/<%= config.resource %>', attributes.merge('id' => 999).to_json)
14
18
 
15
- expect(response.status).to eq(201)
19
+ expect(last_response.status).to eq(201)
16
20
  record = <%= config.namespace %>::<%= config.model_class %>.first
17
21
  expect(record).not_to be_nil
18
22
  expect(record.id).not_to eq(999)
19
- payload = JSON.parse(response.body).fetch('<%= config.model_name %>')
23
+ payload = JSON.parse(last_response.body).fetch('<%= config.model_name %>')
20
24
  expect(payload.fetch('id')).to eq(record.id)
21
25
  expect(payload.keys).to match_array(%w[id <%= config.field_names.join(' ') %> created_at updated_at])
22
26
  end
23
27
 
24
28
  it 'rejects missing required fields without inserting a record' do
25
- response = client.post('/<%= config.resource %>', input: '{}', 'CONTENT_TYPE' => 'application/json')
29
+ post('/<%= config.resource %>', {}.to_json)
26
30
 
27
- expect(response.status).to eq(422)
31
+ expect(last_response.status).to eq(422)
28
32
  expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(0)
29
33
  end
30
34
 
31
35
  it 'rejects an invalid field type without inserting a record' do
32
36
  attributes['<%= config.fields.first[:name] %>'] = []
33
- response = client.post('/<%= config.resource %>', input: JSON.generate(attributes), 'CONTENT_TYPE' => 'application/json')
37
+ post('/<%= config.resource %>', attributes.to_json)
34
38
 
35
- expect(response.status).to eq(400)
39
+ expect(last_response.status).to eq(400)
36
40
  expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(0)
37
41
  end
38
42
 
39
43
  def create_record
40
- response = client.post('/<%= config.resource %>', input: JSON.generate(attributes), 'CONTENT_TYPE' => 'application/json')
41
- expect(response.status).to eq(201)
42
- JSON.parse(response.body).fetch('<%= config.model_name %>')
44
+ post('/<%= config.resource %>', attributes.to_json)
45
+ expect(last_response.status).to eq(201)
46
+ JSON.parse(last_response.body).fetch('<%= config.model_name %>')
43
47
  end
44
48
 
45
49
  it 'lists records in ID order, including an empty collection' do
46
- expect(JSON.parse(client.get('/<%= config.resource %>').body)).to eq('<%= config.resource %>' => [])
50
+ get('/<%= config.resource %>')
51
+ expect(JSON.parse(last_response.body)).to eq('<%= config.resource %>' => [])
47
52
  records = [create_record, create_record]
48
- response = client.get('/<%= config.resource %>')
49
- expect(response.status).to eq(200)
50
- expect(JSON.parse(response.body).fetch('<%= config.resource %>')).to eq(records)
53
+ get('/<%= config.resource %>')
54
+ expect(last_response.status).to eq(200)
55
+ expect(JSON.parse(last_response.body).fetch('<%= config.resource %>')).to eq(records)
51
56
  end
52
57
 
53
58
  it 'shows a single record' do
54
59
  record = create_record
55
- response = client.get("/<%= config.resource %>/#{record.fetch('id')}")
56
- expect(response.status).to eq(200)
57
- expect(JSON.parse(response.body).fetch('<%= config.model_name %>')).to eq(record)
60
+ get("/<%= config.resource %>/#{record.fetch('id')}")
61
+ expect(last_response.status).to eq(200)
62
+ expect(JSON.parse(last_response.body).fetch('<%= config.model_name %>')).to eq(record)
58
63
  end
59
64
 
60
- <% updated_value = {'string' => 'Updated', 'text' => 'Updated text', 'integer' => 2, 'float' => 2.5, 'boolean' => true, 'date' => '2027-02-03', 'datetime' => '2027-02-03T14:30:00Z'}.fetch(config.fields.first[:type]) -%>
61
- %w[patch put].each do |verb|
62
- it "updates permitted fields with #{verb.upcase}, preserving omitted fields and the ID" do
63
- record = create_record
64
- changes = {'<%= config.fields.first[:name] %>' => <%= updated_value.inspect %>, 'id' => 999}
65
- response = client.public_send(verb, "/<%= config.resource %>/#{record.fetch('id')}",
66
- input: JSON.generate(changes), 'CONTENT_TYPE' => 'application/json')
67
- expect(response.status).to eq(200)
68
- payload = JSON.parse(response.body).fetch('<%= config.model_name %>')
69
- expect(payload.fetch('id')).to eq(record.fetch('id'))
65
+ <% updated_value = { 'string' => 'Updated', 'text' => 'Updated text', 'integer' => 2, 'float' => 2.5, 'boolean' => true, 'date' => '2027-02-03', 'datetime' => '2027-02-03T14:30:00Z' }.fetch(config.fields.first[:type]) -%>
66
+ it 'updates permitted fields with PUT, preserving omitted fields and the ID' do
67
+ record = create_record
68
+ changes = { '<%= config.fields.first[:name] %>' => <%= updated_value.inspect %>, 'id' => 999 }
69
+ put("/<%= config.resource %>/#{record.fetch('id')}",
70
+ changes.to_json)
71
+ expect(last_response.status).to eq(200)
72
+ payload = JSON.parse(last_response.body).fetch('<%= config.model_name %>')
73
+ expect(payload.fetch('id')).to eq(record.fetch('id'))
70
74
  <% if config.fields.first[:type] == 'datetime' -%>
71
- expect(DateTime.parse(payload.fetch('<%= config.fields.first[:name] %>'))).to eq(DateTime.iso8601(<%= updated_value.inspect %>))
75
+ expect(DateTime.parse(payload.fetch('<%= config.fields.first[:name] %>'))).to eq(DateTime.iso8601(<%= updated_value.inspect %>))
72
76
  <% else -%>
73
- expect(payload.fetch('<%= config.fields.first[:name] %>')).to eq(<%= updated_value.inspect %>)
77
+ expect(payload.fetch('<%= config.fields.first[:name] %>')).to eq(<%= updated_value.inspect %>)
74
78
  <% end -%>
75
- expect(payload.except('<%= config.fields.first[:name] %>', 'updated_at')).to eq(record.except('<%= config.fields.first[:name] %>', 'updated_at'))
76
- expect(JSON.parse(client.get("/<%= config.resource %>/#{record.fetch('id')}").body).fetch('<%= config.model_name %>')).to eq(payload)
77
- expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(1)
78
- end
79
+ expect(payload.except('<%= config.fields.first[:name] %>', 'updated_at')).to eq(record.except('<%= config.fields.first[:name] %>', 'updated_at'))
80
+ get("/<%= config.resource %>/#{record.fetch('id')}")
81
+ expect(JSON.parse(last_response.body).fetch('<%= config.model_name %>')).to eq(payload)
82
+ expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(1)
79
83
  end
80
84
 
81
85
  it 'rejects invalid updates without changing stored data' do
82
86
  record = create_record
83
87
  <%= %w[string text].include?(config.fields.first[:type]) ? "[[[], 400], [nil, 400], ['', 422]]" : '[[[], 400], [nil, 400]]' %>.each do |value, status|
84
- response = client.patch("/<%= config.resource %>/#{record.fetch('id')}",
85
- input: JSON.generate('<%= config.fields.first[:name] %>' => value), 'CONTENT_TYPE' => 'application/json')
86
- expect(response.status).to eq(status)
87
- expect(JSON.parse(client.get("/<%= config.resource %>/#{record.fetch('id')}").body).fetch('<%= config.model_name %>')).to eq(record)
88
+ put("/<%= config.resource %>/#{record.fetch('id')}",
89
+ { '<%= config.fields.first[:name] %>' => value }.to_json)
90
+ expect(last_response.status).to eq(status)
91
+ get("/<%= config.resource %>/#{record.fetch('id')}")
92
+ expect(JSON.parse(last_response.body).fetch('<%= config.model_name %>')).to eq(record)
88
93
  end
89
94
  end
90
95
 
91
96
  it 'deletes a record and returns its public attributes' do
92
97
  record = create_record
93
- response = client.delete("/<%= config.resource %>/#{record.fetch('id')}")
94
- expect(response.status).to eq(200)
95
- expect(JSON.parse(response.body).fetch('<%= config.model_name %>')).to eq(record)
98
+ delete("/<%= config.resource %>/#{record.fetch('id')}")
99
+ expect(last_response.status).to eq(200)
100
+ expect(JSON.parse(last_response.body).fetch('<%= config.model_name %>')).to eq(record)
96
101
  expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(0)
97
- expect(client.get("/<%= config.resource %>/#{record.fetch('id')}").status).to eq(404)
102
+ get("/<%= config.resource %>/#{record.fetch('id')}")
103
+ expect(last_response.status).to eq(404)
98
104
  end
99
105
 
100
- %w[get patch put delete].each do |verb|
106
+ %w[get put delete].each do |verb|
101
107
  it "returns 404 for #{verb.upcase} of a missing record" do
102
- response = client.public_send(verb, '/<%= config.resource %>/999999',
103
- input: JSON.generate(attributes), 'CONTENT_TYPE' => 'application/json')
104
- expect(response.status).to eq(404)
108
+ public_send(verb, '/<%= config.resource %>/999999',
109
+ attributes.to_json)
110
+ expect(last_response.status).to eq(404)
105
111
  expect(<%= config.namespace %>::<%= config.model_class %>.count).to eq(0)
106
112
  end
107
113
  end
@@ -2,7 +2,7 @@
2
2
 
3
3
  ENV['RACK_ENV'] = 'test'
4
4
  require 'rspec'
5
- require 'rack/mock'
5
+ require 'rack/test'
6
6
  require 'rack/builder'
7
7
  require 'json'
8
8
  require_relative '../database'
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MK
4
- VERSION = '0.2.2'
4
+ VERSION = '0.2.3'
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mk_framework
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Francesco Canessa