chobble-forms 0.6.0 → 0.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32610a1903913985762082f5865bd24bfcae7327bf887cafa70c09db7efe9e88
4
- data.tar.gz: 4d0e15de04ab8ceee7fac1fea871d83582caf5bc96e4cc39760ef7e1297c8915
3
+ metadata.gz: 771171d582d4bdb09d03fc7ac56a37ca4922d7ce9ee76f392c19c0667e2cd6d3
4
+ data.tar.gz: e692e2d66579f14eae1099989efddef5a7358620653ac6ab7daaf3ffab151333
5
5
  SHA512:
6
- metadata.gz: c70cbe6796e0edba08b2c41c5e476401d4579cc86de865710a772c0f4ef570abacd76f4ec08033d1d405ef887cdf44b52aa5f4e82c07085270c28186f5fb7532
7
- data.tar.gz: d9b8cc2aff3b86e9de10b189a44f2d2a9f651a236b87da01dd0095bc27fb81b8ea034f14d72655ae580fa4cd26a777674c3a2db78225769eca42af134e176208
6
+ metadata.gz: c7b53bd70bfa1063f24071b476a9789b43d79c0c539e033857b1331caf0456bb324fea113a93471bc0fef9b114c9b9004f52b10c0c3a82ef52c6a85e54b91e34
7
+ data.tar.gz: f4b9fd0dedb8a2e0d133cf09f0dc0ab5e9e4b390cfeafe510b16424e9ec1037492fdec506db580447c61d6857597bb164d60c2b211fc019f1df58d1472f27202
@@ -7,23 +7,21 @@ module ChobbleForms
7
7
  module FieldUtils
8
8
  extend T::Sig
9
9
 
10
- sig { params(field: Symbol).returns(String) }
10
+ sig { params(field: Symbol).returns(Symbol) }
11
11
  def self.strip_field_suffix(field)
12
- field.to_s.gsub(/_pass$|_comment$/, "")
12
+ field.to_s.gsub(/_pass$|_comment$/, "").to_sym
13
13
  end
14
14
 
15
- sig { params(field: Symbol, partial: Symbol).returns(T::Array[String]) }
15
+ sig { params(field: Symbol, partial: Symbol).returns(T::Array[Symbol]) }
16
16
  def self.get_composite_fields(field, partial)
17
- fields = T.let([], T::Array[String])
17
+ fields = T.let([], T::Array[Symbol])
18
18
  partial_str = partial.to_s
19
19
 
20
- if partial_str.include?("pass_fail") && !field.to_s.end_with?("_pass")
21
- fields << "#{field}_pass"
22
- end
20
+ fields << :"#{field}_pass" if partial_str.include?("pass_fail") && !field.to_s.end_with?("_pass")
23
21
 
24
22
  if partial_str.include?("comment")
25
23
  base = field.to_s.end_with?("_pass") ? strip_field_suffix(field) : field
26
- fields << "#{base}_comment"
24
+ fields << :"#{base}_comment"
27
25
  end
28
26
 
29
27
  fields
@@ -44,7 +42,7 @@ module ChobbleForms
44
42
  is_pass_field?(field) || is_comment_field?(field)
45
43
  end
46
44
 
47
- sig { params(field: Symbol).returns(String) }
45
+ sig { params(field: Symbol).returns(Symbol) }
48
46
  def self.base_field_name(field)
49
47
  strip_field_suffix(field)
50
48
  end
@@ -53,5 +51,10 @@ module ChobbleForms
53
51
  def self.form_field_label(form, field)
54
52
  I18n.t("forms.#{form}.fields.#{field}")
55
53
  end
54
+
55
+ sig { params(assessment_key: Symbol).returns(Symbol) }
56
+ def self.form_name_from_assessment(assessment_key)
57
+ assessment_key.to_s.gsub(/_assessment$/, "").to_sym
58
+ end
56
59
  end
57
60
  end
@@ -7,7 +7,35 @@ module ChobbleForms
7
7
  module Helpers
8
8
  extend T::Sig
9
9
 
10
- sig { params(field: Symbol, local_assigns: T::Hash[Symbol, T.untyped]).returns(T::Hash[Symbol, T.untyped]) }
10
+ SelectOption = T.type_alias {
11
+ [String, T.any(String, Integer)]
12
+ }
13
+
14
+ LocalAssignValue = T.type_alias {
15
+ T.any(
16
+ String,
17
+ Symbol,
18
+ Integer,
19
+ Float,
20
+ T::Boolean,
21
+ T::Array[SelectOption],
22
+ T::Hash[Symbol, T.untyped]
23
+ )
24
+ }
25
+
26
+ FieldSetupResult = T.type_alias {
27
+ {
28
+ form_object: T.untyped,
29
+ i18n_base: String,
30
+ value: T.untyped,
31
+ prefilled: T::Boolean,
32
+ field_label: String,
33
+ field_hint: T.nilable(String),
34
+ field_placeholder: T.nilable(String)
35
+ }
36
+ }
37
+
38
+ sig { params(field: Symbol, local_assigns: T::Hash[Symbol, LocalAssignValue]).returns(FieldSetupResult) }
11
39
  def form_field_setup(field, local_assigns)
12
40
  validate_local_assigns(local_assigns)
13
41
  validate_form_context
@@ -22,6 +50,7 @@ module ChobbleForms
22
50
  sig { params(form_object: T.untyped, field: Symbol).returns([T.untyped, T::Boolean]) }
23
51
  def get_field_value_and_prefilled_status(form_object, field)
24
52
  return [nil, false] unless form_object&.object
53
+
25
54
  model = form_object.object
26
55
  resolved = resolve_field_value(model, field)
27
56
  [resolved[:value], resolved[:prefilled]]
@@ -30,6 +59,7 @@ module ChobbleForms
30
59
  sig { params(form: T.untyped, comment_field: Symbol, base_field_label: String).returns(T::Hash[Symbol, T.untyped]) }
31
60
  def comment_field_options(form, comment_field, base_field_label)
32
61
  raise ArgumentError, "form_object required" unless form
62
+
33
63
  model = form.object
34
64
 
35
65
  comment_value, comment_prefilled =
@@ -81,21 +111,16 @@ module ChobbleForms
81
111
  rows
82
112
  step
83
113
  type
84
- ], T::Array[Symbol])
114
+ ].freeze, T::Array[Symbol])
85
115
 
86
- sig { params(local_assigns: T::Hash[Symbol, T.untyped]).void }
116
+ sig { params(local_assigns: T::Hash[Symbol, LocalAssignValue]).void }
87
117
  def validate_local_assigns(local_assigns)
88
- if local_assigns[:field]&.respond_to?(:to_s) &&
89
- local_assigns[:field].to_s.match?(/^[A-Z]/)
90
- raise ArgumentError, "Field names must be snake_case symbols, not class names. Use :field, not Field."
91
- end
92
-
93
118
  locally_assigned_keys = local_assigns.keys
94
119
  disallowed_keys = locally_assigned_keys - ALLOWED_LOCAL_ASSIGNS
95
120
 
96
- if disallowed_keys.any?
97
- raise ArgumentError, "local_assigns contains #{disallowed_keys.inspect}"
98
- end
121
+ return unless disallowed_keys.any?
122
+
123
+ raise ArgumentError, "local_assigns contains #{disallowed_keys.inspect}"
99
124
  end
100
125
 
101
126
  sig { void }
@@ -109,7 +134,10 @@ module ChobbleForms
109
134
  sig { params(field: Symbol).returns(T::Hash[Symbol, T.nilable(String)]) }
110
135
  def build_field_translations(field)
111
136
  i18n_base = T.unsafe(instance_variable_get(:@_current_i18n_base))
112
- fields_key = "#{i18n_base}.fields.#{field}"
137
+
138
+ # Only strip _pass suffix for pass/fail fields, not _comment fields
139
+ lookup_field = field.to_s.end_with?("_pass") ? FieldUtils.strip_field_suffix(field) : field
140
+ fields_key = "#{i18n_base}.fields.#{lookup_field}"
113
141
  field_label = t(fields_key, raise: true)
114
142
 
115
143
  base_parts = i18n_base.split(".")
@@ -124,34 +152,42 @@ module ChobbleForms
124
152
  }
125
153
  end
126
154
 
127
- sig { params(field_translations: T::Hash[Symbol, T.nilable(String)], value: T.untyped, prefilled: T::Boolean).returns(T::Hash[Symbol, T.untyped]) }
155
+ sig { params(field_translations: T::Hash[Symbol, T.nilable(String)], value: T.untyped, prefilled: T::Boolean).returns(FieldSetupResult) }
128
156
  def build_field_setup_result(field_translations, value, prefilled)
129
157
  form_obj = T.unsafe(instance_variable_get(:@_current_form))
130
158
  i18n_base = T.unsafe(instance_variable_get(:@_current_i18n_base))
131
159
 
132
- {
133
- form_object: form_obj,
134
- i18n_base: i18n_base,
135
- value:,
136
- prefilled:
137
- }.merge(field_translations)
160
+ T.cast(
161
+ {
162
+ form_object: form_obj,
163
+ i18n_base: i18n_base,
164
+ value:,
165
+ prefilled:
166
+ }.merge(field_translations),
167
+ FieldSetupResult
168
+ )
138
169
  end
139
170
 
140
- sig { params(model: T.untyped, field: Symbol).returns(T::Hash[Symbol, T.untyped]) }
171
+ sig { params(model: T.untyped, field: Symbol).returns({value: T.untyped, prefilled: T::Boolean}) }
141
172
  def resolve_field_value(model, field)
142
173
  field_str = field.to_s
143
174
 
144
- if field_str.include?("password")
145
- return {value: nil, prefilled: false}
146
- end
147
-
148
- current_value = model.send(field) if model.respond_to?(field)
175
+ return {value: nil, prefilled: false} if field_str.include?("password")
149
176
 
150
- if defined?(InspectionsController::NOT_COPIED_FIELDS) &&
151
- InspectionsController::NOT_COPIED_FIELDS.include?(field_str)
152
- return {value: current_value, prefilled: false}
177
+ # For composite partials, we receive the base field but need to check for suffixed fields
178
+ # Check for _pass field first (in case both base and _pass exist)
179
+ if model.respond_to?("#{field}_pass")
180
+ current_value = model.send("#{field}_pass")
181
+ elsif model.respond_to?(field)
182
+ current_value = model.send(field)
183
+ else
184
+ raise "Field '#{field}' or '#{field}_pass' not found on #{model.class.name}. Available fields: #{model.attributes.keys.sort.join(", ")}"
153
185
  end
154
186
 
187
+ # Check if this field should not be prefilled based on excluded fields list
188
+ excluded_fields = T.unsafe(instance_variable_get(:@_excluded_prefill_fields))
189
+ return {value: current_value, prefilled: false} if excluded_fields&.include?(field)
190
+
155
191
  prev_inspection = T.unsafe(instance_variable_get(:@previous_inspection))
156
192
  previous_value = extract_previous_value(prev_inspection, model, field)
157
193
 
@@ -2,5 +2,5 @@
2
2
  # frozen_string_literal: true
3
3
 
4
4
  module ChobbleForms
5
- VERSION = "0.6.0"
5
+ VERSION = "0.7.1"
6
6
  end
@@ -8,6 +8,7 @@
8
8
  # url: Form submission URL (defaults to model's update path)
9
9
  # method: HTTP method (defaults to :patch for models, :post for scoped forms)
10
10
  # local: Use standard form submission instead of Turbo (defaults to false)
11
+ # excluded_prefill_fields: Array of field symbols that should not be prefilled
11
12
 
12
13
  if model
13
14
  url ||= url_for(model)
@@ -52,6 +53,11 @@ end %>
52
53
  <% @_current_form = form %>
53
54
  <% @_current_i18n_base = i18n_base %>
54
55
 
56
+ <% # Set excluded prefill fields if provided %>
57
+ <% if local_assigns[:excluded_prefill_fields] %>
58
+ <% @_excluded_prefill_fields = excluded_prefill_fields %>
59
+ <% end %>
60
+
55
61
  <%= yield form %>
56
62
 
57
63
  <% if model && model.respond_to?(:errors) && model.errors.any? %>
@@ -1,13 +1,14 @@
1
1
  <%
2
2
  # Composite partial for pass/fail + comment fields with grid layout
3
3
  # Required parameters:
4
- # field: Base field name (e.g., 'seam_integrity_pass')
4
+ # field: Base field name (e.g., 'seam_integrity')
5
5
  #
6
6
  # This will render:
7
- # - Pass/fail field: field (e.g., 'seam_integrity_pass')
7
+ # - Pass/fail field: base field + '_pass' (e.g., 'seam_integrity_pass')
8
8
  # - Comment field: base field + '_comment' (e.g., 'seam_integrity_comment')
9
9
 
10
10
  base_field = field.to_s.gsub(/_pass$/, "")
11
+ pass_field = "#{base_field}_pass".to_sym
11
12
  comment_field = "#{base_field}_comment".to_sym
12
13
 
13
14
  form = @_current_form
@@ -30,7 +31,7 @@
30
31
  </label>
31
32
 
32
33
  <%= render 'chobble_forms/radio_pass_fail',
33
- field: field,
34
+ field: pass_field,
34
35
  prefilled: field_data[:prefilled],
35
36
  checked_value: checked_value %>
36
37
 
@@ -1,15 +1,16 @@
1
1
  <%
2
2
  # Composite partial for pass/fail + N/A + comment fields with grid layout
3
3
  # Required parameters:
4
- # field: Base field name (e.g., 'seam_integrity_pass')
4
+ # field: Base field name (e.g., 'seam_integrity')
5
5
  #
6
6
  # This will render:
7
- # - Pass/fail/N/A field: field (e.g., 'seam_integrity_pass')
7
+ # - Pass/fail/N/A field: base field + '_pass' (e.g., 'seam_integrity_pass')
8
8
  # - Enum fields: Pass = "pass", Fail = "fail", N/A = "na" (rendered as radio buttons)
9
9
  # - Boolean fields: Pass = true, Fail = false (rendered as radio buttons)
10
10
  # - Comment field: base field + '_comment' (e.g., 'seam_integrity_comment')
11
11
 
12
12
  base_field = field.to_s.gsub(/_pass$/, "")
13
+ pass_field = "#{base_field}_pass".to_sym
13
14
  comment_field = "#{base_field}_comment".to_sym
14
15
 
15
16
  form = @_current_form
@@ -31,7 +32,7 @@
31
32
  </label>
32
33
 
33
34
  <%= render 'chobble_forms/radio_pass_fail',
34
- field: field,
35
+ field: pass_field,
35
36
  prefilled: field_data[:prefilled],
36
37
  checked_value: checked_value %>
37
38
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: chobble-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.7.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chobble.com
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-08-13 00:00:00.000000000 Z
11
+ date: 2025-08-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails