activerecord-postgres-hstore 0.0.2 → 0.0.3
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.
- data/README.textile +4 -0
- data/VERSION +1 -1
- data/lib/activerecord-postgres-hstore.rb +19 -13
- data/lib/activerecord-postgres-hstore/activerecord.rb +71 -33
- data/lib/activerecord-postgres-hstore/hash.rb +3 -0
- data/lib/activerecord-postgres-hstore/string.rb +8 -0
- metadata +4 -5
- data/lib/activerecord-postgres-hstore/hstore.rb +0 -2
data/README.textile
CHANGED
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.0.
|
1
|
+
0.0.3
|
@@ -1,17 +1,29 @@
|
|
1
|
-
#raise ActiveRecord::ConnectionAdapters::Column.inspect
|
2
1
|
require 'rails'
|
3
2
|
require 'rails/generators'
|
4
3
|
require 'rails/generators/migration'
|
5
|
-
|
4
|
+
|
5
|
+
# = Hstore Railtie
|
6
|
+
#
|
7
|
+
# Creates a new railtie for 2 reasons:
|
8
|
+
#
|
9
|
+
# * Initialize ActiveRecord properly
|
10
|
+
# * Add hstore:setup generator
|
11
|
+
class Hstore < Rails::Railtie
|
12
|
+
|
6
13
|
initializer 'activerecord-postgres-hstore' do
|
7
14
|
ActiveSupport.on_load :active_record do
|
8
15
|
require "activerecord-postgres-hstore/activerecord"
|
9
16
|
end
|
10
17
|
end
|
11
|
-
|
12
|
-
#
|
13
|
-
#
|
14
|
-
|
18
|
+
|
19
|
+
# Creates the hstore:setup generator. This generator creates a migration that
|
20
|
+
# adds hstore support for your database. If fact, it's just the sql from the
|
21
|
+
# contrib inside a migration. But it' s handy, isn't it?
|
22
|
+
#
|
23
|
+
# To use your generator, simply run it in your project:
|
24
|
+
#
|
25
|
+
# rails g hstore:setup
|
26
|
+
class Setup < Rails::Generators::Base
|
15
27
|
include Rails::Generators::Migration
|
16
28
|
|
17
29
|
def self.source_root
|
@@ -32,11 +44,5 @@ class Railtie < Rails::Railtie
|
|
32
44
|
|
33
45
|
end
|
34
46
|
end
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
require "activerecord-postgres-hstore/hstore"
|
41
47
|
require "activerecord-postgres-hstore/string"
|
42
|
-
require "activerecord-postgres-hstore/hash"
|
48
|
+
require "activerecord-postgres-hstore/hash"
|
@@ -1,36 +1,53 @@
|
|
1
|
-
#
|
1
|
+
# Extends AR to add Hstore functionality.
|
2
2
|
module ActiveRecord
|
3
|
+
|
4
|
+
# Adds methods for deleting keys in your hstore columns
|
3
5
|
class Base
|
6
|
+
|
7
|
+
# Deletes all keys from a specific column in a model. E.g.
|
8
|
+
# Person.delete_key(:info, :father)
|
9
|
+
# The SQL generated will be:
|
10
|
+
# UPDATE "people" SET "info" = delete("info",'father');
|
4
11
|
def self.delete_key attribute, key
|
5
|
-
|
6
|
-
|
7
|
-
raise "invalid attribute #{attribute}"
|
8
|
-
end
|
9
|
-
update_all(["#{attribute} = delete(#{attribute},?)",key])
|
12
|
+
raise "invalid attribute #{attribute}" unless column_names.include?(attribute.to_s)
|
13
|
+
update_all([%(#{attribute} = delete("#{attribute}",?)),key])
|
10
14
|
end
|
15
|
+
|
16
|
+
# Deletes many keys from a specific column in a model. E.g.
|
17
|
+
# Person.delete_key(:info, :father, :mother)
|
18
|
+
# The SQL generated will be:
|
19
|
+
# UPDATE "people" SET "info" = delete(delete("info",'father'),'mother');
|
11
20
|
def self.delete_keys attribute, *keys
|
12
|
-
unless column_names.include?(attribute.to_s)
|
13
|
-
raise "invalid attribute #{attribute}"
|
14
|
-
end
|
21
|
+
raise "invalid attribute #{attribute}" unless column_names.include?(attribute.to_s)
|
15
22
|
delete_str = "delete(#{attribute},?)"
|
16
|
-
(keys.count-1).times
|
17
|
-
delete_str = "delete(#{delete_str},?)"
|
18
|
-
end
|
23
|
+
(keys.count-1).times{ delete_str = "delete(#{delete_str},?)" }
|
19
24
|
update_all(["#{attribute} = #{delete_str}", *keys])
|
20
25
|
end
|
21
|
-
|
26
|
+
|
27
|
+
# Deletes a key in a record. E.g.
|
28
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
29
|
+
# witt.destroy_key(:info, :father)
|
30
|
+
# It does not save the record, so you'll have to do it.
|
22
31
|
def destroy_key attribute, key
|
23
|
-
unless self.class.column_names.include?(attribute.to_s)
|
24
|
-
raise "invalid attribute #{attribute}"
|
25
|
-
end
|
32
|
+
raise "invalid attribute #{attribute}" unless self.class.column_names.include?(attribute.to_s)
|
26
33
|
new_value = send(attribute)
|
27
34
|
new_value.delete(key.to_s)
|
28
35
|
send("#{attribute}=", new_value)
|
29
36
|
self
|
30
37
|
end
|
38
|
+
|
39
|
+
# Deletes a key in a record. E.g.
|
40
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
41
|
+
# witt.destroy_key(:info, :father)
|
42
|
+
# It does save the record.
|
31
43
|
def destroy_key! attribute, key
|
32
44
|
destroy_key(attribute, key).save
|
33
45
|
end
|
46
|
+
|
47
|
+
# Deletes many keys in a record. E.g.
|
48
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
49
|
+
# witt.destroy_keys(:info, :father, :mother)
|
50
|
+
# It does not save the record, so you'll have to do it.
|
34
51
|
def destroy_keys attribute, *keys
|
35
52
|
for key in keys
|
36
53
|
new_value = send(attribute)
|
@@ -39,23 +56,27 @@ module ActiveRecord
|
|
39
56
|
end
|
40
57
|
self
|
41
58
|
end
|
59
|
+
|
60
|
+
# Deletes many keys in a record. E.g.
|
61
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
62
|
+
# witt.destroy_keys!(:info, :father, :mother)
|
63
|
+
# It does save the record.
|
42
64
|
def destroy_keys! attribute, *keys
|
43
|
-
unless self.class.column_names.include?(attribute.to_s)
|
44
|
-
raise "invalid attribute #{attribute}"
|
45
|
-
end
|
65
|
+
raise "invalid attribute #{attribute}" unless self.class.column_names.include?(attribute.to_s)
|
46
66
|
destroy_keys(attribute, *keys).save
|
47
67
|
end
|
48
68
|
|
49
|
-
#
|
50
|
-
#
|
69
|
+
# This method is replaced for Rails 3 compatibility.
|
70
|
+
# All I do is add the condition when the field is a hash that converts the value
|
71
|
+
# to hstore format.
|
72
|
+
# IMHO this should be delegated to the column, so it won't be necessary to rewrite all
|
73
|
+
# this method.
|
51
74
|
def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
|
52
75
|
attrs = {}
|
53
76
|
attribute_names.each do |name|
|
54
77
|
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
|
55
|
-
|
56
78
|
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
|
57
79
|
value = read_attribute(name)
|
58
|
-
|
59
80
|
if value && ((self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time))) || value.is_a?(Hash) || value.is_a?(Array))
|
60
81
|
if self.class.columns_hash[name].type == :hstore
|
61
82
|
value = value.to_hstore # Done!
|
@@ -69,45 +90,62 @@ module ActiveRecord
|
|
69
90
|
end
|
70
91
|
attrs
|
71
92
|
end
|
93
|
+
|
72
94
|
end
|
95
|
+
|
96
|
+
# This erro class is used when the user passes a wrong value to a hstore column.
|
97
|
+
# Hstore columns accepts hashes or hstore valid strings. It is validated with
|
98
|
+
# String#valid_hstore? method.
|
73
99
|
class HstoreTypeMismatch < ActiveRecord::ActiveRecordError
|
74
100
|
end
|
101
|
+
|
75
102
|
module ConnectionAdapters
|
76
|
-
|
103
|
+
|
77
104
|
class TableDefinition
|
78
|
-
|
105
|
+
|
106
|
+
# Adds hstore type for migrations. So you can add columns to a table like:
|
107
|
+
# create_table :people do |t|
|
108
|
+
# ...
|
109
|
+
# t.hstore :info
|
110
|
+
# ...
|
111
|
+
# end
|
79
112
|
def hstore(*args)
|
80
113
|
options = args.extract_options!
|
81
114
|
column_names = args
|
82
115
|
column_names.each { |name| column(name, 'hstore', options) }
|
83
116
|
end
|
117
|
+
|
84
118
|
end
|
85
119
|
|
86
120
|
class PostgreSQLColumn < Column
|
87
121
|
alias :old_type_cast_code :type_cast_code
|
88
122
|
alias :old_simplified_type :simplified_type
|
89
|
-
|
123
|
+
|
124
|
+
# Does the type casting from hstore columns using String#from_hstore or Hash#from_hstore.
|
90
125
|
def type_cast_code(var_name)
|
91
126
|
type == :hstore ? "#{var_name}.from_hstore" : old_type_cast_code(var_name)
|
92
127
|
end
|
128
|
+
|
129
|
+
# Adds the hstore type for the column.
|
93
130
|
def simplified_type(field_type)
|
94
131
|
field_type =~ /^hstore$/ ? :hstore : old_simplified_type(field_type)
|
95
132
|
end
|
96
|
-
|
97
|
-
type == :hstore ? Hstore : old_klass
|
98
|
-
end
|
133
|
+
|
99
134
|
end
|
135
|
+
|
100
136
|
class PostgreSQLAdapter < AbstractAdapter
|
137
|
+
|
101
138
|
alias :old_quote :quote
|
139
|
+
alias :old_columns :columns
|
140
|
+
|
141
|
+
# Quotes correctly a hstore column value.
|
102
142
|
def quote(value, column = nil)
|
103
143
|
if value && column && column.sql_type =~ /^hstore$/
|
104
|
-
|
105
|
-
raise HstoreTypeMismatch, "#{column.name} must have a Hash or a valid hstore value (#{value})"
|
106
|
-
end
|
144
|
+
raise HstoreTypeMismatch, "#{column.name} must have a Hash or a valid hstore value (#{value})" unless value.kind_of?(Hash) || value.valid_hstore?
|
107
145
|
return value.to_hstore
|
108
146
|
end
|
109
147
|
old_quote(value,column)
|
110
148
|
end
|
111
149
|
end
|
112
150
|
end
|
113
|
-
end
|
151
|
+
end
|
@@ -1,11 +1,14 @@
|
|
1
1
|
class Hash
|
2
2
|
|
3
|
+
# Generates a single quoted hstore string format. This is the format used
|
4
|
+
# to insert or update stuff in the database.
|
3
5
|
def to_hstore
|
4
6
|
return "''" if empty?
|
5
7
|
#@todo DIOGO! Check security issues with this quoting pleaz
|
6
8
|
map{|idx,val| "('#{idx}'=>'#{val.to_s.gsub(/'/,"''")}')" }.join(' || ')
|
7
9
|
end
|
8
10
|
|
11
|
+
# If the method from_hstore is called in a Hash, it just returns self.
|
9
12
|
def from_hstore
|
10
13
|
self
|
11
14
|
end
|
@@ -1,9 +1,15 @@
|
|
1
1
|
class String
|
2
2
|
|
3
|
+
# If the value os a column is already a String and it calls to_hstore, it
|
4
|
+
# just returns self. Validation occurs afterwards.
|
3
5
|
def to_hstore
|
4
6
|
self
|
5
7
|
end
|
6
8
|
|
9
|
+
# Validates the hstore format. Valid formats are:
|
10
|
+
# * An empty string
|
11
|
+
# * A string like %("foo"=>"bar"). I'll call it a "double quoted hstore format".
|
12
|
+
# * A string like %('foo'=>'bar'). I'll call it a "single quoted hstore format".
|
7
13
|
def valid_hstore?
|
8
14
|
return true if empty? || self == "''"
|
9
15
|
# This is what comes from the database
|
@@ -16,6 +22,8 @@ class String
|
|
16
22
|
self.match(dbl_quotes_re) || self.match(sngl_quotes_re)
|
17
23
|
end
|
18
24
|
|
25
|
+
# Creates a hash from a valid double quoted hstore format, 'cause this is the format
|
26
|
+
# that postgresql spits out.
|
19
27
|
def from_hstore
|
20
28
|
Hash[ scan(/"([^"]+)"=>"([^"]+)"/) ]
|
21
29
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-postgres-hstore
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 25
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 3
|
10
|
+
version: 0.0.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Juan Maiz
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-09-
|
18
|
+
date: 2010-09-12 00:00:00 -04:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|
@@ -52,7 +52,6 @@ files:
|
|
52
52
|
- lib/activerecord-postgres-hstore.rb
|
53
53
|
- lib/activerecord-postgres-hstore/activerecord.rb
|
54
54
|
- lib/activerecord-postgres-hstore/hash.rb
|
55
|
-
- lib/activerecord-postgres-hstore/hstore.rb
|
56
55
|
- lib/activerecord-postgres-hstore/string.rb
|
57
56
|
- lib/templates/setup_hstore.rb
|
58
57
|
- spec/activerecord-postgres-hstore_spec.rb
|