activerecord-postgres-composite-types 0.2.4 → 0.2.5
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 +4 -4
- data/Gemfile +9 -9
- data/Rakefile +13 -13
- data/VERSION +1 -1
- data/activerecord-postgres-composite-types.gemspec +4 -4
- data/lib/activerecord-postgres-composite-types.rb +5 -5
- data/lib/activerecord-postgres-composite-types/active_record.rb +141 -148
- data/lib/activerecord-postgres-composite-types/active_record_3.rb +86 -86
- data/lib/activerecord-postgres-composite-types/active_record_4.rb +90 -90
- data/lib/activerecord-postgres-composite-types/composite_type_parser.rb +44 -93
- data/lib/activerecord-postgres-composite-types/railtie.rb +6 -6
- data/lib/postgres_composite_type.rb +89 -89
- data/test/composite_types.rb +5 -5
- data/test/helper.rb +4 -4
- data/test/internal/db/schema.rb +16 -16
- data/test/test_composite_type_class.rb +52 -36
- data/test/test_nested_types.rb +34 -16
- data/test/test_postgres_composite_types.rb +44 -44
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9d4f0647edbf6bedba08366faa187b0920815e9d
|
4
|
+
data.tar.gz: 1256ce07b6c0b404b85f872f316d87e344f327ab
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e1c84c2c8a241aebd3412baa37ac3113468d4df82d84a938be27c8863ff6cdb4645c5ca5e841adfde52fbd7db32125e0e919ff55cc2d3df3fa64e3c6d21cb1b3
|
7
|
+
data.tar.gz: 26709a6e15d0b28768228648754654f222074e3ba8a9728e0507ea0bacca0e86998a1d6e539ebe2714888629dc5147f2b90983c6568ef0455a7dcd9a113eca2d
|
data/Gemfile
CHANGED
@@ -6,13 +6,13 @@ gem 'activerecord', ">= #{AR_VERSION}"
|
|
6
6
|
gem 'pg', '>= 0.17.0'
|
7
7
|
|
8
8
|
group :development do
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
9
|
+
gem 'test-unit', '~> 2.1'
|
10
|
+
gem 'shoulda', '>= 0'
|
11
|
+
gem 'rdoc', '~> 3.12'
|
12
|
+
gem 'rake', '~> 10.3'
|
13
|
+
gem 'bundler', '~> 1.0'
|
14
|
+
gem 'jeweler', '~> 2.0.1' unless RUBY_PLATFORM =~ /mswin/
|
15
|
+
gem 'simplecov', '>= 0'
|
16
|
+
gem 'combustion', '~> 0.5.2'
|
17
|
+
gem 'tzinfo-data' if AR_VERSION > '3.2.0'
|
18
18
|
end
|
data/Rakefile
CHANGED
@@ -12,19 +12,19 @@ end
|
|
12
12
|
require 'rake'
|
13
13
|
|
14
14
|
unless RUBY_PLATFORM =~ /mswin/
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
15
|
+
require 'jeweler'
|
16
|
+
Jeweler::Tasks.new do |gem|
|
17
|
+
# gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
|
18
|
+
gem.name = "activerecord-postgres-composite-types"
|
19
|
+
gem.homepage = "http://github.com/puzzleflow/activerecord-postgres-composite-types"
|
20
|
+
gem.license = "MIT"
|
21
|
+
gem.summary = %Q{ActiveRecord composite types support}
|
22
|
+
gem.description = %Q{This gem adds support to the ActiveRecord (3.x and 4.x) for composite types.}
|
23
|
+
gem.email = "rafal.bigaj@puzzleflow.com"
|
24
|
+
gem.authors = ["Rafal Bigaj"]
|
25
|
+
# dependencies defined in Gemfile
|
26
|
+
end
|
27
|
+
Jeweler::RubygemsDotOrgTasks.new
|
28
28
|
end
|
29
29
|
|
30
30
|
require 'rake/testtask'
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.5
|
@@ -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
|
+
# stub: activerecord-postgres-composite-types 0.2.5 ruby lib
|
6
6
|
|
7
7
|
Gem::Specification.new do |s|
|
8
8
|
s.name = "activerecord-postgres-composite-types"
|
9
|
-
s.version = "0.2.
|
9
|
+
s.version = "0.2.5"
|
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-11
|
14
|
+
s.date = "2014-12-11"
|
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 = [
|
@@ -45,7 +45,7 @@ Gem::Specification.new do |s|
|
|
45
45
|
]
|
46
46
|
s.homepage = "http://github.com/puzzleflow/activerecord-postgres-composite-types"
|
47
47
|
s.licenses = ["MIT"]
|
48
|
-
s.rubygems_version = "2.4.
|
48
|
+
s.rubygems_version = "2.4.3"
|
49
49
|
s.summary = "ActiveRecord composite types support"
|
50
50
|
|
51
51
|
if s.respond_to? :specification_version then
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'active_record'
|
2
2
|
|
3
3
|
if defined? Rails
|
4
|
-
|
4
|
+
require "activerecord-postgres-composite-types/railtie"
|
5
5
|
else
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
6
|
+
ActiveSupport.on_load :active_record do
|
7
|
+
require "activerecord-postgres-composite-types/active_record"
|
8
|
+
require 'postgres_composite_type'
|
9
|
+
end
|
10
10
|
end
|
@@ -5,165 +5,158 @@ require 'activerecord-postgres-composite-types/composite_type_parser'
|
|
5
5
|
|
6
6
|
module ActiveRecord
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
8
|
+
module ConnectionAdapters
|
9
|
+
|
10
|
+
class PostgreSQLAdapter
|
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
|
+
class << self
|
24
|
+
def register_composite_type_class(klass)
|
25
|
+
self.composite_type_classes[klass.type] = klass
|
26
|
+
TableDefinition.register_composite_type klass.type
|
27
|
+
Table.register_composite_type klass.type
|
28
|
+
register_arel_visitor klass
|
29
|
+
register_oid_type klass
|
30
|
+
end
|
31
|
+
|
32
|
+
def register_arel_visitor(klass)
|
33
|
+
Arel::Visitors::Visitor.class_eval <<-RUBY
|
34
34
|
def visit_#{klass.name.gsub('::', '_')}(o, a=nil)
|
35
35
|
"'" + ActiveRecord::ConnectionAdapters::PostgreSQLColumn::composite_type_to_string(o, o.class.connection) + "'" + '::#{klass.type}'
|
36
36
|
end
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
37
|
+
RUBY
|
38
|
+
end
|
39
|
+
|
40
|
+
def register_oid_type(klass)
|
41
|
+
# only AR 4.X
|
42
|
+
end
|
43
|
+
|
44
|
+
# removes composite types definition (for testing)
|
45
|
+
def unregister_composite_types(*composite_types)
|
46
|
+
composite_types.each { |type| unregister_composite_type type }
|
47
|
+
end
|
48
|
+
|
49
|
+
# removes composite type definition (for testing)
|
50
|
+
def unregister_composite_type(type)
|
51
|
+
self.composite_type_classes.delete(type.to_sym)
|
52
|
+
TableDefinition.unregister_composite_type type
|
53
|
+
Table.unregister_composite_type type
|
54
|
+
end
|
55
|
+
|
56
|
+
def composite_type_classes
|
57
|
+
@composite_type_classes ||= {}
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class PostgreSQLColumn < Column
|
63
|
+
# Adds composite type for the column.
|
64
|
+
|
65
|
+
def composite_type_class
|
66
|
+
PostgreSQLAdapter.composite_type_classes[type]
|
67
|
+
end
|
68
|
+
|
69
|
+
def klass_with_composite_types
|
70
|
+
composite_type_klass = PostgreSQLAdapter.composite_type_classes[type]
|
71
|
+
composite_type_klass || klass_without_composite_types
|
72
|
+
end
|
73
|
+
|
74
|
+
alias_method_chain :klass, :composite_types
|
75
|
+
|
76
|
+
def self.string_to_composite_type(klass, string)
|
77
|
+
return string unless String === string
|
78
|
+
if string.present?
|
79
|
+
fields = CompositeTypeParser.parse_data(string).map.with_index { |val, i| type_cast_composite_type_field(klass, i, val) }
|
80
|
+
klass.new(fields)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.type_cast_composite_type_field(klass, i, value)
|
85
|
+
klass.initialize_column_definition
|
86
|
+
|
87
|
+
column = klass.columns[i]
|
88
|
+
raise "Invalid column index: #{i}" unless column
|
89
|
+
cv = column.type_cast(value)
|
90
|
+
if cv.is_a?(String)
|
91
|
+
# unquote
|
92
|
+
cv = cv.upcase == 'NULL' ? nil : cv.gsub(/\A"(.*)"\Z/m) { $1.gsub(/\\(.)/, '\1') }
|
93
|
+
end
|
94
|
+
cv
|
95
|
+
end
|
96
|
+
|
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
|
106
|
+
end
|
107
|
+
|
108
|
+
alias_method_chain :simplified_type, :composite_types
|
109
|
+
end
|
110
|
+
|
111
|
+
class << TableDefinition
|
112
|
+
# Adds composite type for migrations. So you can add columns to a table like:
|
113
|
+
# create_table :people do |t|
|
114
|
+
# ...
|
115
|
+
# t.composite_type :composite_value
|
116
|
+
# ...
|
117
|
+
# end
|
118
|
+
def register_composite_type(composite_type)
|
119
|
+
class_eval <<-RUBY
|
120
|
+
def #{composite_type}(*args)
|
121
|
+
options = args.extract_options!
|
122
|
+
column_names = args
|
123
|
+
column_names.each { |name| column(name, '#{composite_type}', options) }
|
124
|
+
end
|
125
|
+
RUBY
|
126
|
+
end
|
127
|
+
|
128
|
+
# Removes composite types from migrations (for testing)
|
129
|
+
def unregister_composite_type(composite_type)
|
130
|
+
remove_method composite_type
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
class << Table
|
135
|
+
|
136
|
+
# Adds composite type for migrations. So you can add columns to a table like:
|
137
|
+
# change_table :people do |t|
|
138
|
+
# ...
|
139
|
+
# t.composite_type :composite_value
|
140
|
+
# ...
|
141
|
+
# end
|
142
|
+
def register_composite_type(composite_type)
|
143
|
+
class_eval <<-RUBY
|
127
144
|
def #{composite_type}(*args)
|
128
145
|
options = args.extract_options!
|
129
146
|
column_names = args
|
130
147
|
column_names.each { |name| column(name, '#{composite_type}', options) }
|
131
148
|
end
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
# Removes composite types from migrations (for testing)
|
136
|
-
def unregister_composite_type(composite_type)
|
137
|
-
remove_method composite_type
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
class << Table
|
142
|
-
|
143
|
-
# Adds composite type for migrations. So you can add columns to a table like:
|
144
|
-
# change_table :people do |t|
|
145
|
-
# ...
|
146
|
-
# t.composite_type :composite_value
|
147
|
-
# ...
|
148
|
-
# end
|
149
|
-
def register_composite_type(composite_type)
|
150
|
-
class_eval <<-RUBY
|
151
|
-
def #{composite_type}(*args)
|
152
|
-
options = args.extract_options!
|
153
|
-
column_names = args
|
154
|
-
column_names.each { |name| column(name, '#{composite_type}', options) }
|
155
|
-
end
|
156
|
-
RUBY
|
157
|
-
end
|
149
|
+
RUBY
|
150
|
+
end
|
158
151
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
152
|
+
# Removes composite types from migrations (for testing)
|
153
|
+
def unregister_composite_type(composite_type)
|
154
|
+
remove_method composite_type
|
155
|
+
end
|
163
156
|
|
164
|
-
|
157
|
+
end
|
165
158
|
|
166
|
-
|
159
|
+
end
|
167
160
|
|
168
161
|
end
|
169
162
|
|
@@ -1,104 +1,104 @@
|
|
1
1
|
# ActiveRecord 3.X specific extensions.
|
2
2
|
module ActiveRecord
|
3
3
|
|
4
|
-
|
4
|
+
module ConnectionAdapters
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
6
|
+
class PostgreSQLAdapter
|
7
|
+
# Cast a +value+ to a type that the database understands.
|
8
|
+
def type_cast_with_composite_types(value, column)
|
9
|
+
case value
|
10
|
+
when PostgresCompositeType
|
11
|
+
PostgreSQLColumn.composite_type_to_string(value, self)
|
12
|
+
when Array, Hash
|
13
|
+
if klass = column.composite_type_class
|
14
|
+
value = klass.new(value)
|
15
|
+
PostgreSQLColumn.composite_type_to_string(value, self)
|
16
|
+
else
|
17
|
+
type_cast_without_composite_types(value, column)
|
18
|
+
end
|
19
|
+
else
|
20
|
+
type_cast_without_composite_types(value, column)
|
21
|
+
end
|
22
|
+
end
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
alias_method_chain :type_cast, :composite_types
|
25
|
+
end
|
26
26
|
|
27
|
-
|
28
|
-
|
27
|
+
class PostgreSQLColumn < Column
|
28
|
+
# Adds composite type for the column.
|
29
29
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
# Casts value (which is a String) to an appropriate instance.
|
31
|
+
def type_cast_with_composite_types(value)
|
32
|
+
if composite_type_klass = PostgreSQLAdapter.composite_type_classes[type]
|
33
|
+
self.class.string_to_composite_type(composite_type_klass, value)
|
34
|
+
else
|
35
|
+
type_cast_without_composite_types(value)
|
36
|
+
end
|
37
|
+
end
|
38
38
|
|
39
|
-
|
39
|
+
alias_method_chain :type_cast, :composite_types
|
40
40
|
|
41
|
-
|
41
|
+
# quote_and_escape - Rails 4 code
|
42
42
|
|
43
|
-
|
43
|
+
ARRAY_ESCAPE = "\\" * 2 * 2 # escape the backslash twice for PG arrays
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
45
|
+
def self.quote_and_escape(value)
|
46
|
+
case value
|
47
|
+
when "NULL", Numeric
|
48
|
+
value
|
49
|
+
else
|
50
|
+
value = value.gsub(/\\/, ARRAY_ESCAPE)
|
51
|
+
value.gsub!(/"/, "\\\"")
|
52
|
+
"\"#{value}\""
|
53
|
+
end
|
54
|
+
end
|
55
55
|
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
56
|
+
def self.composite_type_to_string(object, adapter)
|
57
|
+
quoted_values = object.class.columns.collect do |column|
|
58
|
+
value = object.send(column.name)
|
59
|
+
if String === value
|
60
|
+
if value == "NULL"
|
61
|
+
"\"#{value}\""
|
62
|
+
else
|
63
|
+
quote_and_escape(adapter.type_cast(value, column))
|
64
|
+
end
|
65
|
+
else
|
66
|
+
adapter.type_cast(value, column)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
"(#{quoted_values.join(',')})"
|
70
|
+
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
72
|
+
def type_cast_code_with_composite_types(var_name)
|
73
|
+
if composite_type_klass = PostgreSQLAdapter.composite_type_classes[type]
|
74
|
+
"#{self.class}.string_to_composite_type(#{composite_type_klass}, #{var_name})"
|
75
|
+
else
|
76
|
+
type_cast_code_without_composite_types(var_name)
|
77
|
+
end
|
78
|
+
end
|
79
79
|
|
80
|
-
|
81
|
-
|
82
|
-
|
80
|
+
alias_method_chain :type_cast_code, :composite_types
|
81
|
+
end
|
82
|
+
end
|
83
83
|
|
84
|
-
|
85
|
-
|
86
|
-
|
84
|
+
module AttributeMethods
|
85
|
+
module CompositeTypes
|
86
|
+
extend ActiveSupport::Concern
|
87
87
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
88
|
+
def type_cast_attribute_for_write(column, value)
|
89
|
+
if column && klass = column.composite_type_class
|
90
|
+
# Cast Hash and Array to composite type klass
|
91
|
+
if value.is_a?(klass)
|
92
|
+
value
|
93
|
+
else
|
94
|
+
klass.new(value)
|
95
|
+
end
|
96
|
+
else
|
97
|
+
super(column, value)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
102
|
|
103
|
-
|
103
|
+
Base.send :include, AttributeMethods::CompositeTypes
|
104
104
|
end
|