active_scaffold 4.2.1 → 4.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: 0ce255799e0bff6d3854d55afdf9278d3a283f68f6d0b1a639acf5bd229b359a
4
- data.tar.gz: 62761ad0b3530526880f7d8ac464f1f61623f76a6cbdd55512ab6e6102b6f963
3
+ metadata.gz: d279597d030fea168fba4fbcc8cd44c99d02f44b4596c774ec4476db04d53cd1
4
+ data.tar.gz: 48fd530b7990ec28454a1b3adebd5da80ca1d833727b22563a85cef1e140747f
5
5
  SHA512:
6
- metadata.gz: ad75a5aef07ab0ed9a6997847249ebfd30b749ffe6502472a74066e86ff08fd196b85579bdec112108a71d149f7918af79cfeec64cdee23d095440594a33dba0
7
- data.tar.gz: 6bcb9392ee5554b46ebb708d957b061c890b23fc613298c8296370276480b27bae10c4dc7fbb683aeed615aec9bbb1c83de7ac11ada6e1714fcf1d5b6958813b
6
+ metadata.gz: 1317103cf2e2344d0340f76f020a7df076f7aedff8f07b1c0a19499338d2d7941e762c51b33757cbcf3cd039d0d9f8bc8b57653282098f23fadfc069fd631bf0
7
+ data.tar.gz: 1300926c2397ea724f788a2b9899397e6c1e0ec6ca5a55a77677edf93dc98e5ed70e1b742c9a6ee8618067e4c378e58899d18e0fba9514e41cbde943c5589c01
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,13 @@
1
+ = 4.2.3
2
+ - Make simplified searches on LogicalQueryParser bridge working with upstream gem, no need for my fork
3
+ - Styles for radio form_ui
4
+ - Fix style for checkbox list in subform
5
+ - Fix constraints (broken on 4.2.2 on some cases)
6
+
7
+ = 4.2.2
8
+ - Support setting form_ui_options with nil, for subforms
9
+ - Support saving when embedded on through singular association, as it's supported for nested too
10
+
1
11
  = 4.2.1
2
12
  - Fix replacing tinymce fields with update_columns
3
13
  - Support refresh_link in radio form UI
@@ -750,6 +750,12 @@ float: left;
750
750
  .active-scaffold li.form-element dd input[type="checkbox"] {
751
751
  margin-top: 6px;
752
752
  }
753
+ .active-scaffold form input[type=radio] {
754
+ margin-right: 5px;
755
+ }
756
+ .active-scaffold form label:has(input[type=radio]) {
757
+ margin-right: 10px;
758
+ }
753
759
 
754
760
  .active-scaffold li.form-element dd .select-field:not(.draggable-lists-container) {
755
761
  display: inline-block;
@@ -1039,7 +1045,7 @@ padding: 0 2px 2px 2px;
1039
1045
  border: solid 1px;
1040
1046
  }
1041
1047
 
1042
- .active-scaffold .sub-form .checkbox-list label {
1048
+ .active-scaffold .sub-form .checkbox-list li {
1043
1049
  display: block;
1044
1050
  }
1045
1051
 
@@ -146,6 +146,16 @@ module ActiveScaffold::Actions
146
146
  "#{params[:parent_controller].camelize}Controller"
147
147
  end
148
148
 
149
+ def create_on_through_singular(record, association, parent_record)
150
+ through = parent_record.send(association.through_reflection.name) ||
151
+ parent_record.send(:"build_#{association.through_reflection.name}")
152
+ if association.source_reflection.reverse_association.collection?
153
+ record.send(association.source_reflection.reverse) << through
154
+ else
155
+ record.send(:"#{association.source_reflection.reverse}=", through)
156
+ end
157
+ end
158
+
149
159
  def setup_parent
150
160
  cfg = main_form_controller.active_scaffold_config
151
161
  parent = cfg.model.new
@@ -107,13 +107,7 @@ module ActiveScaffold::Actions
107
107
  if nested.child_association&.singular?
108
108
  record.send(:"#{nested.child_association.name}=", nested_parent_record)
109
109
  elsif nested.create_through_singular?
110
- through = nested_parent_record.send(nested.association.through_reflection.name) ||
111
- nested_parent_record.send(:"build_#{nested.association.through_reflection.name}")
112
- if nested.source_reflection.reverse_association.collection?
113
- record.send(nested.source_reflection.reverse) << through
114
- else
115
- record.send(:"#{nested.source_reflection.reverse}=", through)
116
- end
110
+ create_on_through_singular(record, nested.association, nested_parent_record)
117
111
  else
118
112
  record.send(nested.child_association.name) << nested_parent_record
119
113
  end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'method_source'
4
+
5
+ class ActiveScaffold::Bridges::LogicalQueryParser
6
+ class KeywordQueryParser
7
+ LogicalQueryParser.singleton_methods.each do |method_name|
8
+ method = LogicalQueryParser.method(method_name)
9
+ define_method(method_name, &method)
10
+ end
11
+
12
+ # Copy search method from LogicalQueryParser
13
+ class_eval(LogicalQueryParser.method(:search).source, __FILE__, __LINE__)
14
+
15
+ def initialize(operator)
16
+ @operator = operator
17
+ end
18
+
19
+ def new
20
+ ActiveScaffold::Bridges::LogicalQueryParser::TokensGrammar::Parser.new(@operator)
21
+ end
22
+ end
23
+ end
@@ -1,9 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class ActiveScaffold::Bridges::LogicalQueryParser < ActiveScaffold::DataStructures::Bridge
4
+ autoload :TokensGrammar, 'active_scaffold/bridges/logical_query_parser/tokens_grammar'
5
+ autoload :KeywordQueryParser, 'active_scaffold/bridges/logical_query_parser/keyword_query_parser'
6
+
4
7
  def self.install
5
- require File.join(File.dirname(__FILE__), 'logical_query_parser/tokens_grammar')
6
- ActiveScaffold::Finder.send(:remove_const, :LOGICAL_COMPARATORS)
7
8
  ActiveScaffold::Finder.const_set :LOGICAL_COMPARATORS, %w[all_tokens any_token logical].freeze
8
9
  end
9
10
  end
@@ -155,6 +155,41 @@ module ActiveScaffold
155
155
  "Malformed constraint `#{klass}##{column_name}'. If it's a legitimate column, and you are using a nested scaffold, please specify or double-check the reverse association name."
156
156
  end
157
157
 
158
+ def apply_constraint_on_association(record, association, value, allow_autosave: false)
159
+ if association.through_singular? && association.source_reflection.reverse
160
+ create_on_through_singular(record, association, association.klass.find(value))
161
+ elsif association.collection?
162
+ record.send(k.to_s).send(:<<, association.klass.find(value)) unless association.nested?
163
+ elsif association.polymorphic?
164
+ apply_constraint_on_polymorphic_association(record, association, value)
165
+ elsif !association.source_reflection&.through? && # regular singular association, or one-level through association
166
+ !value.is_a?(Array)
167
+ record.send(:"#{association.name}=", association.klass.find(value))
168
+
169
+ # setting the belongs_to side of a has_one isn't safe. if the has_one was already
170
+ # specified, rails won't automatically clear out the previous associated record.
171
+ #
172
+ # note that we can't take the extra step to correct this unless we're permitted to
173
+ # run operations where activerecord auto-saves the object.
174
+ reverse = association.reverse_association
175
+ if reverse&.singular? && !reverse.belongs_to? && allow_autosave
176
+ record.send(association.name).send(:"#{reverse.name}=", record)
177
+ end
178
+ end
179
+ end
180
+
181
+ def apply_constraint_on_polymorphic_association(record, association, value)
182
+ unless value.is_a?(Array) && value.size >= 2
183
+ raise ActiveScaffold::MalformedConstraint, polymorphic_constraint_error(association), caller
184
+ end
185
+
186
+ if value.size == 2
187
+ record.send(:"#{association.name}=", value[0].constantize.find(value[1]))
188
+ else
189
+ record.send(:"#{association.foreign_type}=", value[0])
190
+ end
191
+ end
192
+
158
193
  # Applies constraints to the given record.
159
194
  #
160
195
  # Searches through the known columns for association columns. If the given constraint is an association,
@@ -163,40 +198,12 @@ module ActiveScaffold
163
198
  #
164
199
  # For some operations ActiveRecord will automatically update the database. That's not always ok.
165
200
  # If it *is* ok (e.g. you're in a transaction), then set :allow_autosave to true.
166
- def apply_constraints_to_record(record, options = {})
167
- options[:allow_autosave] = false if options[:allow_autosave].nil?
168
- constraints = options[:constraints] || active_scaffold_constraints
169
-
201
+ def apply_constraints_to_record(record, allow_autosave: false, constraints: active_scaffold_constraints)
170
202
  config = record.is_a?(active_scaffold_config.model) ? active_scaffold_config : active_scaffold_config_for(record.class)
171
203
  constraints.each do |k, v|
172
204
  column = config.columns[k]
173
205
  if column&.association
174
- if column.association.collection?
175
- record.send(k.to_s).send(:<<, column.association.klass.find(v)) unless column.association.nested?
176
- elsif column.association.polymorphic?
177
- unless v.is_a?(Array) && v.size >= 2
178
- raise ActiveScaffold::MalformedConstraint, polymorphic_constraint_error(column.association), caller
179
- end
180
-
181
- if v.size == 2
182
- record.send(:"#{k}=", v[0].constantize.find(v[1]))
183
- else
184
- record.send(:"#{column.association.foreign_type}=", v[0])
185
- end
186
- elsif !column.association.source_reflection&.options&.include?(:through) && # regular singular association, or one-level through association
187
- !v.is_a?(Array)
188
- record.send(:"#{k}=", column.association.klass.find(v))
189
-
190
- # setting the belongs_to side of a has_one isn't safe. if the has_one was already
191
- # specified, rails won't automatically clear out the previous associated record.
192
- #
193
- # note that we can't take the extra step to correct this unless we're permitted to
194
- # run operations where activerecord auto-saves the object.
195
- reverse = column.association.reverse_association
196
- if reverse&.singular? && !reverse.belongs_to? && options[:allow_autosave]
197
- record.send(k).send(:"#{reverse.name}=", record)
198
- end
199
- end
206
+ apply_constraint_on_association(record, column.association, v, allow_autosave: allow_autosave)
200
207
  else
201
208
  record.send(:"#{k}=", v)
202
209
  end
@@ -6,7 +6,7 @@ module ActiveScaffold::DataStructures::Association
6
6
  klass.reflect_on_all_associations
7
7
  end
8
8
 
9
- delegate :collection?, :polymorphic?, :association_primary_key, :foreign_type, :table_name, :nested?, :scope, to: :@association
9
+ delegate :collection?, :polymorphic?, :association_primary_key, :foreign_type, :table_name, :nested?, :scope, :class_name, to: :@association
10
10
 
11
11
  def through?
12
12
  @association.options[:through].present?
@@ -17,11 +17,11 @@ module ActiveScaffold::DataStructures::Association
17
17
  end
18
18
 
19
19
  def through_reflection
20
- @association.through_reflection if through?
20
+ @through_reflection ||= self.class.new(@association.through_reflection) if through?
21
21
  end
22
22
 
23
23
  def source_reflection
24
- @association.source_reflection if through?
24
+ @source_reflection ||= self.class.new(@association.source_reflection) if through?
25
25
  end
26
26
 
27
27
  def inverse_klass
@@ -346,7 +346,7 @@ module ActiveScaffold::DataStructures
346
346
  end
347
347
 
348
348
  def searchable?
349
- search_sql.present? || (logical_search.present? && ActiveScaffold::Finder::LOGICAL_COMPARATORS.present?)
349
+ search_sql.present? || (logical_search.present? && ActiveScaffold::Finder.logical_comparators.present?)
350
350
  end
351
351
 
352
352
  def link
@@ -746,10 +746,14 @@ module ActiveScaffold::DataStructures
746
746
 
747
747
  def valid_action_ui_params?(value)
748
748
  if value.is_a?(Array)
749
- value.size <= 2 && value[0].is_a?(Symbol) && (value[1].nil? || value[1].is_a?(Hash))
749
+ value.size <= 2 && valid_form_ui?(value[0]) && (value[1].nil? || value[1].is_a?(Hash))
750
750
  else
751
- value.nil? || value.is_a?(Symbol)
751
+ valid_form_ui?(value)
752
752
  end
753
753
  end
754
+
755
+ def valid_form_ui?(value)
756
+ value.nil? || value.is_a?(Symbol)
757
+ end
754
758
  end
755
759
  end
@@ -104,8 +104,8 @@ module ActiveScaffold::DataStructures
104
104
  # RatesController has employee in create action columns (reverse is vendor, and through association employee is in create form).
105
105
  def readonly_through_association?(columns)
106
106
  return false unless through_association?
107
- return true if association.through_reflection.options[:through] # create not possible, too many levels
108
- return true if association.source_reflection.options[:through] # create not possible, too many levels
107
+ return true if association.through_reflection.through? # create not possible, too many levels
108
+ return true if association.source_reflection.through? # create not possible, too many levels
109
109
  return false if create_through_singular? # create allowed, AS has code for this
110
110
  return false unless association.source_reflection.collection? # create allowed if source is singular, rails creates joint model
111
111
 
@@ -114,7 +114,7 @@ module ActiveScaffold::DataStructures
114
114
  end
115
115
 
116
116
  def create_through_singular?
117
- association.through_singular? && source_reflection.reverse
117
+ association.through_singular? && association.source_reflection.reverse
118
118
  end
119
119
 
120
120
  def create_with_parent?
@@ -125,10 +125,6 @@ module ActiveScaffold::DataStructures
125
125
  end
126
126
  end
127
127
 
128
- def source_reflection
129
- @source_reflection ||= ActiveScaffold::DataStructures::Association::ActiveRecord.new(association.source_reflection)
130
- end
131
-
132
128
  def through_association?
133
129
  association.through?
134
130
  end
@@ -6,6 +6,10 @@ module ActiveScaffold
6
6
  @@like_operator ||= ::ActiveRecord::Base.connection.adapter_name.in?(%w[PostgreSQL PostGIS]) ? 'ILIKE' : 'LIKE'
7
7
  end
8
8
 
9
+ def self.logical_comparators
10
+ ActiveScaffold::Finder::LOGICAL_COMPARATORS if ActiveScaffold::Finder.const_defined? :LOGICAL_COMPARATORS
11
+ end
12
+
9
13
  module ClassMethods
10
14
  def self.extended(klass)
11
15
  return unless klass.active_scaffold_config
@@ -231,21 +235,21 @@ module ActiveScaffold
231
235
  ['(%<search_sql>s BETWEEN ? AND ?)', value[:from], value[:to]]
232
236
  elsif ActiveScaffold::Finder::NUMERIC_COMPARATORS.include?(value[:opt])
233
237
  ["%<search_sql>s #{value[:opt]} ?", value[:from]]
234
- elsif ActiveScaffold::Finder::LOGICAL_COMPARATORS.include?(value[:opt])
238
+ elsif ActiveScaffold::Finder.logical_comparators&.include?(value[:opt])
235
239
  operator =
236
240
  case value[:opt]
237
241
  when 'all_tokens' then 'AND'
238
242
  when 'any_token' then 'OR'
239
243
  end
240
- parser = ActiveScaffold::Bridges::LogicalQueryParser::TokensGrammar::Parser.new(operator) if operator
241
- [logical_search_condition(column, value[:from], parser)]
244
+ parser = ActiveScaffold::Bridges::LogicalQueryParser::KeywordQueryParser.new(operator) if operator
245
+ [logical_search_condition(column, value[:from], parser || ::LogicalQueryParser)]
242
246
  end
243
247
  end
244
248
 
245
- def logical_search_condition(column, search, parser = nil)
249
+ def logical_search_condition(column, search, parser)
246
250
  model = column.active_record_class
247
251
  subquery = alias_query_for_same_table_exists(model.all) if column.logical_search.any?(Hash)
248
- query = ::LogicalQueryParser.search(search, subquery || model, columns: column.logical_search, parser: parser)
252
+ query = parser.search(search, subquery || model, column.logical_search)
249
253
  if subquery
250
254
  model.where(same_table_exists_subquery(query))
251
255
  else
@@ -526,7 +530,6 @@ module ActiveScaffold
526
530
  doesnt_begin_with: 'not_?%',
527
531
  doesnt_end_with: 'not_%?'
528
532
  }.freeze
529
- LOGICAL_COMPARATORS = [].freeze
530
533
  NULL_COMPARATORS = %w[null not_null].freeze
531
534
  DATE_COMPARATORS = %w[PAST FUTURE RANGE].freeze
532
535
  DATE_UNITS = %w[DAYS WEEKS MONTHS YEARS].freeze
@@ -167,15 +167,13 @@ module ActiveScaffold
167
167
  def build_associated(association, parent_record)
168
168
  if association.through? && association.through_reflection.collection?
169
169
  # build full chain, only check create_associated on initial parent_record
170
- parent_record = build_associated(association.class.new(association.through_reflection), parent_record)
171
- source_assoc = association.class.new(association.source_reflection)
172
- build_associated(source_assoc, parent_record).tap do |record|
173
- save_record_to_association(record, source_assoc.reverse_association, parent_record) # set inverse
170
+ parent_record = build_associated(association.through_reflection, parent_record)
171
+ build_associated(association.source_reflection, parent_record).tap do |record|
172
+ save_record_to_association(record, association.source_reflection.reverse_association, parent_record) # set inverse
174
173
  end
175
174
  elsif association.through? # through belongs_to/has_one
176
175
  parent_record = parent_record.send(association.through_reflection.name)
177
- source_assoc = association.class.new(association.source_reflection)
178
- build_associated(source_assoc, parent_record)
176
+ build_associated(association.source_reflection, parent_record)
179
177
  elsif association.collection?
180
178
  parent_record.send(association.name).build
181
179
  elsif association.belongs_to? || parent_record.new_record? || parent_record.send(association.name).nil?
@@ -213,8 +213,8 @@ module ActiveScaffold
213
213
  if column.search_sql.present?
214
214
  select_options.concat(ActiveScaffold::Finder::STRING_COMPARATORS.collect { |title, comp| [as_(title), comp] })
215
215
  end
216
- if ActiveScaffold::Finder::LOGICAL_COMPARATORS.present? && column.logical_search.present?
217
- select_options.concat(ActiveScaffold::Finder::LOGICAL_COMPARATORS.collect { |comp| [as_(comp.downcase.to_sym), comp] })
216
+ if ActiveScaffold::Finder.logical_comparators.present? && column.logical_search.present?
217
+ select_options.concat(ActiveScaffold::Finder.logical_comparators.collect { |comp| [as_(comp.downcase.to_sym), comp] })
218
218
  end
219
219
  end
220
220
  if column.search_sql.present?
@@ -4,7 +4,7 @@ module ActiveScaffold
4
4
  module Version
5
5
  MAJOR = 4
6
6
  MINOR = 2
7
- PATCH = 1
7
+ PATCH = 3
8
8
  FIX = nil
9
9
 
10
10
  STRING = [MAJOR, MINOR, PATCH, FIX].compact.join('.')
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_scaffold
3
3
  version: !ruby/object:Gem::Version
4
- version: 4.2.1
4
+ version: 4.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Many, see README
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-09 00:00:00.000000000 Z
11
+ date: 2026-02-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dartsass-sprockets
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0.11'
41
+ - !ruby/object:Gem::Dependency
42
+ name: method_source
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.1'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.1'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: rails
43
57
  requirement: !ruby/object:Gem::Requirement
@@ -242,6 +256,7 @@ files:
242
256
  - lib/active_scaffold/bridges/file_column/test/functional/file_column_keep_test.rb
243
257
  - lib/active_scaffold/bridges/file_column/test/mock_model.rb
244
258
  - lib/active_scaffold/bridges/logical_query_parser.rb
259
+ - lib/active_scaffold/bridges/logical_query_parser/keyword_query_parser.rb
245
260
  - lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.rb
246
261
  - lib/active_scaffold/bridges/logical_query_parser/tokens_grammar.treetop
247
262
  - lib/active_scaffold/bridges/paper_trail.rb