diff --git a/lib/rails_admin/adapters/active_record.rb b/lib/rails_admin/adapters/active_record.rb index 4a01e94448..00fea84f7b 100644 --- a/lib/rails_admin/adapters/active_record.rb +++ b/lib/rails_admin/adapters/active_record.rb @@ -204,6 +204,7 @@ def build_statement_for_type when :string, :text then build_statement_for_string_or_text when :enum then build_statement_for_enum when :belongs_to_association then build_statement_for_belongs_to_association + when :uuid then build_statement_for_uuid end end @@ -223,22 +224,27 @@ def build_statement_for_belongs_to_association def build_statement_for_string_or_text return if @value.blank? + + unless ['postgresql', 'postgis'].include? ar_adapter + @value = @value.mb_chars.downcase + end + @value = begin case @operator when 'default', 'like' - "%#{@value.downcase}%" + "%#{@value}%" when 'starts_with' - "#{@value.downcase}%" + "#{@value}%" when 'ends_with' - "%#{@value.downcase}" + "%#{@value}" when 'is', '=' - @value.downcase + @value else return end end - if ar_adapter == 'postgresql' + if ['postgresql', 'postgis'].include? ar_adapter ["(#{@column} ILIKE ?)", @value] else ["(LOWER(#{@column}) LIKE ?)", @value] @@ -250,6 +256,12 @@ def build_statement_for_enum ["(#{@column} IN (?))", Array.wrap(@value)] end + def build_statement_for_uuid + if @value.to_s =~ /\A[0-9a-f]{8}-([0-9a-f]{4}-){3}[0-9a-f]{12}\z/ + column_for_value(@value) + end + end + def ar_adapter ::ActiveRecord::Base.connection.adapter_name.downcase end diff --git a/spec/rails_admin/adapters/active_record_spec.rb b/spec/rails_admin/adapters/active_record_spec.rb index 86113a8e00..3f03176a1c 100644 --- a/spec/rails_admin/adapters/active_record_spec.rb +++ b/spec/rails_admin/adapters/active_record_spec.rb @@ -3,7 +3,7 @@ describe 'RailsAdmin::Adapters::ActiveRecord', active_record: true do before do - @like = if ::ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'postgresql' + @like = if ['postgresql', 'postgis'].include? ::ActiveRecord::Base.configurations[Rails.env]['adapter'] '(field ILIKE ?)' else '(LOWER(field) LIKE ?)' @@ -36,7 +36,11 @@ def predicates_for(scope) let(:abstract_model) { RailsAdmin::AbstractModel.new('Player') } before do - @players = FactoryGirl.create_list(:player, 3) + @players = FactoryGirl.create_list(:player, 3) + [ + # Multibyte players + FactoryGirl.create(:player, name: 'Антоха'), + FactoryGirl.create(:player, name: 'Петруха'), + ] end it '#new returns instance of AbstractObject' do @@ -77,7 +81,7 @@ class PlayerWithDefaultScope < Player it '#destroy destroys multiple items' do abstract_model.destroy(@players[0..1]) - expect(Player.all).to eq(@players[2..2]) + expect(Player.all).to eq(@players[2..-1]) end it '#where returns filtered results' do @@ -102,8 +106,8 @@ class PlayerWithDefaultScope < Player end it 'supports pagination' do - expect(abstract_model.all(sort: 'id', page: 2, per: 1)).to eq(@players[1..1]) - expect(abstract_model.all(sort: 'id', page: 1, per: 2)).to eq(@players[1..2].reverse) + expect(abstract_model.all(sort: 'id', page: 2, per: 1)).to eq(@players[-2, 1]) + expect(abstract_model.all(sort: 'id', page: 1, per: 2)).to eq(@players[-2, 2].reverse) end it 'supports ordering' do @@ -115,6 +119,13 @@ class PlayerWithDefaultScope < Player expect(results).to eq(@players[1..1]) end + it 'supports multibyte querying' do + unless ::ActiveRecord::Base.configurations[Rails.env]['adapter'] == 'sqlite3' + results = abstract_model.all(query: @players[4].name) + expect(results).to eq(@players[4, 1]) + end + end + it 'supports filtering' do expect(abstract_model.all(filters: {'name' => {'0000' => {o: 'is', v: @players[1].name}}})).to eq(@players[1..1]) end @@ -201,8 +212,10 @@ def build_statement(type, value, operator) end it 'performs case-insensitive searches' do - expect(build_statement(:string, 'foo', 'default')).to eq([@like, '%foo%']) - expect(build_statement(:string, 'FOO', 'default')).to eq([@like, '%foo%']) + unless ['postgresql', 'postgis'].include?(::ActiveRecord::Base.configurations[Rails.env]['adapter']) + expect(build_statement(:string, 'foo', 'default')).to eq([@like, '%foo%']) + expect(build_statement(:string, 'FOO', 'default')).to eq([@like, '%foo%']) + end end it "supports '_blank' operator" do @@ -380,6 +393,11 @@ def build_statement(type, value, operator) it 'supports enum type query' do expect(build_statement(:enum, '1', nil)).to eq(['(field IN (?))', ['1']]) end + + it 'supports uuid type query' do + uuid = SecureRandom.uuid + expect(build_statement(:uuid, uuid, nil)).to eq(['(field = ?)', uuid]) + end end describe 'model attribute method' do