Skip to content
This repository was archived by the owner on Jan 3, 2024. It is now read-only.

Commit beef7bd

Browse files
committed
added integration specs using combustion, added specs for array methods
1 parent 95ec6d1 commit beef7bd

File tree

13 files changed

+230
-24
lines changed

13 files changed

+230
-24
lines changed

Gemfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
source :rubygems
22

33
gemspec
4+
# fix for failing tests with combustion 0.3.1 and rails 3.2.0
5+
gem "rails", "~> 3.1.0"

activerecord-postgres-array.gemspec

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,9 @@ Gem::Specification.new do |s|
1717
s.rubygems_version = %q{1.3.7}
1818
s.summary = s.description
1919

20+
s.add_dependency "rails"
2021
s.add_development_dependency 'rake'
2122
s.add_development_dependency 'rspec', '~> 2.0'
23+
s.add_development_dependency 'pg'
24+
s.add_development_dependency 'combustion', '~> 0.3.1'
2225
end
23-

lib/activerecord-postgres-array/activerecord.rb

Lines changed: 25 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@ module ActiveRecord
22
class ArrayTypeMismatch < ActiveRecord::ActiveRecordError
33
end
44

5+
class Base
6+
def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
7+
attrs = {}
8+
klass = self.class
9+
arel_table = klass.arel_table
10+
11+
attribute_names.each do |name|
12+
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
13+
if include_readonly_attributes || !self.class.readonly_attributes.include?(name)
14+
value = read_attribute(name)
15+
if column.type.to_s =~ /_array$/ && value && value.is_a?(Array)
16+
value = value.to_postgres_array(new_record?)
17+
elsif klass.serialized_attributes.include?(name)
18+
value = @attributes[name].serialized_value
19+
end
20+
attrs[arel_table[name]] = value
21+
end
22+
end
23+
end
24+
25+
attrs
26+
end
27+
end
28+
529
module ConnectionAdapters
630
class PostgreSQLAdapter < AbstractAdapter
731
POSTGRES_ARRAY_TYPES = %w( string text integer float decimal datetime timestamp time date binary boolean )
@@ -14,7 +38,7 @@ def native_database_types_with_array(*args)
1438
# Quotes a value for use in an SQL statement
1539
def quote_with_array(value, column = nil)
1640
if value && column && column.sql_type =~ /\[\]$/
17-
raise ArrayTypeMismatch, "#{column.name} must have a valid array value (#{value})" unless value.kind_of?(Array) || value.valid_postgres_array?
41+
raise ArrayTypeMismatch, "#{column.name} must be an Array or have a valid array value (#{value})" unless value.kind_of?(Array) || value.valid_postgres_array?
1842
return value.to_postgres_array
1943
end
2044
quote_without_array(value,column)
@@ -52,7 +76,6 @@ def type_cast_code_with_array(var_name)
5276
end
5377
alias_method_chain :type_cast_code, :array
5478

55-
5679
# Adds the array type for the column.
5780
def simplified_type_with_array(field_type)
5881
if field_type =~ /^numeric.+\[\]$/
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,28 @@
11
class Array
2-
32
# Generates a single quoted postgres array string format. This is the format used
43
# to insert or update stuff in the database.
54
def to_postgres_array(omit_quotes = false)
65
result = "#{omit_quotes ? '' : "'" }{"
7-
6+
87
result << collect do |value|
98
if value.is_a?(Array)
109
value.to_postgres_array(true)
11-
else
10+
elsif value.is_a?(String)
1211
value = value.gsub(/\\/, '\&\&')
1312
value = value.gsub(/'/, "''")
1413
value = value.gsub(/"/, '\"')
1514
value = "\"#{ value }\""
1615
value
16+
else
17+
value
1718
end
18-
end.join(", ")
19-
19+
end.join(",")
20+
2021
result << "}#{omit_quotes ? '' : "'" }"
2122
end
2223

2324
# If the method from_postgres_array is called in an Array, it just returns self.
2425
def from_postgres_array(base_type = :string)
2526
self
2627
end
27-
2828
end

lib/activerecord-postgres-array/string.rb

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,28 +8,27 @@ def to_postgres_array
88
# * A string like '{10000, 10000, 10000, 10000}'
99
# * TODO A multi dimensional array string like '{{"meeting", "lunch"}, {"training", "presentation"}}'
1010
def valid_postgres_array?
11-
quoted_string_regexp = /"[^"\\]*(?:\\.[^"\\]*)*"|'[^'\\]*(?:\\.[^'\\]*)*'/
11+
string_regexp = /[^",\\]+/
12+
quoted_string_regexp = /"[^"\\]*(?:\\.[^"\\]*)*"/
1213
number_regexp = /[-+]?[0-9]*\.?[0-9]+/
13-
!!match(/^\s*(\{\s*(#{number_regexp}|#{quoted_string_regexp})(\s*\,\s*(#{number_regexp}|#{quoted_string_regexp}))*\})?\s*$/)
14+
validation_regexp = /\{\s*(#{number_regexp}|#{quoted_string_regexp}|#{string_regexp})(\s*\,\s*(#{number_regexp}|#{quoted_string_regexp}|#{string_regexp}))*\}/
15+
!!match(/^\s*('#{validation_regexp}'|#{validation_regexp})?\s*$/)
1416
end
1517

1618
# Creates an array from a postgres array string that postgresql spits out.
1719
def from_postgres_array(base_type = :string)
1820
if empty?
19-
return []
21+
[]
2022
else
21-
elements = match(/^\{(.+)\}$/).captures.first.split(",")
23+
elements = match(/\{(.*)\}/).captures.first.gsub(/\\"/, '$ESCAPED_DOUBLE_QUOTE$').split(/(,)(?=(?:[^"]|"[^"]*")*$)/).reject {|e| e == ',' }
2224
elements = elements.map do |e|
23-
e = e.gsub(/\\"/, '"')
24-
e = e.gsub(/^\"/, '')
25-
e = e.gsub(/\"$/, '')
26-
e = e.strip
25+
e.gsub('$ESCAPED_DOUBLE_QUOTE$', '"').gsub("\\\\", "\\").gsub(/^"/, '').gsub(/"$/, '').gsub("''", "'").strip
2726
end
28-
27+
2928
if base_type == :decimal
30-
return elements.collect(&:to_d)
29+
elements.collect(&:to_d)
3130
else
32-
return elements
31+
elements
3332
end
3433
end
3534
end

spec/array_ext_spec.rb

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
require 'spec_helper'
2+
require 'activerecord-postgres-array/array'
3+
4+
describe "Array" do
5+
describe "#to_postgres_array" do
6+
it "returns '{}' if used on an empty array" do
7+
[].to_postgres_array.should == "'{}'"
8+
end
9+
10+
it "returns a correct array if used on a numerical array" do
11+
[1,2,3].to_postgres_array.should == "'{1,2,3}'"
12+
end
13+
14+
it "returns a correct array if used on a string array" do
15+
["Ruby","on","Rails"].to_postgres_array.should == "'{\"Ruby\",\"on\",\"Rails\"}'"
16+
end
17+
18+
it "escapes double quotes correctly" do
19+
["Ruby","on","Ra\"ils"].to_postgres_array.should == "'{\"Ruby\",\"on\",\"Ra\\\"ils\"}'"
20+
end
21+
22+
it "escapes backslashes correctly" do
23+
["\\","\""].to_postgres_array.should == '\'{"\\\\","\\""}\''
24+
end
25+
end
26+
end

spec/integration_spec.rb

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
require 'spec_helper'
2+
3+
describe Article do
4+
describe ".create" do
5+
it "builds valid arrays" do
6+
article = Article.create(:languages => ["English", "German"])
7+
article.reload
8+
article.languages_before_type_cast.should == "{English,German}"
9+
article.languages.should == ["English", "German"]
10+
end
11+
12+
it "escapes single quotes correctly" do
13+
article = Article.create(:languages => ["English", "Ger'man"])
14+
article.reload
15+
article.languages_before_type_cast.should == "{English,Ger''man}"
16+
article.languages.should == ["English", "Ger'man"]
17+
end
18+
19+
it "escapes double quotes correctly" do
20+
article = Article.create(:languages => ["English", "Ger\"man"])
21+
article.reload
22+
article.languages_before_type_cast.should == "{English,\"Ger\\\"man\"}"
23+
article.languages.should == ["English", "Ger\"man"]
24+
end
25+
26+
it "handles commas correctly" do
27+
article = Article.create(:languages => ["English", "Ger,man"])
28+
article.reload
29+
article.languages_before_type_cast.should == "{English,\"Ger,man\"}"
30+
article.languages.should == ["English", "Ger,man"]
31+
end
32+
33+
it "handles backslashes correctly" do
34+
article = Article.create(:languages => ["\\","\""])
35+
article.reload
36+
article.languages_before_type_cast.should == '{"\\\\","\\""}'
37+
article.languages.should == ["\\","\""]
38+
end
39+
end
40+
41+
describe ".update" do
42+
before(:each) do
43+
@article = Article.create
44+
end
45+
46+
it "builds valid arrays" do
47+
@article.languages = ["English", "German"]
48+
@article.save
49+
@article.reload
50+
@article.languages_before_type_cast.should == "{English,German}"
51+
end
52+
53+
it "escapes single quotes correctly" do
54+
@article.languages = ["English", "Ger'man"]
55+
@article.save
56+
@article.reload
57+
@article.languages_before_type_cast.should == "{English,Ger'man}"
58+
@article.languages.should == ["English", "Ger'man"]
59+
end
60+
61+
it "escapes double quotes correctly" do
62+
@article.languages = ["English", "Ger\"man"]
63+
@article.save
64+
@article.reload
65+
@article.languages_before_type_cast.should == "{English,\"Ger\\\"man\"}"
66+
@article.languages.should == ["English", "Ger\"man"]
67+
end
68+
69+
it "handles commas correctly" do
70+
@article.languages = ["English", "Ger,man"]
71+
@article.save
72+
@article.reload
73+
@article.languages_before_type_cast.should == "{English,\"Ger,man\"}"
74+
@article.languages.should == ["English", "Ger,man"]
75+
end
76+
77+
it "handles backslashes correctly" do
78+
@article.languages = ["\\","\""]
79+
@article.save
80+
@article.reload
81+
@article.languages_before_type_cast.should == '{"\\\\","\\""}'
82+
@article.languages.should == ["\\","\""]
83+
end
84+
end
85+
86+
end

spec/internal/app/models/article.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
class Article < ActiveRecord::Base
2+
end

spec/internal/config/database.yml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
test:
2+
adapter: postgresql
3+
encoding: unicode
4+
database: apa_test
5+
username: apa_test
6+
password:

spec/internal/db/schema.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
ActiveRecord::Schema.define do
2+
create_table(:articles, :force => true) do |t|
3+
t.string :name
4+
t.string_array :languages
5+
end
6+
end

0 commit comments

Comments
 (0)