activerecord-postgres-composite-types 0.2.5 → 0.2.6

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
  SHA1:
3
- metadata.gz: 9d4f0647edbf6bedba08366faa187b0920815e9d
4
- data.tar.gz: 1256ce07b6c0b404b85f872f316d87e344f327ab
3
+ metadata.gz: 147af26bcacdef7cd83aa2c4bb67dad6b0b6747f
4
+ data.tar.gz: 4f8e376d697804a4be2e04d3931047b1922b92ac
5
5
  SHA512:
6
- metadata.gz: e1c84c2c8a241aebd3412baa37ac3113468d4df82d84a938be27c8863ff6cdb4645c5ca5e841adfde52fbd7db32125e0e919ff55cc2d3df3fa64e3c6d21cb1b3
7
- data.tar.gz: 26709a6e15d0b28768228648754654f222074e3ba8a9728e0507ea0bacca0e86998a1d6e539ebe2714888629dc5147f2b90983c6568ef0455a7dcd9a113eca2d
6
+ metadata.gz: 7a503873a92e2c34cd620e52b1526a006910ecaa1a9b3ade3ab0498ecc771118eb3d9353cf2dd7df965895f41a6a8016388506194474e29a972cb762f11c196c
7
+ data.tar.gz: 47e52eda590974f4b530428313145ca0d8d210059e2a1bbd98d23e4bd817ae27e54dccf5aa7e2da592175751eec9834a154836caadee839cdae06eb94914f81c
@@ -1,6 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.1.0
4
+ env:
5
+ - AR_VERSION=3.2.0
6
+ - AR_VERSION=4.1.9
7
+ - AR_VERSION=4.2.0
4
8
  addons:
5
9
  postgresql: "9.3"
6
10
  before_script:
data/Gemfile CHANGED
@@ -1,8 +1,8 @@
1
1
  source 'http://rubygems.org'
2
2
 
3
- AR_VERSION = '3.2.0'
3
+ AR_VERSION = ENV['AR_VERSION'] || '3.2.0'
4
4
 
5
- gem 'activerecord', ">= #{AR_VERSION}"
5
+ gem 'activerecord', AR_VERSION
6
6
  gem 'pg', '>= 0.17.0'
7
7
 
8
8
  group :development do
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.5
1
+ 0.2.6
@@ -2,16 +2,16 @@
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
3
  # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
- # stub: activerecord-postgres-composite-types 0.2.5 ruby lib
5
+ # stub: activerecord-postgres-composite-types 0.2.6 ruby lib
6
6
 
7
7
  Gem::Specification.new do |s|
8
8
  s.name = "activerecord-postgres-composite-types"
9
- s.version = "0.2.5"
9
+ s.version = "0.2.6"
10
10
 
11
11
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
12
12
  s.require_paths = ["lib"]
13
13
  s.authors = ["Rafal Bigaj"]
14
- s.date = "2014-12-11"
14
+ s.date = "2015-03-20"
15
15
  s.description = "This gem adds support to the ActiveRecord (3.x and 4.x) for composite types."
16
16
  s.email = "rafal.bigaj@puzzleflow.com"
17
17
  s.extra_rdoc_files = [
@@ -29,9 +29,12 @@ Gem::Specification.new do |s|
29
29
  "activerecord-postgres-composite-types.gemspec",
30
30
  "lib/activerecord-postgres-composite-types.rb",
31
31
  "lib/activerecord-postgres-composite-types/active_record.rb",
32
- "lib/activerecord-postgres-composite-types/active_record_3.rb",
33
- "lib/activerecord-postgres-composite-types/active_record_4.rb",
32
+ "lib/activerecord-postgres-composite-types/active_record_3_2.rb",
33
+ "lib/activerecord-postgres-composite-types/active_record_4_1.rb",
34
+ "lib/activerecord-postgres-composite-types/active_record_4_2.rb",
34
35
  "lib/activerecord-postgres-composite-types/composite_type_parser.rb",
36
+ "lib/activerecord-postgres-composite-types/oid/composite_type_4_1.rb",
37
+ "lib/activerecord-postgres-composite-types/oid/composite_type_4_2.rb",
35
38
  "lib/activerecord-postgres-composite-types/railtie.rb",
36
39
  "lib/postgres_composite_type.rb",
37
40
  "test/composite_types.rb",
@@ -45,14 +48,14 @@ Gem::Specification.new do |s|
45
48
  ]
46
49
  s.homepage = "http://github.com/puzzleflow/activerecord-postgres-composite-types"
47
50
  s.licenses = ["MIT"]
48
- s.rubygems_version = "2.4.3"
51
+ s.rubygems_version = "2.4.5"
49
52
  s.summary = "ActiveRecord composite types support"
50
53
 
51
54
  if s.respond_to? :specification_version then
52
55
  s.specification_version = 4
53
56
 
54
57
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
55
- s.add_runtime_dependency(%q<activerecord>, [">= 3.2.0"])
58
+ s.add_runtime_dependency(%q<activerecord>, ["= 4.1.9"])
56
59
  s.add_runtime_dependency(%q<pg>, [">= 0.17.0"])
57
60
  s.add_development_dependency(%q<test-unit>, ["~> 2.1"])
58
61
  s.add_development_dependency(%q<shoulda>, [">= 0"])
@@ -62,8 +65,9 @@ Gem::Specification.new do |s|
62
65
  s.add_development_dependency(%q<jeweler>, ["~> 2.0.1"])
63
66
  s.add_development_dependency(%q<simplecov>, [">= 0"])
64
67
  s.add_development_dependency(%q<combustion>, ["~> 0.5.2"])
68
+ s.add_development_dependency(%q<tzinfo-data>, [">= 0"])
65
69
  else
66
- s.add_dependency(%q<activerecord>, [">= 3.2.0"])
70
+ s.add_dependency(%q<activerecord>, ["= 4.1.9"])
67
71
  s.add_dependency(%q<pg>, [">= 0.17.0"])
68
72
  s.add_dependency(%q<test-unit>, ["~> 2.1"])
69
73
  s.add_dependency(%q<shoulda>, [">= 0"])
@@ -73,9 +77,10 @@ Gem::Specification.new do |s|
73
77
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
74
78
  s.add_dependency(%q<simplecov>, [">= 0"])
75
79
  s.add_dependency(%q<combustion>, ["~> 0.5.2"])
80
+ s.add_dependency(%q<tzinfo-data>, [">= 0"])
76
81
  end
77
82
  else
78
- s.add_dependency(%q<activerecord>, [">= 3.2.0"])
83
+ s.add_dependency(%q<activerecord>, ["= 4.1.9"])
79
84
  s.add_dependency(%q<pg>, [">= 0.17.0"])
80
85
  s.add_dependency(%q<test-unit>, ["~> 2.1"])
81
86
  s.add_dependency(%q<shoulda>, [">= 0"])
@@ -85,6 +90,7 @@ Gem::Specification.new do |s|
85
90
  s.add_dependency(%q<jeweler>, ["~> 2.0.1"])
86
91
  s.add_dependency(%q<simplecov>, [">= 0"])
87
92
  s.add_dependency(%q<combustion>, ["~> 0.5.2"])
93
+ s.add_dependency(%q<tzinfo-data>, [">= 0"])
88
94
  end
89
95
  end
90
96
 
@@ -9,38 +9,14 @@ module ActiveRecord
9
9
 
10
10
  class PostgreSQLAdapter
11
11
 
12
- # Quotes the column value to help prevent {SQL injection attacks}
13
- def quote_with_composite_types(value, column = nil)
14
- if value.class < PostgresCompositeType
15
- "'#{PostgreSQLColumn.composite_type_to_string(value, self).gsub(/'/, "''")}'"
16
- else
17
- quote_without_composite_types(value, column)
18
- end
19
- end
20
-
21
- alias_method_chain :quote, :composite_types
22
-
23
12
  class << self
24
13
  def register_composite_type_class(klass)
25
14
  self.composite_type_classes[klass.type] = klass
26
15
  TableDefinition.register_composite_type klass.type
27
16
  Table.register_composite_type klass.type
28
- register_arel_visitor klass
29
17
  register_oid_type klass
30
18
  end
31
19
 
32
- def register_arel_visitor(klass)
33
- Arel::Visitors::Visitor.class_eval <<-RUBY
34
- def visit_#{klass.name.gsub('::', '_')}(o, a=nil)
35
- "'" + ActiveRecord::ConnectionAdapters::PostgreSQLColumn::composite_type_to_string(o, o.class.connection) + "'" + '::#{klass.type}'
36
- end
37
- RUBY
38
- end
39
-
40
- def register_oid_type(klass)
41
- # only AR 4.X
42
- end
43
-
44
20
  # removes composite types definition (for testing)
45
21
  def unregister_composite_types(*composite_types)
46
22
  composite_types.each { |type| unregister_composite_type type }
@@ -86,7 +62,8 @@ module ActiveRecord
86
62
 
87
63
  column = klass.columns[i]
88
64
  raise "Invalid column index: #{i}" unless column
89
- cv = column.type_cast(value)
65
+
66
+ cv = column.type_cast_from_database(value)
90
67
  if cv.is_a?(String)
91
68
  # unquote
92
69
  cv = cv.upcase == 'NULL' ? nil : cv.gsub(/\A"(.*)"\Z/m) { $1.gsub(/\\(.)/, '\1') }
@@ -94,18 +71,24 @@ module ActiveRecord
94
71
  cv
95
72
  end
96
73
 
97
- private
98
-
99
- def simplified_type_with_composite_types(field_type)
100
- type = field_type.to_sym
101
- if PostgreSQLAdapter.composite_type_classes.has_key?(type)
102
- type
103
- else
104
- simplified_type_without_composite_types(field_type)
105
- end
74
+ unless method_defined?(:type_cast_from_database) # AR ver < 4.2
75
+ alias_method :type_cast_from_database, :type_cast
106
76
  end
107
77
 
108
- alias_method_chain :simplified_type, :composite_types
78
+ private
79
+
80
+ if private_method_defined?(:simplified_type) # up to v4.1
81
+ def simplified_type_with_composite_types(field_type)
82
+ type = field_type.to_sym
83
+ if PostgreSQLAdapter.composite_type_classes.has_key?(type)
84
+ type
85
+ else
86
+ simplified_type_without_composite_types(field_type)
87
+ end
88
+ end
89
+
90
+ alias_method_chain :simplified_type, :composite_types
91
+ end
109
92
  end
110
93
 
111
94
  class << TableDefinition
@@ -160,4 +143,8 @@ module ActiveRecord
160
143
 
161
144
  end
162
145
 
163
- require_relative "active_record_#{ActiveRecord::VERSION::MAJOR}"
146
+ begin
147
+ require_relative "active_record_#{ActiveRecord::VERSION::STRING[0..2].sub('.', '_')}"
148
+ rescue LoadError
149
+ raise "Unsupported ActiveRecord version: #{ActiveRecord::VERSION::STRING}"
150
+ end
@@ -4,24 +4,43 @@ module ActiveRecord
4
4
  module ConnectionAdapters
5
5
 
6
6
  class PostgreSQLAdapter
7
- # Cast a +value+ to a type that the database understands.
7
+ # Quotes the column value to help prevent {SQL injection attacks}
8
+ def quote_with_composite_types(value, column = nil)
9
+ if value.class < PostgresCompositeType
10
+ "'#{PostgreSQLColumn.composite_type_to_string(value, self).gsub(/'/, "''")}'"
11
+ else
12
+ quote_without_composite_types(value, column)
13
+ end
14
+ end
15
+ alias_method_chain :quote, :composite_types
16
+
17
+ # Cast a +value+ to a type that the database understands.
8
18
  def type_cast_with_composite_types(value, column)
9
19
  case value
10
20
  when PostgresCompositeType
11
21
  PostgreSQLColumn.composite_type_to_string(value, self)
12
22
  when Array, Hash
13
- if klass = column.composite_type_class
23
+ if (klass = column.composite_type_class)
14
24
  value = klass.new(value)
15
25
  PostgreSQLColumn.composite_type_to_string(value, self)
16
26
  else
17
27
  type_cast_without_composite_types(value, column)
18
28
  end
19
- else
29
+ else
20
30
  type_cast_without_composite_types(value, column)
21
31
  end
22
32
  end
23
-
24
33
  alias_method_chain :type_cast, :composite_types
34
+
35
+ class << self
36
+ def register_oid_type(klass)
37
+ Arel::Visitors::Visitor.module_eval <<-RUBY, __FILE__, __LINE__
38
+ def visit_#{klass.name.gsub('::', '_')}(o, a=nil)
39
+ o.quoted_value
40
+ end
41
+ RUBY
42
+ end
43
+ end
25
44
  end
26
45
 
27
46
  class PostgreSQLColumn < Column
@@ -29,13 +48,12 @@ module ActiveRecord
29
48
 
30
49
  # Casts value (which is a String) to an appropriate instance.
31
50
  def type_cast_with_composite_types(value)
32
- if composite_type_klass = PostgreSQLAdapter.composite_type_classes[type]
51
+ if (composite_type_klass = PostgreSQLAdapter.composite_type_classes[type])
33
52
  self.class.string_to_composite_type(composite_type_klass, value)
34
53
  else
35
54
  type_cast_without_composite_types(value)
36
55
  end
37
56
  end
38
-
39
57
  alias_method_chain :type_cast, :composite_types
40
58
 
41
59
  # quote_and_escape - Rails 4 code
@@ -63,7 +81,12 @@ module ActiveRecord
63
81
  quote_and_escape(adapter.type_cast(value, column))
64
82
  end
65
83
  else
66
- adapter.type_cast(value, column)
84
+ res = adapter.type_cast(value, column)
85
+ if value.class < PostgresCompositeType
86
+ quote_and_escape(res)
87
+ else
88
+ res
89
+ end
67
90
  end
68
91
  end
69
92
  "(#{quoted_values.join(',')})"
@@ -86,7 +109,7 @@ module ActiveRecord
86
109
  extend ActiveSupport::Concern
87
110
 
88
111
  def type_cast_attribute_for_write(column, value)
89
- if column && klass = column.composite_type_class
112
+ if column && !value.nil? && (klass = column.composite_type_class)
90
113
  # Cast Hash and Array to composite type klass
91
114
  if value.is_a?(klass)
92
115
  value
@@ -1,33 +1,12 @@
1
- # ActiveRecord 3.X specific extensions.
1
+ # ActiveRecord 4.0, 4.1 specific extensions.
2
+
3
+ require_relative 'oid/composite_type_4_1'
4
+
2
5
  module ActiveRecord
3
6
 
4
7
  module ConnectionAdapters
5
8
 
6
9
  class PostgreSQLAdapter
7
- module OID
8
- class CompositeType < Type
9
- def initialize(composite_type_class)
10
- @composite_type_class = composite_type_class
11
- end
12
-
13
- # Casts value (which is a String) to an appropriate instance.
14
- def type_cast(value)
15
- PostgreSQLColumn.string_to_composite_type(@composite_type_class, value)
16
- # @composite_type_class.new(value)
17
- end
18
-
19
- # Casts a Ruby value to something appropriate for writing to the database.
20
- def type_cast_for_write(value)
21
- # Cast Hash and Array to composite type klass
22
- if value.is_a?(@composite_type_class)
23
- value
24
- else
25
- @composite_type_class.new(value)
26
- end
27
- end
28
- end
29
- end
30
-
31
10
  class << self
32
11
  def register_oid_type(klass)
33
12
  OID.register_type klass.type.to_s, OID::CompositeType.new(klass)
@@ -36,18 +15,15 @@ module ActiveRecord
36
15
  end
37
16
  end
38
17
 
39
- def add_composite_type_to_map(type)
40
- oid_type = OID::NAMES[type.to_s]
41
- raise "OID type: '#{type}' not registered" unless oid_type
42
-
43
- result = execute("SELECT oid, typname, typelem, typdelim, typinput FROM pg_type WHERE typname = '#{type}'", 'SCHEMA')
44
- raise "Composite type: '#{type}' not defined in PostgreSQL database" if result.empty?
45
- row = result[0]
46
-
47
- unless type_map.key? row['typelem'].to_i
48
- type_map[row['oid'].to_i] = vector
49
- end
18
+ # Quotes the column value to help prevent {SQL injection attacks}
19
+ def quote_with_composite_types(value, column = nil)
20
+ if value.class < PostgresCompositeType
21
+ "'#{PostgreSQLColumn.composite_type_to_string(value, self).gsub(/'/, "''")}'"
22
+ else
23
+ quote_without_composite_types(value, column)
24
+ end
50
25
  end
26
+ alias_method_chain :quote, :composite_types
51
27
 
52
28
  # Cast a +value+ to a type that the database understands.
53
29
  def type_cast_with_composite_types(value, column, array_member = false)
@@ -0,0 +1,38 @@
1
+ # ActiveRecord 4.2 specific extensions.
2
+
3
+ require_relative 'oid/composite_type_4_2'
4
+
5
+ module ActiveRecord
6
+
7
+ module ConnectionAdapters
8
+
9
+ class PostgreSQLAdapter
10
+
11
+ class << self
12
+ mattr_accessor :ordered_composite_type_classes
13
+ self.ordered_composite_type_classes = []
14
+
15
+ def register_oid_type(klass)
16
+ self.ordered_composite_type_classes << klass
17
+ # Dirty. Only this type should be added to type map
18
+ klass.connection.send(:reload_type_map) if klass.connected?
19
+ end
20
+ end
21
+
22
+ # Rails 4.2
23
+ def initialize_type_map_with_composite_types(m)
24
+ initialize_type_map_without_composite_types(m)
25
+ self.class.ordered_composite_type_classes.each do |klass|
26
+ m.register_type klass.type.to_s, OID::CompositeType.new(klass)
27
+ end
28
+ end
29
+ alias_method_chain :initialize_type_map, :composite_types
30
+ end
31
+
32
+ class PostgreSQLColumn < Column
33
+ def self.composite_type_to_string(object, adapter)
34
+ PostgreSQL::OID::CompositeType.new(object.class).type_cast_for_database(object)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,29 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ class PostgreSQLAdapter
4
+ module OID
5
+ class CompositeType < Type
6
+ def initialize(composite_type_class)
7
+ @composite_type_class = composite_type_class
8
+ end
9
+
10
+ # Casts value (which is a String) to an appropriate instance.
11
+ def type_cast(value)
12
+ PostgreSQLColumn.string_to_composite_type(@composite_type_class, value)
13
+ # @composite_type_class.new(value)
14
+ end
15
+
16
+ # Casts a Ruby value to something appropriate for writing to the database.
17
+ def type_cast_for_write(value)
18
+ # Cast Hash and Array to composite type klass
19
+ if value.is_a?(@composite_type_class) || value.nil?
20
+ value
21
+ else
22
+ @composite_type_class.new(value)
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,60 @@
1
+ module ActiveRecord
2
+ module ConnectionAdapters
3
+ module PostgreSQL
4
+ module OID
5
+ class CompositeType < OID::Array
6
+
7
+ def initialize(composite_type_class)
8
+ @composite_type_class = composite_type_class
9
+ @delimiter = ','
10
+ end
11
+
12
+ def type
13
+ @composite_type_class.type
14
+ end
15
+
16
+ def type_cast_from_database(value)
17
+ PostgreSQLColumn.string_to_composite_type(@composite_type_class, value)
18
+ end
19
+
20
+ def type_cast_from_user(value)
21
+ if value.is_a?(@composite_type_class) || value.nil?
22
+ value
23
+ else
24
+ @composite_type_class.new(value)
25
+ end
26
+ end
27
+
28
+
29
+ def type_cast_for_database(object)
30
+ return object if object.is_a?(String) # already quoted by AREL visitor
31
+ return "NULL" if object == nil
32
+ quoted_values = object.class.columns.collect do |column|
33
+ value = object.send(column.name)
34
+ if String === value
35
+ quote_and_escape(column.type_cast_for_database(value))
36
+ else
37
+ res = column.type_cast_for_database(value)
38
+ if value.class < PostgresCompositeType
39
+ quote_and_escape(res)
40
+ else
41
+ res
42
+ end
43
+ end
44
+ end
45
+ "(#{quoted_values.join(',')})"
46
+ end
47
+
48
+ # Overwrite OID::Array method - regular brackets () instead of {} have to be escaped
49
+ def string_requires_quoting?(string)
50
+ string.empty? ||
51
+ string == "NULL" ||
52
+ string =~ /[\(\)"\\\s]/ ||
53
+ string.include?(delimiter)
54
+ end
55
+
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -50,14 +50,18 @@ class PostgresCompositeType
50
50
  def initialize_column_definition
51
51
  unless @columns
52
52
  @columns = self.connection.columns(type)
53
- attr_accessor *@columns.map(&:name)
53
+ attr_reader *@columns.map(&:name)
54
+ @columns.each do |column|
55
+ define_method("#{column.name}=") do |value|
56
+ instance_variable_set "@#{column.name}", type_cast_field_from_user(column, value)
57
+ end
58
+ end
54
59
  end
55
60
  end
56
61
  end
57
62
 
58
63
  def initialize(value)
59
64
  self.class.initialize_column_definition
60
-
61
65
  case value
62
66
  when String
63
67
  ActiveRecord::ConnectionAdapters::PostgreSQLColumn.string_to_composite_type(self.class, value)
@@ -80,14 +84,45 @@ class PostgresCompositeType
80
84
  0
81
85
  end
82
86
 
87
+ # Used for AREL eq node
88
+ def quoted_value
89
+ "'" + ActiveRecord::ConnectionAdapters::PostgreSQLColumn::composite_type_to_string(self, self.class.connection) + "'::#{self.class.type}"
90
+ end
91
+
83
92
  private
84
93
 
94
+ if ActiveRecord::ConnectionAdapters::PostgreSQLColumn.method_defined?(:type_cast_from_user) # v4.2 and later
95
+ def type_cast_field_from_user(column, value)
96
+ column.type_cast_from_user(value)
97
+ end
98
+ elsif ActiveRecord::ConnectionAdapters::PostgreSQLColumn.method_defined?(:type_cast_for_write) # v4.0 and v4.1
99
+ def type_cast_field_from_user(column, value)
100
+ column.type_cast_for_write(value)
101
+ end
102
+ else # v3.X
103
+ def type_cast_field_from_user(column, value)
104
+ klass = column.composite_type_class
105
+ if klass
106
+ case value
107
+ when Hash, Array
108
+ klass.new(value)
109
+ when String
110
+ ActiveRecord::ConnectionAdapters::PostgreSQLColumn.string_to_composite_type(klass, value)
111
+ else
112
+ value
113
+ end
114
+ else
115
+ value
116
+ end
117
+ end
118
+ end
119
+
85
120
  def set_attributes(values)
86
121
  values.each do |name, value|
87
- if Hash === value || Array === value
88
- klass = self.class.columns.find(name).first.try(:composite_type_class)
89
- value = klass.new(value) if klass
90
- end
122
+ #if Hash === value || Array === value
123
+ # klass = self.class.columns.find(name).first.try(:composite_type_class)
124
+ # value = klass.new(value) if klass
125
+ #end
91
126
  send "#{name}=", value
92
127
  end
93
128
  end
@@ -95,12 +130,16 @@ class PostgresCompositeType
95
130
  def set_values(values)
96
131
  raise "Invalid values count: #{values.size}, expected: #{self.class.columns.size}" if values.size != self.class.columns.size
97
132
  self.class.columns.each.with_index do |column, i|
98
- if Hash === values[i] || Array === values[i]
99
- klass = column.composite_type_class
100
- values[i] = klass.new(values[i]) if klass
101
- end
133
+ #if Hash === values[i] || Array === values[i]
134
+ # klass = column.composite_type_class
135
+ # values[i] = klass.new(values[i]) if klass
136
+ #end
102
137
  send "#{column.name}=", values[i]
103
138
  end
104
139
  end
105
140
 
141
+ ActiveRecord::PredicateBuilder.register_handler(PostgresCompositeType, ->(attribute, value) {
142
+ quoted = Arel::Nodes::SqlLiteral.new(value.quoted_value)
143
+ Arel::Nodes::Equality.new(attribute, quoted)
144
+ }) if ActiveRecord::PredicateBuilder.respond_to?(:register_handler) # v.4.X
106
145
  end
@@ -1,6 +1,6 @@
1
1
  require 'postgres_composite_type'
2
2
 
3
- if ActiveRecord::VERSION::MAJOR > 3
3
+ if ActiveRecord::VERSION::MAJOR > 3 && ActiveRecord::VERSION::MINOR < 2 # v4.0 & v4.1
4
4
  ActiveRecord::ConnectionAdapters::PostgreSQLAdapter::OID.alias_type 'rgb_color', 'text'
5
5
  end
6
6
 
@@ -24,6 +24,8 @@ rescue Bundler::BundlerError => e
24
24
  exit e.status_code
25
25
  end
26
26
 
27
+ require 'shoulda'
28
+
27
29
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
28
30
  $LOAD_PATH.unshift(File.dirname(__FILE__))
29
31
 
@@ -36,6 +38,8 @@ end
36
38
  Combustion.path = 'test/internal'
37
39
  Combustion.initialize! :active_record
38
40
 
41
+ ActiveRecord::Base.default_timezone = :utc
42
+
39
43
  class Test::Unit::TestCase
40
44
  def connection
41
45
  ActiveRecord::Base.connection
@@ -7,6 +7,10 @@ ActiveRecord::Schema.define do
7
7
  execute "CREATE TYPE nested_type AS (comp compfoo, color rgb_color)"
8
8
  execute "CREATE TYPE nested_nested_type AS (nested nested_type, color rgb_color)"
9
9
 
10
+ create_table :my_table, :id => false do |t|
11
+ t.column :value, :my_type
12
+ end
13
+
10
14
  create_table :foos, :id => false do |t|
11
15
  t.column :comp, :compfoo, default: "(0,\"\")"
12
16
  end
@@ -22,3 +26,5 @@ ActiveRecord::Schema.define do
22
26
  execute "INSERT INTO foos VALUES ((0,'abc')), ((1,'a/b''c\\d e f'))"
23
27
  execute "INSERT INTO bars VALUES (((0,'abc'),'red')), (((1,'cba'),'blue'))"
24
28
  end
29
+
30
+ ActiveRecord::Base.connection.send(:reload_type_map) rescue nil # only v4.X
@@ -4,6 +4,10 @@ class TestCompositeTypeClass < Test::Unit::TestCase
4
4
 
5
5
  PostgreSQLColumn = ActiveRecord::ConnectionAdapters::PostgreSQLColumn
6
6
 
7
+ setup do
8
+ @my_type_column = connection.columns(:my_table).first
9
+ end
10
+
7
11
  should "define accessors" do
8
12
  assert MyType.method_defined?(:name)
9
13
  assert MyType.method_defined?(:name=)
@@ -36,7 +40,8 @@ class TestCompositeTypeClass < Test::Unit::TestCase
36
40
 
37
41
  should "cast to qouted string" do
38
42
  value = MyType.new(number: 1, name: '"\'a\'bc[]*/\"', date: Time.parse('2014-08-27 12:00:00 UTC'))
39
- assert_equal %Q{'("\\\"''a''bc[]*/\\\\\\\"",1,2014-08-27 12:00:00.000000)'}, connection.quote(value)
43
+ quoted = connection.quote(value, @my_type_column).sub(':00.000000', ':00 UTC') # On AR ver < 4.2 time is quoted to format with milliseconds
44
+ assert_equal %Q{'("\\\"''a''bc[]*/\\\\\\\"",1,2014-08-27 12:00:00 UTC)'}, quoted
40
45
  end
41
46
 
42
47
  should "parse string and return array" do
@@ -46,19 +46,19 @@ class TestNestedTypes < Test::Unit::TestCase
46
46
  end
47
47
 
48
48
  should "parser should work when nested attribute contains parenthesis" do
49
- bar = Bar2.create!(nested: {nested: {comp: [1, 'dc)))a'], color: 'blue'}, color: 'red'})
50
- Bar2.all.to_a.last.nested
51
- assert true
49
+ Bar2.create!(nested: {nested: {comp: [1, 'dc)))a'], color: 'blue'}, color: 'red'})
50
+ assert_equal 'dc)))a', Bar2.all.to_a.last.nested.nested.comp.f2
51
+ assert Bar2.where(nested: NestedNestedType.new(nested: {comp: [1, 'dc)))a'], color: 'blue'}, color: 'red')).exists?
52
52
  end
53
53
 
54
54
  should "parser should work when nested attribute contains double quote" do
55
- bar = Bar2.create!(nested: {nested: {comp: [1, "dc\"a"], color: 'blue'}, color: 'blue'})
55
+ Bar2.create!(nested: {nested: {comp: [1, "dc\"a"], color: 'blue'}, color: 'blue'})
56
56
  assert_equal 'blue', Bar2.all.to_a.last.nested.color
57
57
  assert_equal 'dc"a', Bar2.all.to_a.last.nested.nested.comp.f2
58
58
  end
59
59
 
60
60
  should "parser should work when nested attribute contains backslash" do
61
- bar = Bar2.create!(nested: {nested: {comp: [1, "dc\\a"], color: 'blue'}, color: 'green'})
61
+ Bar2.create!(nested: {nested: {comp: [1, "dc\\a"], color: 'blue'}, color: 'green'})
62
62
  assert_equal 'green', Bar2.all.to_a.last.nested.color
63
63
  assert_equal 'dc\\a', Bar2.all.to_a.last.nested.nested.comp.f2
64
64
  end
@@ -45,4 +45,11 @@ class TestPostgresCompositeTypes < Test::Unit::TestCase
45
45
  assert_equal 'text 3', foo.comp.f2
46
46
  assert Foo.where(comp: Compfoo.new({f1: 111, f2: 'text 3'})).exists?
47
47
  end
48
+
49
+ should 'make object nil' do
50
+ foo = Foo.create!(comp: [333, 'ala ma kota'])
51
+ foo.comp = nil
52
+
53
+ assert_nil foo.comp
54
+ end
48
55
  end
metadata CHANGED
@@ -1,29 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: activerecord-postgres-composite-types
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.5
4
+ version: 0.2.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rafal Bigaj
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-12-11 00:00:00.000000000 Z
11
+ date: 2015-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ">="
17
+ - - '='
18
18
  - !ruby/object:Gem::Version
19
- version: 3.2.0
19
+ version: 4.1.9
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ">="
24
+ - - '='
25
25
  - !ruby/object:Gem::Version
26
- version: 3.2.0
26
+ version: 4.1.9
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: pg
29
29
  requirement: !ruby/object:Gem::Requirement
@@ -150,6 +150,20 @@ dependencies:
150
150
  - - "~>"
151
151
  - !ruby/object:Gem::Version
152
152
  version: 0.5.2
153
+ - !ruby/object:Gem::Dependency
154
+ name: tzinfo-data
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
153
167
  description: This gem adds support to the ActiveRecord (3.x and 4.x) for composite
154
168
  types.
155
169
  email: rafal.bigaj@puzzleflow.com
@@ -169,9 +183,12 @@ files:
169
183
  - activerecord-postgres-composite-types.gemspec
170
184
  - lib/activerecord-postgres-composite-types.rb
171
185
  - lib/activerecord-postgres-composite-types/active_record.rb
172
- - lib/activerecord-postgres-composite-types/active_record_3.rb
173
- - lib/activerecord-postgres-composite-types/active_record_4.rb
186
+ - lib/activerecord-postgres-composite-types/active_record_3_2.rb
187
+ - lib/activerecord-postgres-composite-types/active_record_4_1.rb
188
+ - lib/activerecord-postgres-composite-types/active_record_4_2.rb
174
189
  - lib/activerecord-postgres-composite-types/composite_type_parser.rb
190
+ - lib/activerecord-postgres-composite-types/oid/composite_type_4_1.rb
191
+ - lib/activerecord-postgres-composite-types/oid/composite_type_4_2.rb
175
192
  - lib/activerecord-postgres-composite-types/railtie.rb
176
193
  - lib/postgres_composite_type.rb
177
194
  - test/composite_types.rb
@@ -202,7 +219,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
202
219
  version: '0'
203
220
  requirements: []
204
221
  rubyforge_project:
205
- rubygems_version: 2.4.3
222
+ rubygems_version: 2.4.5
206
223
  signing_key:
207
224
  specification_version: 4
208
225
  summary: ActiveRecord composite types support