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 +4 -4
- data/lib/inline_forms_installer/installer_core.rb +66 -9
- data/lib/inline_forms_installer/version.rb +1 -1
- data/lib/inline_forms_installer.rb +15 -3
- data/lib/installer_templates/example_app_tests/test/integration/example_app_showcase_numeric_fields_test.rb +79 -4
- data/lib/installer_templates/example_app_tests/test/integration/example_app_showcase_page_render_test.rb +4 -0
- data/lib/installer_templates/example_app_tests/test/models/example_app_form_element_showcase_test.rb +61 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 949a3d8e8b439ac7f34a04e4acfc38974c3a7125a45a8cc6384e50bee43f9ae9
|
|
4
|
+
data.tar.gz: 7ae6307dad95185fe50df14b7f5e4b3a1f7b010bb27d9b6cdb5847f5eb61b997
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
"
|
|
1425
|
+
"#{rails_test} 2>&1 | tee -a #{Shellwords.escape(log_path)}"
|
|
1369
1426
|
else
|
|
1370
|
-
"
|
|
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
|
|
@@ -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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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
|
-
|
|
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?
|
data/lib/installer_templates/example_app_tests/test/models/example_app_form_element_showcase_test.rb
CHANGED
|
@@ -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
|