padrino-hstore 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE.md +7 -0
- data/README.md +14 -0
- data/lib/padrino-hstore.rb +6 -0
- data/lib/padrino-hstore/activerecord.rb +150 -0
- data/lib/padrino-hstore/hash.rb +29 -0
- data/lib/padrino-hstore/string.rb +43 -0
- metadata +72 -0
data/LICENSE.md
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright (c) 2012 Cory Buecker
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
# padrino-hstore
|
2
|
+
|
3
|
+
A hstore plugin for padrino using Activerecord on PostgreSQL.
|
4
|
+
|
5
|
+
All the really good parts are based on https://github.com/softa/activerecord-postgres-hstore; huge kudos there!
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add the following to your Gemfile (note that Padrino must be loaded first):
|
9
|
+
|
10
|
+
gem "padrino"
|
11
|
+
gem "padrino-hstore"
|
12
|
+
|
13
|
+
## Usage
|
14
|
+
Not much to tell here. You can use hstore as a column type and all string/hash conversions are handled for you. Check https://github.com/softa/activerecord-postgres-hstore for more details.
|
@@ -0,0 +1,150 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
|
3
|
+
# Adds methods for deleting keys in your hstore columns
|
4
|
+
class Base
|
5
|
+
|
6
|
+
# Deletes all keys from a specific column in a model. E.g.
|
7
|
+
# Person.delete_key(:info, :father)
|
8
|
+
# The SQL generated will be:
|
9
|
+
# UPDATE "people" SET "info" = delete("info",'father');
|
10
|
+
def self.delete_key attribute, key
|
11
|
+
raise "invalid attribute #{attribute}" unless column_names.include?(attribute.to_s)
|
12
|
+
update_all([%(#{attribute} = delete("#{attribute}",?)),key])
|
13
|
+
end
|
14
|
+
|
15
|
+
# Deletes many keys from a specific column in a model. E.g.
|
16
|
+
# Person.delete_key(:info, :father, :mother)
|
17
|
+
# The SQL generated will be:
|
18
|
+
# UPDATE "people" SET "info" = delete(delete("info",'father'),'mother');
|
19
|
+
def self.delete_keys attribute, *keys
|
20
|
+
raise "invalid attribute #{attribute}" unless column_names.include?(attribute.to_s)
|
21
|
+
delete_str = "delete(#{attribute},?)"
|
22
|
+
(keys.count-1).times{ delete_str = "delete(#{delete_str},?)" }
|
23
|
+
update_all(["#{attribute} = #{delete_str}", *keys])
|
24
|
+
end
|
25
|
+
|
26
|
+
# Deletes a key in a record. E.g.
|
27
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
28
|
+
# witt.destroy_key(:info, :father)
|
29
|
+
# It does not save the record, so you'll have to do it.
|
30
|
+
def destroy_key attribute, key
|
31
|
+
raise "invalid attribute #{attribute}" unless self.class.column_names.include?(attribute.to_s)
|
32
|
+
new_value = send(attribute)
|
33
|
+
new_value.delete(key.to_s)
|
34
|
+
send("#{attribute}=", new_value)
|
35
|
+
self
|
36
|
+
end
|
37
|
+
|
38
|
+
# Deletes a key in a record. E.g.
|
39
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
40
|
+
# witt.destroy_key(:info, :father)
|
41
|
+
# It does save the record.
|
42
|
+
def destroy_key! attribute, key
|
43
|
+
destroy_key(attribute, key).save
|
44
|
+
end
|
45
|
+
|
46
|
+
# Deletes many keys in a record. E.g.
|
47
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
48
|
+
# witt.destroy_keys(:info, :father, :mother)
|
49
|
+
# It does not save the record, so you'll have to do it.
|
50
|
+
def destroy_keys attribute, *keys
|
51
|
+
for key in keys
|
52
|
+
new_value = send(attribute)
|
53
|
+
new_value.delete(key.to_s)
|
54
|
+
send("#{attribute}=", new_value)
|
55
|
+
end
|
56
|
+
self
|
57
|
+
end
|
58
|
+
|
59
|
+
# Deletes many keys in a record. E.g.
|
60
|
+
# witt = Person.find_by_name("Ludwig Wittgenstein")
|
61
|
+
# witt.destroy_keys!(:info, :father, :mother)
|
62
|
+
# It does save the record.
|
63
|
+
def destroy_keys! attribute, *keys
|
64
|
+
raise "invalid attribute #{attribute}" unless self.class.column_names.include?(attribute.to_s)
|
65
|
+
destroy_keys(attribute, *keys).save
|
66
|
+
end
|
67
|
+
|
68
|
+
# This method is replaced for Rails 3 compatibility.
|
69
|
+
# All I do is add the condition when the field is a hash that converts the value
|
70
|
+
# to hstore format.
|
71
|
+
# IMHO this should be delegated to the column, so it won't be necessary to rewrite all
|
72
|
+
# this method.
|
73
|
+
def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
|
74
|
+
attrs = {}
|
75
|
+
attribute_names.each do |name|
|
76
|
+
if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
|
77
|
+
if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
|
78
|
+
value = read_attribute(name)
|
79
|
+
if self.class.columns_hash[name].type == :hstore && value && value.is_a?(Hash)
|
80
|
+
value = value.to_hstore # Done!
|
81
|
+
elsif value && self.class.serialized_attributes.has_key?(name) && (value.acts_like?(:date) || value.acts_like?(:time) || value.is_a?(Hash) || value.is_a?(Array))
|
82
|
+
value = value.to_yaml
|
83
|
+
end
|
84
|
+
attrs[self.class.arel_table[name]] = value
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
attrs
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
# This erro class is used when the user passes a wrong value to a hstore column.
|
94
|
+
# Hstore columns accepts hashes or hstore valid strings. It is validated with
|
95
|
+
# String#valid_hstore? method.
|
96
|
+
class HstoreTypeMismatch < ActiveRecord::ActiveRecordError
|
97
|
+
end
|
98
|
+
|
99
|
+
module ConnectionAdapters
|
100
|
+
|
101
|
+
class TableDefinition
|
102
|
+
|
103
|
+
# Adds hstore type for migrations. So you can add columns to a table like:
|
104
|
+
# create_table :people do |t|
|
105
|
+
# ...
|
106
|
+
# t.hstore :info
|
107
|
+
# ...
|
108
|
+
# end
|
109
|
+
def hstore(*args)
|
110
|
+
options = args.extract_options!
|
111
|
+
column_names = args
|
112
|
+
column_names.each { |name| column(name, 'hstore', options) }
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class PostgreSQLColumn < Column
|
118
|
+
# Does the type casting from hstore columns using String#from_hstore or Hash#from_hstore.
|
119
|
+
def type_cast_code_with_hstore(var_name)
|
120
|
+
type == :hstore ? "#{var_name}.from_hstore" : type_cast_code_without_hstore(var_name)
|
121
|
+
end
|
122
|
+
|
123
|
+
# Adds the hstore type for the column.
|
124
|
+
def simplified_type_with_hstore(field_type)
|
125
|
+
field_type == 'hstore' ? :hstore : simplified_type_without_hstore(field_type)
|
126
|
+
end
|
127
|
+
|
128
|
+
alias_method_chain :type_cast_code, :hstore
|
129
|
+
alias_method_chain :simplified_type, :hstore
|
130
|
+
end
|
131
|
+
|
132
|
+
class PostgreSQLAdapter < AbstractAdapter
|
133
|
+
def native_database_types_with_hstore
|
134
|
+
native_database_types_without_hstore.merge({:hstore => { :name => "hstore" }})
|
135
|
+
end
|
136
|
+
|
137
|
+
# Quotes correctly a hstore column value.
|
138
|
+
def quote_with_hstore(value, column = nil)
|
139
|
+
if value && column && column.sql_type == 'hstore'
|
140
|
+
raise HstoreTypeMismatch, "#{column.name} must have a Hash or a valid hstore value (#{value})" unless value.kind_of?(Hash) || value.valid_hstore?
|
141
|
+
return quote_without_hstore(value.to_hstore, column)
|
142
|
+
end
|
143
|
+
quote_without_hstore(value,column)
|
144
|
+
end
|
145
|
+
|
146
|
+
alias_method_chain :quote, :hstore
|
147
|
+
alias_method_chain :native_database_types, :hstore
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
# Generates an hstore string format. This is the format used
|
4
|
+
# to insert or update stuff in the database.
|
5
|
+
def to_hstore
|
6
|
+
return "" if empty?
|
7
|
+
|
8
|
+
map { |idx, val|
|
9
|
+
iv = [idx,val].map { |_|
|
10
|
+
e = _.to_s.gsub(/"/, '\"')
|
11
|
+
if _.nil?
|
12
|
+
'NULL'
|
13
|
+
elsif e =~ /[,\s=>]/ || e.blank?
|
14
|
+
'"%s"' % e
|
15
|
+
else
|
16
|
+
e
|
17
|
+
end
|
18
|
+
}
|
19
|
+
|
20
|
+
"%s=>%s" % iv
|
21
|
+
} * ","
|
22
|
+
end
|
23
|
+
|
24
|
+
# If the method from_hstore is called in a Hash, it just returns self.
|
25
|
+
def from_hstore
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class String
|
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.
|
5
|
+
def to_hstore
|
6
|
+
self
|
7
|
+
end
|
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). Postgres doesn't emit this but it does accept it as input, we should accept any input Postgres does
|
13
|
+
|
14
|
+
def valid_hstore?
|
15
|
+
pair = hstore_pair
|
16
|
+
!!match(/^\s*(#{pair}\s*(,\s*#{pair})*)?\s*$/)
|
17
|
+
end
|
18
|
+
|
19
|
+
# Creates a hash from a valid double quoted hstore format, 'cause this is the format
|
20
|
+
# that postgresql spits out.
|
21
|
+
def from_hstore
|
22
|
+
token_pairs = (scan(hstore_pair)).map { |k,v| [k,v =~ /^NULL$/i ? nil : v] }
|
23
|
+
token_pairs = token_pairs.map { |k,v|
|
24
|
+
[k,v].map { |t|
|
25
|
+
case t
|
26
|
+
when nil then t
|
27
|
+
when /^"(.*)"$/ then $1.gsub(/\\(.)/, '\1')
|
28
|
+
else t.gsub(/\\(.)/, '\1')
|
29
|
+
end
|
30
|
+
}
|
31
|
+
}
|
32
|
+
Hash[ token_pairs ]
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def hstore_pair
|
38
|
+
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
|
39
|
+
unquoted_string = /[^\s=,][^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
|
40
|
+
string = /(#{quoted_string}|#{unquoted_string})/
|
41
|
+
/#{string}\s*=>\s*#{string}/
|
42
|
+
end
|
43
|
+
end
|
metadata
ADDED
@@ -0,0 +1,72 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: padrino-hstore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Cory Buecker
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-05-08 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activerecord
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - <
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '4'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - <
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '4'
|
30
|
+
description: This gem adds support for hstore to Activerecord (< version 4) on the
|
31
|
+
Padrino framework. It is based on the excellent work from https://github.com/softa/activerecord-postgres-hstore.
|
32
|
+
email: email@corybuecker.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files:
|
36
|
+
- LICENSE.md
|
37
|
+
- README.md
|
38
|
+
files:
|
39
|
+
- lib/padrino-hstore.rb
|
40
|
+
- lib/padrino-hstore/activerecord.rb
|
41
|
+
- lib/padrino-hstore/string.rb
|
42
|
+
- lib/padrino-hstore/hash.rb
|
43
|
+
- LICENSE.md
|
44
|
+
- README.md
|
45
|
+
homepage: https://github.com/thegreyjoy/padrino-hstore
|
46
|
+
licenses:
|
47
|
+
- MIT
|
48
|
+
post_install_message:
|
49
|
+
rdoc_options: []
|
50
|
+
require_paths:
|
51
|
+
- lib
|
52
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
53
|
+
none: false
|
54
|
+
requirements:
|
55
|
+
- - ! '>='
|
56
|
+
- !ruby/object:Gem::Version
|
57
|
+
version: '0'
|
58
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
59
|
+
none: false
|
60
|
+
requirements:
|
61
|
+
- - ! '>='
|
62
|
+
- !ruby/object:Gem::Version
|
63
|
+
version: '0'
|
64
|
+
requirements: []
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.8.24
|
67
|
+
signing_key:
|
68
|
+
specification_version: 3
|
69
|
+
summary: This gem adds support for hstore to Activerecord (< version 4) on the Padrino
|
70
|
+
framework.
|
71
|
+
test_files: []
|
72
|
+
has_rdoc:
|