inline_forms_installer 8.1.9 → 8.1.11

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: f4163e5ce8733e5d7f2226ce39f9a475a75860497da6d6d0ddf12575deaa661f
4
- data.tar.gz: 42640181a8f3927a9e70ea3b536dfa3507eb21b0997b1fd5a4a5409668422002
3
+ metadata.gz: 949a3d8e8b439ac7f34a04e4acfc38974c3a7125a45a8cc6384e50bee43f9ae9
4
+ data.tar.gz: 7ae6307dad95185fe50df14b7f5e4b3a1f7b010bb27d9b6cdb5847f5eb61b997
5
5
  SHA512:
6
- metadata.gz: 7034b106b0c14b639fff1385ef6827f5e3431fe9209e978ea932a65e28a58c97ef8a45e9c66b6cac1bbd8a1c906d8628611127714716f954d75df28ac1c232aa
7
- data.tar.gz: 70ac2e021ba10ffb43fbae4f7608134b1f25ad0583d6f92658166a963aadcc8a9d7deb25b8d3389cbd575224e95f0698ba470f3b7f26f016d0dc8a03e79542f8
6
+ metadata.gz: 70aa006317eaab16ee0995ebe148d1296c20e52819da3f3bcce30469413287297a2918301ff68c5ddd96395bf3374d6586c1a81a47018a2da0594b23f12ae307
7
+ data.tar.gz: 34481743978a4551cba788d665975d57c057ac7864d3aae9f8eaf78d75c755510d82aa9b3df68ffbbf686c8417b0638b84b9433b21ec9cc1e8f84ad5f71b5de6
@@ -46,10 +46,28 @@ def install_prerelease_gems_from_roots!
46
46
  return if roots.empty?
47
47
 
48
48
  %w[validation_hints inline_forms inline_forms_installer].each do |name|
49
- gem_file = roots.filter_map { |root|
50
- files = Dir[File.join(root, "#{name}-*.gem")]
51
- files.sort.last if files.any?
52
- }.max
49
+ # Pick the *highest version*, not the highest filename. String sort
50
+ # placed `inline_forms-8.1.7.gem` above `inline_forms-8.1.10.gem`
51
+ # because "7" > "1" lexicographically — silently picking up a stale
52
+ # gem build on every release once minor versions cross a digit
53
+ # boundary. Parse the version out of the filename with Gem::Version
54
+ # so the comparison is numeric.
55
+ #
56
+ # Look in both the checkout root (`gem build` output) *and* `pkg/`
57
+ # (`rake build` output from Bundler::GemHelper.install_tasks). 8.1.10
58
+ # only globbed the root, so the moment a maintainer ran `rake build`
59
+ # the freshly-built gem ended up in `pkg/` and was invisible — the
60
+ # installer fell back to whatever stale `<name>-*.gem` was still
61
+ # sitting in the checkout root from an earlier `gem build`. That's
62
+ # the exact shape that bit us between 8.1.6 (default gemset's
63
+ # highest installer) and 8.1.10.
64
+ candidates = roots.flat_map { |root|
65
+ Dir[File.join(root, "#{name}-*.gem"), File.join(root, "pkg", "#{name}-*.gem")]
66
+ }
67
+ gem_file = candidates.max_by do |path|
68
+ ver_str = File.basename(path, ".gem").sub(/\A#{Regexp.escape(name)}-/, "")
69
+ Gem::Version.new(ver_str) rescue Gem::Version.new("0")
70
+ end
53
71
  next unless gem_file && File.file?(gem_file)
54
72
 
55
73
  say "- Installing #{File.basename(gem_file)} into app gemset..."
@@ -1065,7 +1083,7 @@ if ENV['install_example'] == 'true'
1065
1083
  # ---------------------------------------------------------------------
1066
1084
  say "- Generating FormElementShowcase (one resource per kept Tier 1 form_element)..."
1067
1085
  sleep 1
1068
- run %q{bundle exec rails g inline_forms FormElementShowcase title:string body_plain_area:plain_text_area count:integer_field price:decimal_field amount:money_field meeting_date:date_select meeting_time:time_select birth_month:month_select start_month:month_year_picker is_active:check_box gender:radio_button rating_int:dropdown_with_integers priority:dropdown_with_values priority2:dropdown_with_values stars:dropdown_with_values_with_stars scale_int:scale_with_integers scale_val:scale_with_values attachment:file_field jingle:audio_field cover:image_field description:rich_text locales:has_and_belongs_to_many _enabled:yes _list_order:title _list_search:title _presentation:'#{title}'}
1086
+ run %q{bundle exec rails g inline_forms FormElementShowcase title:string body_plain_area:plain_text_area count:integer_field price:decimal_field amount:money_field latitude:decimal_field{9,6} longitude:decimal_field{10,6} meeting_date:date_select meeting_time:time_select birth_month:month_select start_month:month_year_picker is_active:check_box gender:radio_button rating_int:dropdown_with_integers priority:dropdown_with_values priority2:dropdown_with_values stars:dropdown_with_values_with_stars scale_int:scale_with_integers scale_val:scale_with_values attachment:file_field jingle:audio_field cover:image_field description:rich_text locales:has_and_belongs_to_many _enabled:yes _list_order:title _list_search:title _presentation:'#{title}'}
1069
1087
 
1070
1088
  say "- Generating Attachment + Jingle uploaders (Cover reuses ImageUploader)..."
1071
1089
  run "bundle exec rails generate uploader Attachment"
@@ -1127,7 +1145,11 @@ if ENV['install_example'] == 'true'
1127
1145
  # A `def` body is parsed but only resolved at call time, side-stepping
1128
1146
  # the ordering hazard.
1129
1147
  inject_into_file "app/models/form_element_showcase.rb",
1130
- "\n validates :count, numericality: { only_integer: true }, allow_blank: true\n mount_uploader :attachment, AttachmentUploader\n monetize :amount_cents\n\n def locales_display\n locales\n end\n",
1148
+ "\n validates :count, numericality: { only_integer: true }, allow_blank: true\n" \
1149
+ " validates :price, numericality: true, allow_blank: true\n" \
1150
+ " validates :latitude, numericality: { greater_than_or_equal_to: -90, less_than_or_equal_to: 90 }, allow_blank: true\n" \
1151
+ " validates :longitude, numericality: { greater_than_or_equal_to: -180, less_than_or_equal_to: 180 }, allow_blank: true\n" \
1152
+ " mount_uploader :attachment, AttachmentUploader\n monetize :amount_cents\n\n def locales_display\n locales\n end\n",
1131
1153
  after: "class FormElementShowcase < ApplicationRecord\n"
1132
1154
 
1133
1155
  # Value-bearing rows for every form_element that needs a values hash
@@ -1199,6 +1221,8 @@ if ENV['install_example'] == 'true'
1199
1221
  amount: Amount
1200
1222
  meeting_date: Meeting date
1201
1223
  meeting_time: Meeting time
1224
+ latitude: Latitude
1225
+ longitude: Longitude
1202
1226
  birth_month: Birth month
1203
1227
  start_month: Start month and year
1204
1228
  is_active: Is active
@@ -1280,6 +1304,11 @@ if ENV['install_example'] == 'true'
1280
1304
  s.count = 7
1281
1305
  s.price = "12.34"
1282
1306
  s.amount = Money.from_amount(99.95, "USD") if s.respond_to?(:amount=)
1307
+ # Curaçao (Willemstad). Exactly representable in decimal(9,6)
1308
+ # and decimal(10,6) — useful for the precision-survival test
1309
+ # that asserts the round-trip is bit-identical, not float-fuzzy.
1310
+ s.latitude = BigDecimal("12.123456")
1311
+ s.longitude = BigDecimal("-68.987654")
1283
1312
  s.meeting_date = Date.new(2026, 6, 1)
1284
1313
  s.meeting_time = Time.utc(2000, 1, 1, 14, 30)
1285
1314
  s.birth_month = 7
@@ -1362,12 +1391,40 @@ if ENV['install_example'] == 'true'
1362
1391
  create_file rel, example_user_cfg.adapt_example_test_source(File.read(abs))
1363
1392
  end
1364
1393
 
1365
- say "- Running example regression tests (bundle exec rails test)..."
1394
+ # Cap test parallelism so the example-app gate fits on memory-modest
1395
+ # machines. Rails' minitest parallelizer defaults to `workers:
1396
+ # number_of_processors`, which on a 20-core box forks 20 full Rails
1397
+ # processes — each ~200-300 MB resident with the example app's full
1398
+ # gem stack (CarrierWave + Devise + PaperTrail + Foundation +
1399
+ # tabs_on_rails + money-rails). Multiply that by 20 and you're 4-6 GB
1400
+ # in worker processes alone, on top of the parent installer/bundler
1401
+ # state. On a memory-pressured host systemd-oomd can kill the whole
1402
+ # VTE/terminal session before the gate finishes, with no useful
1403
+ # signal back to the user (one such kill happened mid-gate on
1404
+ # 2026-05-28 07:50:35; that's what motivated this cap).
1405
+ #
1406
+ # `PARALLEL_WORKERS=2` keeps the worker footprint to ~600 MB and
1407
+ # roughly doubles the wall-clock vs 20 workers — fine for a one-shot
1408
+ # gate. The `INLINE_FORMS_TEST_WORKERS` env override lets machines
1409
+ # with RAM headroom crank it back up (use `0` for Rails' default
1410
+ # number_of_processors, any positive integer to pin).
1411
+ workers_env = ENV["INLINE_FORMS_TEST_WORKERS"].to_s.strip
1412
+ workers = if workers_env.empty?
1413
+ "2"
1414
+ elsif workers_env == "0"
1415
+ nil # let Rails pick number_of_processors
1416
+ else
1417
+ workers_env
1418
+ end
1419
+ worker_prefix = workers ? "PARALLEL_WORKERS=#{Shellwords.escape(workers)} " : ""
1420
+
1421
+ say "- Running example regression tests (bundle exec rails test)#{workers ? " with PARALLEL_WORKERS=#{workers}" : ""}..."
1366
1422
  log_path = ENV["INLINE_FORMS_INSTALLER_LOG"].to_s
1423
+ rails_test = "#{worker_prefix}bundle exec rails test"
1367
1424
  test_cmd = if log_path != ""
1368
- "bundle exec rails test 2>&1 | tee -a #{Shellwords.escape(log_path)}"
1425
+ "#{rails_test} 2>&1 | tee -a #{Shellwords.escape(log_path)}"
1369
1426
  else
1370
- "bundle exec rails test 2>&1"
1427
+ "#{rails_test} 2>&1"
1371
1428
  end
1372
1429
  test_ok = system("bash", "-c", "#{test_cmd}; exit ${PIPESTATUS[0]}")
1373
1430
  abort "ERROR: bundle exec rails test failed during --example install. See #{log_path}" unless test_ok
@@ -1,6 +1,6 @@
1
1
  # -*- encoding : utf-8 -*-
2
2
  module InlineFormsInstaller
3
- VERSION = "8.1.9"
3
+ VERSION = "8.1.11"
4
4
 
5
5
  # Written into generated apps' `.ruby-version` (must match gemspec `required_ruby_version`).
6
6
  TARGET_RUBY_VERSION = "ruby-4.0.4"
@@ -38,12 +38,12 @@ module InlineFormsInstaller
38
38
  6.times do
39
39
  if ENV["INLINE_FORMS_RELEASE_ROOT"].to_s == "" &&
40
40
  File.file?(File.join(dir, "inline_forms.gemspec")) &&
41
- Dir[File.join(dir, "inline_forms-*.gem")].any?
41
+ checkout_has_built_gem?(dir, "inline_forms")
42
42
  ENV["INLINE_FORMS_RELEASE_ROOT"] = dir
43
43
  end
44
44
  if ENV["VALIDATION_HINTS_ROOT"].to_s == "" &&
45
45
  File.file?(File.join(dir, "validation_hints.gemspec")) &&
46
- Dir[File.join(dir, "validation_hints-*.gem")].any?
46
+ checkout_has_built_gem?(dir, "validation_hints")
47
47
  ENV["VALIDATION_HINTS_ROOT"] = dir
48
48
  end
49
49
  parent = File.expand_path("..", dir)
@@ -74,9 +74,21 @@ module InlineFormsInstaller
74
74
  File.expand_path("~/#{repo_name}")
75
75
  ].uniq.find do |checkout|
76
76
  File.file?(File.join(checkout, "#{repo_name}.gemspec")) &&
77
- Dir[File.join(checkout, "#{repo_name}-*.gem")].any?
77
+ checkout_has_built_gem?(checkout, repo_name)
78
78
  end
79
79
  end
80
+
81
+ # Look for built `<name>-*.gem` files in both the checkout root *and*
82
+ # `pkg/` — `gem build` writes to the root, but `rake build` (i.e.
83
+ # `Bundler::GemHelper.install_tasks` from the Rakefile) writes to
84
+ # `pkg/`. Either is a legitimate "I just built this" location; only
85
+ # globbing the root meant a freshly-`rake build`-ed gem was invisible
86
+ # to install_prerelease_gems_from_roots! and the installer would
87
+ # silently fall back to whatever stale `*.gem` was sitting in the
88
+ # checkout root from a previous `gem build` run.
89
+ def self.checkout_has_built_gem?(checkout, name)
90
+ Dir[File.join(checkout, "#{name}-*.gem"), File.join(checkout, "pkg", "#{name}-*.gem")].any?
91
+ end
80
92
  end
81
93
 
82
94
  require "inline_forms_installer/user_model_config"
@@ -3,8 +3,10 @@
3
3
  require_relative "../example_app/example_integration_test_case"
4
4
 
5
5
  # Numeric Tier 1 helpers on FormElementShowcase:
6
- # integer_field (count) + numericality validation regression
7
- # decimal_field (price)
6
+ # integer_field (count) + numericality validation regression
7
+ # decimal_field (price) default precision: 10, scale: 2
8
+ # decimal_field (latitude, longitude) explicit `{p,s}` CLI suffix:
9
+ # decimal(9,6) / decimal(10,6)
8
10
  # money_field (amount, money-rails `monetize :amount_cents`)
9
11
  class ExampleAppShowcaseNumericFieldsTest < ExampleAppIntegrationTestCase
10
12
  setup do
@@ -30,7 +32,11 @@ class ExampleAppShowcaseNumericFieldsTest < ExampleAppIntegrationTestCase
30
32
  assert_equal 42, @showcase.reload.count
31
33
  end
32
34
 
33
- test "decimal_field price round-trips" do
35
+ test "decimal_field price round-trips as BigDecimal with default precision/scale" do
36
+ # Since 8.1.10 the registry maps :decimal_field to a real :decimal
37
+ # column. Bare `price:decimal_field` (no `{p,s}` suffix) defaults to
38
+ # precision: 10, scale: 2 — so 99.95 round-trips losslessly *and*
39
+ # ActiveRecord returns a BigDecimal, not a String.
34
40
  frame = "form_element_showcase_#{@showcase.id}_price"
35
41
  headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
36
42
 
@@ -44,7 +50,76 @@ class ExampleAppShowcaseNumericFieldsTest < ExampleAppIntegrationTestCase
44
50
  assert_response :success
45
51
  assert_includes @response.body, %(<turbo-frame id="#{frame}">)
46
52
  assert_includes @response.body, "99.95"
47
- assert_equal "99.95", @showcase.reload.price
53
+ reloaded_price = @showcase.reload.price
54
+ assert_kind_of BigDecimal, reloaded_price,
55
+ "expected :decimal column to return BigDecimal, got #{reloaded_price.class}"
56
+ assert_equal BigDecimal("99.95"), reloaded_price
57
+ end
58
+
59
+ test "decimal_field{9,6} latitude round-trips at full scale (6 fractional digits)" do
60
+ # The `latitude:decimal_field{9,6}` CLI suffix should give us
61
+ # exactly 6 fractional digits of precision — enough for ~11cm
62
+ # accuracy in GPS terms. Pick a value that fully populates the
63
+ # scale so we'd catch off-by-one truncation.
64
+ frame = "form_element_showcase_#{@showcase.id}_latitude"
65
+ headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
66
+
67
+ put form_element_showcase_path(
68
+ @showcase,
69
+ attribute: "latitude",
70
+ form_element: "decimal_field",
71
+ update: frame
72
+ ), params: { latitude: "12.123456" }, headers: headers
73
+
74
+ assert_response :success
75
+ reloaded = @showcase.reload.latitude
76
+ assert_kind_of BigDecimal, reloaded
77
+ assert_equal BigDecimal("12.123456"), reloaded
78
+ end
79
+
80
+ test "decimal_field{10,6} longitude accepts the full ±180 range" do
81
+ frame = "form_element_showcase_#{@showcase.id}_longitude"
82
+ headers = { "Turbo-Frame" => frame, "Accept" => "text/html" }
83
+
84
+ put form_element_showcase_path(
85
+ @showcase,
86
+ attribute: "longitude",
87
+ form_element: "decimal_field",
88
+ update: frame
89
+ ), params: { longitude: "-179.999999" }, headers: headers
90
+
91
+ assert_response :success
92
+ assert_equal BigDecimal("-179.999999"), @showcase.reload.longitude
93
+ end
94
+
95
+ test "decimal_field price rejects non-numeric via top-level create" do
96
+ # Counterpart to the integer_field rejection test below. Without
97
+ # the `validates :price, numericality: true` line in the showcase
98
+ # model, ActiveRecord would silently cast "ace" to BigDecimal("0")
99
+ # and the create would succeed. The validation keeps the
100
+ # round-trip honest.
101
+ assert_no_difference "FormElementShowcase.count" do
102
+ post form_element_showcases_path(update: @list_frame),
103
+ params: {
104
+ title: "Bad Price",
105
+ price: "ace",
106
+ count: 1,
107
+ amount: "0.00",
108
+ start_month: "September 2026",
109
+ _form_element_showcase: {
110
+ rating_int: 1,
111
+ priority: 1,
112
+ priority2: 1,
113
+ stars: 1,
114
+ scale_int: 1,
115
+ scale_val: 1,
116
+ },
117
+ },
118
+ headers: @list_headers
119
+ end
120
+ assert_response :success
121
+ assert_match(/is not a number|price[^<]*is not a number/i, @response.body,
122
+ "expected numericality error to render after invalid price")
48
123
  end
49
124
 
50
125
  test "money_field amount round-trips through monetize :amount_cents" do
@@ -14,6 +14,8 @@ class ExampleAppShowcasePageRenderTest < ExampleAppIntegrationTestCase
14
14
  count
15
15
  price
16
16
  amount
17
+ latitude
18
+ longitude
17
19
  meeting_date
18
20
  meeting_time
19
21
  birth_month
@@ -66,6 +68,8 @@ class ExampleAppShowcasePageRenderTest < ExampleAppIntegrationTestCase
66
68
  s.scale_int = 3
67
69
  s.scale_val = 2
68
70
  s.amount = Money.from_amount(99.95, "USD") if s.respond_to?(:amount=) && defined?(Money)
71
+ s.latitude = BigDecimal("12.123456")
72
+ s.longitude = BigDecimal("-68.987654")
69
73
  s.description = "<p>A rich-text body.</p>"
70
74
  end
71
75
  @full.locales << @locale unless @full.locales.where(id: @locale.id).exists?
@@ -16,6 +16,8 @@ class ExampleAppFormElementShowcaseTest < ActiveSupport::TestCase
16
16
  count
17
17
  price
18
18
  amount
19
+ latitude
20
+ longitude
19
21
  header_dates
20
22
  meeting_date
21
23
  meeting_time
@@ -82,4 +84,63 @@ class ExampleAppFormElementShowcaseTest < ActiveSupport::TestCase
82
84
  showcase = FormElementShowcase.new(title: "X", count: nil)
83
85
  assert showcase.valid?, "expected showcase to be valid with count=nil (allow_blank), got #{showcase.errors.full_messages.inspect}"
84
86
  end
87
+
88
+ # ---------------------------------------------------------------------
89
+ # 8.1.10: :decimal_field now maps to a real :decimal column with
90
+ # precision/scale instead of the legacy varchar. The migration emitter
91
+ # applies (10, 2) by default and reads `{p,s}` overrides from the CLI
92
+ # type suffix (e.g. `latitude:decimal_field{9,6}` -> decimal(9,6)).
93
+ # ---------------------------------------------------------------------
94
+
95
+ test "decimal_field maps to a real :decimal column with default precision/scale" do
96
+ price_col = FormElementShowcase.columns_hash["price"]
97
+ assert_not_nil price_col, "expected a :price column"
98
+ assert_equal :decimal, price_col.type,
99
+ "expected :price to be a :decimal column, got #{price_col.type.inspect} (#{price_col.sql_type.inspect})"
100
+ assert_equal 10, price_col.precision, "expected default precision: 10"
101
+ assert_equal 2, price_col.scale, "expected default scale: 2"
102
+ end
103
+
104
+ test "decimal_field{p,s} CLI suffix sets precision and scale" do
105
+ lat = FormElementShowcase.columns_hash["latitude"]
106
+ lon = FormElementShowcase.columns_hash["longitude"]
107
+ assert_equal :decimal, lat.type
108
+ assert_equal :decimal, lon.type
109
+ assert_equal [9, 6], [lat.precision, lat.scale],
110
+ "expected latitude:decimal_field{9,6} -> decimal(9,6), got decimal(#{lat.precision},#{lat.scale})"
111
+ assert_equal [10, 6], [lon.precision, lon.scale],
112
+ "expected longitude:decimal_field{10,6} -> decimal(10,6), got decimal(#{lon.precision},#{lon.scale})"
113
+ end
114
+
115
+ test "decimal column round-trips BigDecimal losslessly at full scale" do
116
+ showcase = FormElementShowcase.create!(
117
+ title: "decimal-roundtrip",
118
+ latitude: BigDecimal("12.123456"),
119
+ longitude: BigDecimal("-68.987654"),
120
+ )
121
+ showcase.reload
122
+ assert_kind_of BigDecimal, showcase.latitude
123
+ assert_equal BigDecimal("12.123456"), showcase.latitude
124
+ assert_equal BigDecimal("-68.987654"), showcase.longitude
125
+ end
126
+
127
+ test "price rejects non-numeric input with a numericality error" do
128
+ showcase = FormElementShowcase.new(title: "X", price: "ace")
129
+ assert_not showcase.valid?
130
+ assert_includes showcase.errors.attribute_names, :price
131
+ assert showcase.errors[:price].any? { |m| m.match?(/not a number/i) },
132
+ "expected `not a number` error on :price, got #{showcase.errors[:price].inspect}"
133
+ end
134
+
135
+ test "latitude rejects out-of-range values via numericality range" do
136
+ showcase = FormElementShowcase.new(title: "X", latitude: 91)
137
+ assert_not showcase.valid?
138
+ assert_includes showcase.errors.attribute_names, :latitude
139
+ end
140
+
141
+ test "longitude rejects out-of-range values via numericality range" do
142
+ showcase = FormElementShowcase.new(title: "X", longitude: 181)
143
+ assert_not showcase.valid?
144
+ assert_includes showcase.errors.attribute_names, :longitude
145
+ end
85
146
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: inline_forms_installer
3
3
  version: !ruby/object:Gem::Version
4
- version: 8.1.9
4
+ version: 8.1.11
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ace Suares