lib/active_scaffold/finder.rb in active_scaffold-3.6.0.pre vs lib/active_scaffold/finder.rb in active_scaffold-3.6.0.rc1
- old
+ new
@@ -1,9 +1,9 @@
module ActiveScaffold
module Finder
def self.like_operator
- @@like_operator ||= ::ActiveRecord::Base.connection.adapter_name == 'PostgreSQL' ? 'ILIKE' : 'LIKE'
+ @@like_operator ||= ::ActiveRecord::Base.connection.adapter_name.in?(%w[PostgreSQL PostGIS]) ? 'ILIKE' : 'LIKE'
end
module ClassMethods
def self.extended(klass)
return unless klass.active_scaffold_config
@@ -66,11 +66,13 @@
conditions = tokens.map do |columns_token|
token_conditions = columns.map do |column|
value = columns_token[column.name]
value = /#{value}/ if column.text?
column.search_sql.map do |search_sql|
- {search_sql => value}
+ # call .to_s so String is returned from CowProxy::String in threadsafe mode
+ # in other case, or method from Mongoid would fail
+ {search_sql.to_s => value}
end
end.flatten
active_scaffold_config.model.or(token_conditions).selector
end
[active_scaffold_config.model.and(conditions).selector]
@@ -99,16 +101,14 @@
search_ui = column.search_ui || column.column_type
begin
sql, *values =
if search_ui && respond_to?("condition_for_#{search_ui}_type")
send("condition_for_#{search_ui}_type", column, value, like_pattern)
+ elsif column.search_sql.instance_of? Proc
+ column.search_sql.call(value)
else
- if column.search_sql.instance_of? Proc
- column.search_sql.call(value)
- else
- condition_for_search_ui(column, value, like_pattern, search_ui)
- end
+ condition_for_search_ui(column, value, like_pattern, search_ui)
end
return nil unless sql
conditions = [column.search_sql.collect { |search_sql| sql % {:search_sql => search_sql} }.join(' OR ')]
conditions += values * column.search_sql.size if values.present?
@@ -174,16 +174,18 @@
["%<search_sql>s #{value[:opt]} ?", value[:from]]
end
end
def tables_for_translating_days_and_months(format)
+ # rubocop:disable Style/FormatStringToken
keys = {
'%A' => 'date.day_names',
'%a' => 'date.abbr_day_names',
'%B' => 'date.month_names',
'%b' => 'date.abbr_month_names'
}
+ # rubocop:enable Style/FormatStringToken
key_index = keys.keys.map { |key| [key, format.index(key)] }.to_h
keys.select! { |k, _| key_index[k] }
keys.sort_by { |k, _| key_index[k] }.map do |_, k|
I18n.t(k).compact.zip(I18n.t(k, :locale => :en).compact).to_h
end
@@ -224,49 +226,77 @@
format.gsub!(/.*(?=%H)/, '') if !parts[:year] && !parts[:month] && !parts[:mday]
[format, parts[:offset]]
end
+ def local_time_from_hash(value, conversion = :to_time)
+ time = Time.zone.local(*%i[year month day hour minute second].collect { |part| value[part].to_i })
+ time.send(conversion)
+ rescue StandardError => e
+ message = "Error creating time from #{value.inspect}:"
+ Rails.logger.warn "#{message}\n#{e.message}\n#{e.backtrace.join("\n")}"
+ nil
+ end
+
+ def parse_date_with_format(value, format_name)
+ format = I18n.t("date.formats.#{format_name || :default}")
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
+ en_value = I18n.locale == :en ? value : translate_days_and_months(value, format)
+ Date.strptime(en_value, format)
+ rescue StandardError => e
+ message = "Error parsing date from #{en_value}"
+ message << " (#{value})" if en_value != value
+ message << ", with format #{format}" if format
+ Rails.logger.warn "#{message}:\n#{e.message}\n#{e.backtrace.join("\n")}"
+ nil
+ end
+
+ def parse_time_with_format(value, format, offset)
+ format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
+ en_value = I18n.locale == :en ? value : translate_days_and_months(value, format)
+ time = Time.strptime(en_value, format)
+ offset ? time : Time.zone.local_to_utc(time).in_time_zone
+ rescue StandardError => e
+ message = "Error parsing time from #{en_value}"
+ message << " (#{value})" if en_value != value
+ message << ", with format #{format}" if format
+ Rails.logger.warn "#{message}:\n#{e.message}\n#{e.backtrace.join("\n")}"
+ nil
+ end
+
def condition_value_for_datetime(column, value, conversion = :to_time)
- unless value.nil? || value.blank?
- if value.is_a? Hash
- time = Time.zone.local(*%i[year month day hour minute second].collect { |part| value[part].to_i }) rescue nil
- time&.send(conversion)
- elsif value.respond_to?(:strftime)
- if conversion == :to_time
- # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
- # https://github.com/rails/rails/pull/2453
- value.to_time.in_time_zone
- else
- value.send(conversion)
- end
- elsif conversion == :to_date
- format = I18n.t("date.formats.#{column.options[:format] || :default}")
- format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
- value = translate_days_and_months(value, format) if I18n.locale != :en
- Date.strptime(value, format) rescue nil
- elsif value.include?('T')
- Time.zone.parse(value)
- else # datetime
- format, offset = format_for_datetime(column, value)
- format.gsub!(/%-d|%-m|%_m/) { |s| s.gsub(/[-_]/, '') } # strptime fails with %-d, %-m, %_m
- value = translate_days_and_months(value, format) if I18n.locale != :en
- time = Time.strptime(value, format) rescue nil
- if time
- time = Time.zone.local_to_utc(time).in_time_zone unless offset
- time = time.send(conversion) unless conversion == :to_time
- end
- time
+ return if value.nil? || value.blank?
+ if value.is_a? Hash
+ local_time_from_hash(value, conversion)
+ elsif value.respond_to?(:strftime)
+ if conversion == :to_time
+ # Explicitly get the current zone, because TimeWithZone#to_time in rails 3.2.3 returns UTC.
+ # https://github.com/rails/rails/pull/2453
+ value.to_time.in_time_zone
+ else
+ value.send(conversion)
end
+ elsif conversion == :to_date
+ parse_date_with_format(value, column.options[:format])
+ elsif value.include?('T')
+ Time.zone.parse(value)
+ else # datetime
+ time = parse_time_with_format(value, *format_for_datetime(column, value))
+ conversion == :to_time ? time : time.send(conversion)
end
end
def condition_value_for_numeric(column, value)
return value if value.nil?
value = column.number_to_native(value) if column.options[:format] && column.search_ui != :number
case (column.search_ui || column.column.type)
- when :integer then value.to_i rescue value ? 1 : 0
+ when :integer then
+ if value.is_a?(TrueClass) || value.is_a?(FalseClass)
+ value ? 1 : 0
+ else
+ value.to_i
+ end
when :float then value.to_f
when :decimal
::ActiveRecord::Type::Decimal.new.type_cast_from_user(value)
else
value
@@ -360,14 +390,16 @@
attr_writer :active_scaffold_references
def active_scaffold_references
@active_scaffold_references ||= []
end
- # Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
+ # Override this method on your controller to define conditions to be used when querying a recordset (e.g. for List).
+ # The return of this method should be any format compatible with the :conditions clause of ActiveRecord::Base's find.
def conditions_for_collection; end
- # Override this method on your controller to define joins to be used when querying a recordset (e.g. for List). The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
+ # Override this method on your controller to define joins to be used when querying a recordset (e.g. for List).
+ # The return of this method should be any format compatible with the :joins clause of ActiveRecord::Base's find.
def joins_for_collection; end
# Override this method on your controller to provide custom finder options to the find() call. The return of this method should be a hash.
def custom_finder_options
{}
@@ -401,19 +433,22 @@
raise ActiveScaffold::RecordNotAllowed, "#{klass} with id = #{id}" unless record.authorized_for? security_options
record
end
# valid options may include:
- # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction, e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]). please note that multi-column sorting has some limitations: if any column in a multi-field sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
+ # * :sorting - a Sorting DataStructure (basically an array of hashes of field => direction,
+ # e.g. [{:field1 => 'asc'}, {:field2 => 'desc'}]).
+ # please note that multi-column sorting has some limitations: if any column in a multi-field
+ # sort uses method-based sorting, it will be ignored. method sorting only works for single-column sorting.
# * :per_page
# * :page
def finder_options(options = {})
search_conditions = all_conditions
# create a general-use options array that's compatible with Rails finders
finder_options = {
- :reorder => options[:sorting]&.clause((grouped_columns_calculations if grouped_search?)),
+ :reorder => options[:sorting]&.clause((grouped_columns_calculations if grouped_search?)).map(&Arel.method(:sql)),
:conditions => search_conditions
}
if active_scaffold_config.mongoid?
finder_options[:includes] = [active_scaffold_references, active_scaffold_preload].compact.flatten.uniq.presence
else
@@ -478,11 +513,11 @@
end
pager.page(options[:page])
end
def calculate_last_modified(query)
- return unless conditional_get_support? && query.klass.columns_hash['updated_at']
+ return unless conditional_get_support? && ActiveScaffold::OrmChecks.columns_hash(query.klass)['updated_at']
@last_modified = query.maximum(:updated_at)
end
def calculate_query
conditions = all_conditions
@@ -495,16 +530,16 @@
subquery = subquery.unscope(:order)
active_scaffold_config.model.where(primary_key => subquery)
end
def append_to_query(relation, options)
- options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset, :joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly, :from, :conditions, :preload, :references
+ options.assert_valid_keys :where, :select, :having, :group, :reorder, :order, :limit, :offset,
+ :joins, :left_joins, :left_outer_joins, :includes, :lock, :readonly,
+ :from, :conditions, :preload, :references
relation = options.reject { |_, v| v.blank? }.inject(relation) do |rel, (k, v)|
k == :conditions ? apply_conditions(rel, *v) : rel.send(k, v)
end
- if options[:left_outer_joins].present? || options[:left_joins].present?
- relation.distinct_value = true
- end
+ relation.distinct_value = true if options[:left_outer_joins].present? || options[:left_joins].present?
relation
end
def joins_for_finder
case joins_for_collection