padrino-hstore 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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.
@@ -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,6 @@
1
+ require "padrino-hstore/string"
2
+ require "padrino-hstore/hash"
3
+
4
+ Padrino.after_load do
5
+ require "padrino-hstore/activerecord"
6
+ end
@@ -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: