activerecord-postgres-hstore 0.0.1

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Juan Maiz
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.textile ADDED
@@ -0,0 +1,109 @@
1
+ .h2 Goodbye serialize, hello hstore.
2
+
3
+ You need dynamic columns in your tables. What do you do?
4
+
5
+ * Create lots of tables to handle it.
6
+ Nice, now you'll need more models and lots of additional sqls. Insertion and selection will be slow as hell.
7
+
8
+ * Use a noSQL database just for this issue.
9
+ Good luck.
10
+
11
+ * Create a serialized column.
12
+ Nice, insertion will be fine, and reading data from a record too. But, what if you have a condition in your select that includes serialized data? Yeah, regular expressions.
13
+
14
+ !https://spreadsheets.google.com/oimg?key=0AoQcKrACYfGjdGRVV3cyTDZYQkJMa255VkxxQW9LTHc&oid=1&zx=ksfsz3-j87df5!
15
+
16
+ |action|serialize|hstore|serialize sql|hstore sql|
17
+ |count all with condition|10114,575|1830,444|SELECT count(*) FROM foos WHERE data ~ 'foo: bar';|SELECT count(*) FROM bars WHERE data @> 'foo=>bar';|
18
+ |count all with negative condition|18722,149|1677,948|SELECT count(*) FROM foos WHERE data !~ 'another key: 9999990';|SELECT count(*) FROM bars WHERE not data @> '"another key"=>9999990';|
19
+ |find one with condition|17740,307|130,227|SELECT count(*) FROM foos WHERE data ~ 'another key: 9999990';|SELECT * FROM bars WHERE data @> '"another key"=>9999990'|
20
+
21
+ Benchmarks made in my local machine, time in milliseconds.
22
+
23
+ .h2 Requirements
24
+
25
+ Postgresql 8.4 with contrib and Rails 3. (It might work on 2.3.x with minor patches...)
26
+
27
+ .h2 Install
28
+
29
+ Hstore is a postgres contrib type. Check it out first:
30
+
31
+ http://www.postgresql.org/docs/8.4/static/hstore.html
32
+
33
+ Then, just add this to your Gemfile:
34
+
35
+ gem 'activerecord-postgres-hstore'
36
+
37
+ And run your bundler:
38
+
39
+ bundle install
40
+
41
+ Now you need to create a migration that adds hstore support for your postgresql database:
42
+
43
+ rails g railtie:hstore
44
+
45
+ Run it:
46
+
47
+ rake db:migrate
48
+
49
+ Finally you can create your own tables using hstore type. It's easy:
50
+
51
+ rails g model Person name:string data:hstore
52
+
53
+ You're done.
54
+
55
+ Well, not yet. Don't forget to add indexes. Like this:
56
+
57
+ CREATE INDEX people_gist_data ON people USING GIST(data);
58
+ -- or
59
+ CREATE INDEX people_gin_data ON people USING GIN(data);
60
+
61
+ To my experience GIN is faster for searching records.
62
+
63
+ .h2 Usage
64
+
65
+ Once you have it installed, you just need to learn a little bit of new sqls for selecting stuff (creting and updating is transparent).
66
+
67
+ Find records that contains a key named 'foo':
68
+
69
+ Person.where("data ? 'foo'")
70
+
71
+ Find records where 'foo' is equal to 'bar':
72
+
73
+ Person.where("data -> 'foo' = 'bar'")
74
+
75
+ This same sql is at least twice as fast (using indexes) if you do it that way:
76
+
77
+ Person.where("data @> 'foo=>bar'")
78
+
79
+ Find records where 'foo' is not equal to 'bar':
80
+
81
+ Person.where("data -> 'foo' <> 'bar'")
82
+ # or
83
+ Person.where("not data @> 'foo=>bar'")
84
+
85
+ Find records where 'foo' is like 'bar':
86
+
87
+ Person.where("data -> 'foo' LIKE '%bar%'")
88
+ # or something like ...
89
+ Person.where("data -> 'foo' ILIKE '%bar%'")
90
+
91
+ Have fun.
92
+
93
+ .h2 Help
94
+
95
+ You can use issues in github for that. Or else you can reach me at twitter: @joaomilho
96
+
97
+ .h2 Note on Patches/Pull Requests
98
+
99
+ * Fork the project.
100
+ * Make your feature addition or bug fix.
101
+ * Add tests for it. This is important so I don't break it in a
102
+ future version unintentionally.
103
+ * Commit, do not mess with rakefile, version, or history.
104
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
105
+ * Send me a pull request. Bonus points for topic branches.
106
+
107
+ .h2 Copyright
108
+
109
+ Copyright (c) 2010 Juan Maiz. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,44 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "activerecord-postgres-hstore"
8
+ gem.summary = %Q{Goodbye serialize, hello hstore}
9
+ gem.description = %Q{This gem adds support for the postgres hstore type. It is the _just right_ alternative for storing hashes instead of using seralization or dynamic tables.}
10
+ gem.email = "juanmaiz@gmail.com"
11
+ gem.homepage = "http://github.com/softa/activerecord-postgres-hstore"
12
+ gem.authors = ["Juan Maiz"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ gem.files = FileList['.document', '.gitignore', 'LICENSE', 'README.rdoc', 'Rakefile', 'VERSION', 'spec/**/*_spec.rb', 'lib/**/*.rb'].to_a
15
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
+ end
17
+ Jeweler::GemcutterTasks.new
18
+ rescue LoadError
19
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
20
+ end
21
+
22
+ require 'spec/rake/spectask'
23
+ Spec::Rake::SpecTask.new(:spec) do |spec|
24
+ spec.libs << 'lib' << 'spec'
25
+ spec.spec_files = FileList['spec/**/*_spec.rb']
26
+ end
27
+
28
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
29
+ spec.libs << 'lib' << 'spec'
30
+ spec.pattern = 'spec/**/*_spec.rb'
31
+ spec.rcov = true
32
+ end
33
+
34
+ task :spec => :check_dependencies
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+ rdoc.rdoc_dir = 'rdoc'
41
+ rdoc.title = "activerecord-postgres-hstore #{version}"
42
+ rdoc.rdoc_files.include('README*')
43
+ rdoc.rdoc_files.include('lib/**/*.rb')
44
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,42 @@
1
+ #raise ActiveRecord::ConnectionAdapters::Column.inspect
2
+ require 'rails'
3
+ require 'rails/generators'
4
+ require 'rails/generators/migration'
5
+ class Railtie < Rails::Railtie
6
+ initializer 'activerecord-postgres-hstore' do
7
+ ActiveSupport.on_load :active_record do
8
+ require "activerecord-postgres-hstore/activerecord"
9
+ end
10
+ end
11
+ #rake_tasks do
12
+ # load "lib/tasks/hstore.rake"
13
+ #end
14
+ class HstoreGenerator < Rails::Generators::Base
15
+ include Rails::Generators::Migration
16
+
17
+ def self.source_root
18
+ @source_root ||= File.join(File.dirname(__FILE__), 'templates')
19
+ end
20
+
21
+ def self.next_migration_number(dirname)
22
+ if ActiveRecord::Base.timestamped_migrations
23
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
24
+ else
25
+ "%.3d" % (current_migration_number(dirname) + 1)
26
+ end
27
+ end
28
+
29
+ def create_migration_file
30
+ migration_template 'setup_hstore.rb', 'db/migrate/setup_hstore.rb'
31
+ end
32
+
33
+ end
34
+ end
35
+
36
+
37
+
38
+
39
+
40
+ require "activerecord-postgres-hstore/hstore"
41
+ require "activerecord-postgres-hstore/string"
42
+ require "activerecord-postgres-hstore/hash"
@@ -0,0 +1,68 @@
1
+ # ActiveRecord::ConnectionAdapters::PostgreSQLColumn.new('data','','hstore').type
2
+ module ActiveRecord
3
+ class Base
4
+ # For Rails 3 compat :D
5
+ #alias :old_arel_attributes_values :arel_attributes_values
6
+ def arel_attributes_values(include_primary_key = true, include_readonly_attributes = true, attribute_names = @attributes.keys)
7
+ attrs = {}
8
+ attribute_names.each do |name|
9
+ if (column = column_for_attribute(name)) && (include_primary_key || !column.primary)
10
+
11
+ if include_readonly_attributes || (!include_readonly_attributes && !self.class.readonly_attributes.include?(name))
12
+ value = read_attribute(name)
13
+
14
+ 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))
15
+ if self.class.columns_hash[name].type == :hstore
16
+ value = value.to_hstore # Done!
17
+ else
18
+ value = value.to_yaml
19
+ end
20
+ end
21
+ attrs[self.class.arel_table[name]] = value
22
+ end
23
+ end
24
+ end
25
+ attrs
26
+ end
27
+ end
28
+ class HstoreTypeMismatch < ActiveRecord::ActiveRecordError
29
+ end
30
+ module ConnectionAdapters
31
+
32
+ class TableDefinition
33
+ # Adds hstore type for migrations
34
+ def hstore(*args)
35
+ options = args.extract_options!
36
+ column_names = args
37
+ column_names.each { |name| column(name, 'hstore', options) }
38
+ end
39
+ end
40
+
41
+ class PostgreSQLColumn < Column
42
+ alias :old_type_cast_code :type_cast_code
43
+ alias :old_simplified_type :simplified_type
44
+ alias :old_klass :klass
45
+ def type_cast_code(var_name)
46
+ type == :hstore ? "#{var_name}.from_hstore" : old_type_cast_code(var_name)
47
+ end
48
+ def simplified_type(field_type)
49
+ field_type =~ /^hstore$/ ? :hstore : old_simplified_type(field_type)
50
+ end
51
+ def klass
52
+ type == :hstore ? Hstore : old_klass
53
+ end
54
+ end
55
+ class PostgreSQLAdapter < AbstractAdapter
56
+ alias :old_quote :quote
57
+ def quote(value, column = nil)
58
+ if value && column && column.sql_type =~ /^hstore$/
59
+ if ! value.kind_of?(Hash) and ! value.valid_hstore?
60
+ raise HstoreTypeMismatch, "#{column.name} must have a Hash or a valid hstore value (#{value})"
61
+ end
62
+ return value.to_hstore
63
+ end
64
+ old_quote(value,column)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,12 @@
1
+ class Hash
2
+
3
+ def to_hstore
4
+ #@todo DIOGO! Check security issues with this quoting pleaz
5
+ map{|idx,val| "('#{idx}'=>'#{val.to_s.gsub(/'/,"''")}')" }.join(' || ')
6
+ end
7
+
8
+ def from_hstore
9
+ self
10
+ end
11
+
12
+ end
@@ -0,0 +1,2 @@
1
+ class Hstore < Hash
2
+ end
@@ -0,0 +1,22 @@
1
+ class String
2
+
3
+ def to_hstore
4
+ self
5
+ end
6
+
7
+ def valid_hstore?
8
+ # This is what comes from the database
9
+ dbl_quotes_re = /"([^"]+)"=>"([^"]+)"/
10
+ # TODO
11
+ # This is what comes from the plugin
12
+ # this is a big problem, 'cause regexes does not know how to count...
13
+ # how should i very values quoted with two single quotes? using .+ sux.
14
+ sngl_quotes_re = /'(.+)'=>'(.+)'/
15
+ self.match(dbl_quotes_re) || self.match(sngl_quotes_re)
16
+ end
17
+
18
+ def from_hstore
19
+ Hash[ scan(/"([^"]+)"=>"([^"]+)"/) ]
20
+ end
21
+
22
+ end
@@ -0,0 +1,277 @@
1
+ class SetupHstore < ActiveRecord::Migration
2
+ def self.up
3
+ sql = <<-HSTORE_SQL
4
+ /* $PostgreSQL: pgsql/contrib/hstore/hstore.sql.in,v 1.11 2009/06/11 18:30:03 tgl Exp $ */
5
+
6
+ -- Adjust this setting to control where the objects get created.
7
+ SET search_path = public;
8
+
9
+ CREATE TYPE hstore;
10
+
11
+ CREATE OR REPLACE FUNCTION hstore_in(cstring)
12
+ RETURNS hstore
13
+ AS '$libdir/hstore'
14
+ LANGUAGE C STRICT;
15
+
16
+ CREATE OR REPLACE FUNCTION hstore_out(hstore)
17
+ RETURNS cstring
18
+ AS '$libdir/hstore'
19
+ LANGUAGE C STRICT;
20
+
21
+ CREATE TYPE hstore (
22
+ INTERNALLENGTH = -1,
23
+ INPUT = hstore_in,
24
+ OUTPUT = hstore_out,
25
+ STORAGE = extended
26
+ );
27
+
28
+ CREATE OR REPLACE FUNCTION fetchval(hstore,text)
29
+ RETURNS text
30
+ AS '$libdir/hstore'
31
+ LANGUAGE C STRICT IMMUTABLE;
32
+
33
+ CREATE OPERATOR -> (
34
+ LEFTARG = hstore,
35
+ RIGHTARG = text,
36
+ PROCEDURE = fetchval
37
+ );
38
+
39
+ CREATE OR REPLACE FUNCTION isexists(hstore,text)
40
+ RETURNS bool
41
+ AS '$libdir/hstore','exists'
42
+ LANGUAGE C STRICT IMMUTABLE;
43
+
44
+ CREATE OR REPLACE FUNCTION exist(hstore,text)
45
+ RETURNS bool
46
+ AS '$libdir/hstore','exists'
47
+ LANGUAGE C STRICT IMMUTABLE;
48
+
49
+ CREATE OPERATOR ? (
50
+ LEFTARG = hstore,
51
+ RIGHTARG = text,
52
+ PROCEDURE = exist,
53
+ RESTRICT = contsel,
54
+ JOIN = contjoinsel
55
+ );
56
+
57
+ CREATE OR REPLACE FUNCTION isdefined(hstore,text)
58
+ RETURNS bool
59
+ AS '$libdir/hstore','defined'
60
+ LANGUAGE C STRICT IMMUTABLE;
61
+
62
+ CREATE OR REPLACE FUNCTION defined(hstore,text)
63
+ RETURNS bool
64
+ AS '$libdir/hstore','defined'
65
+ LANGUAGE C STRICT IMMUTABLE;
66
+
67
+ CREATE OR REPLACE FUNCTION delete(hstore,text)
68
+ RETURNS hstore
69
+ AS '$libdir/hstore','delete'
70
+ LANGUAGE C STRICT IMMUTABLE;
71
+
72
+ CREATE OR REPLACE FUNCTION hs_concat(hstore,hstore)
73
+ RETURNS hstore
74
+ AS '$libdir/hstore'
75
+ LANGUAGE C STRICT IMMUTABLE;
76
+
77
+ CREATE OPERATOR || (
78
+ LEFTARG = hstore,
79
+ RIGHTARG = hstore,
80
+ PROCEDURE = hs_concat
81
+ );
82
+
83
+ CREATE OR REPLACE FUNCTION hs_contains(hstore,hstore)
84
+ RETURNS bool
85
+ AS '$libdir/hstore'
86
+ LANGUAGE C STRICT IMMUTABLE;
87
+
88
+ CREATE OR REPLACE FUNCTION hs_contained(hstore,hstore)
89
+ RETURNS bool
90
+ AS '$libdir/hstore'
91
+ LANGUAGE C STRICT IMMUTABLE;
92
+
93
+ CREATE OPERATOR @> (
94
+ LEFTARG = hstore,
95
+ RIGHTARG = hstore,
96
+ PROCEDURE = hs_contains,
97
+ COMMUTATOR = '<@',
98
+ RESTRICT = contsel,
99
+ JOIN = contjoinsel
100
+ );
101
+
102
+ CREATE OPERATOR <@ (
103
+ LEFTARG = hstore,
104
+ RIGHTARG = hstore,
105
+ PROCEDURE = hs_contained,
106
+ COMMUTATOR = '@>',
107
+ RESTRICT = contsel,
108
+ JOIN = contjoinsel
109
+ );
110
+
111
+ -- obsolete:
112
+ CREATE OPERATOR @ (
113
+ LEFTARG = hstore,
114
+ RIGHTARG = hstore,
115
+ PROCEDURE = hs_contains,
116
+ COMMUTATOR = '~',
117
+ RESTRICT = contsel,
118
+ JOIN = contjoinsel
119
+ );
120
+
121
+ CREATE OPERATOR ~ (
122
+ LEFTARG = hstore,
123
+ RIGHTARG = hstore,
124
+ PROCEDURE = hs_contained,
125
+ COMMUTATOR = '@',
126
+ RESTRICT = contsel,
127
+ JOIN = contjoinsel
128
+ );
129
+
130
+ CREATE OR REPLACE FUNCTION tconvert(text,text)
131
+ RETURNS hstore
132
+ AS '$libdir/hstore'
133
+ LANGUAGE C IMMUTABLE; -- not STRICT
134
+
135
+ CREATE OPERATOR => (
136
+ LEFTARG = text,
137
+ RIGHTARG = text,
138
+ PROCEDURE = tconvert
139
+ );
140
+
141
+ CREATE OR REPLACE FUNCTION akeys(hstore)
142
+ RETURNS _text
143
+ AS '$libdir/hstore'
144
+ LANGUAGE C STRICT IMMUTABLE;
145
+
146
+ CREATE OR REPLACE FUNCTION avals(hstore)
147
+ RETURNS _text
148
+ AS '$libdir/hstore'
149
+ LANGUAGE C STRICT IMMUTABLE;
150
+
151
+ CREATE OR REPLACE FUNCTION skeys(hstore)
152
+ RETURNS setof text
153
+ AS '$libdir/hstore'
154
+ LANGUAGE C STRICT IMMUTABLE;
155
+
156
+ CREATE OR REPLACE FUNCTION svals(hstore)
157
+ RETURNS setof text
158
+ AS '$libdir/hstore'
159
+ LANGUAGE C STRICT IMMUTABLE;
160
+
161
+ CREATE OR REPLACE FUNCTION each(IN hs hstore,
162
+ OUT key text,
163
+ OUT value text)
164
+ RETURNS SETOF record
165
+ AS '$libdir/hstore'
166
+ LANGUAGE C STRICT IMMUTABLE;
167
+
168
+
169
+
170
+ -- define the GiST support methods
171
+
172
+ CREATE TYPE ghstore;
173
+
174
+ CREATE OR REPLACE FUNCTION ghstore_in(cstring)
175
+ RETURNS ghstore
176
+ AS '$libdir/hstore'
177
+ LANGUAGE C STRICT;
178
+
179
+ CREATE OR REPLACE FUNCTION ghstore_out(ghstore)
180
+ RETURNS cstring
181
+ AS '$libdir/hstore'
182
+ LANGUAGE C STRICT;
183
+
184
+ CREATE TYPE ghstore (
185
+ INTERNALLENGTH = -1,
186
+ INPUT = ghstore_in,
187
+ OUTPUT = ghstore_out
188
+ );
189
+
190
+ CREATE OR REPLACE FUNCTION ghstore_compress(internal)
191
+ RETURNS internal
192
+ AS '$libdir/hstore'
193
+ LANGUAGE C IMMUTABLE STRICT;
194
+
195
+ CREATE OR REPLACE FUNCTION ghstore_decompress(internal)
196
+ RETURNS internal
197
+ AS '$libdir/hstore'
198
+ LANGUAGE C IMMUTABLE STRICT;
199
+
200
+ CREATE OR REPLACE FUNCTION ghstore_penalty(internal,internal,internal)
201
+ RETURNS internal
202
+ AS '$libdir/hstore'
203
+ LANGUAGE C IMMUTABLE STRICT;
204
+
205
+ CREATE OR REPLACE FUNCTION ghstore_picksplit(internal, internal)
206
+ RETURNS internal
207
+ AS '$libdir/hstore'
208
+ LANGUAGE C IMMUTABLE STRICT;
209
+
210
+ CREATE OR REPLACE FUNCTION ghstore_union(internal, internal)
211
+ RETURNS internal
212
+ AS '$libdir/hstore'
213
+ LANGUAGE C IMMUTABLE STRICT;
214
+
215
+ CREATE OR REPLACE FUNCTION ghstore_same(internal, internal, internal)
216
+ RETURNS internal
217
+ AS '$libdir/hstore'
218
+ LANGUAGE C IMMUTABLE STRICT;
219
+
220
+ CREATE OR REPLACE FUNCTION ghstore_consistent(internal,internal,int,oid,internal)
221
+ RETURNS bool
222
+ AS '$libdir/hstore'
223
+ LANGUAGE C IMMUTABLE STRICT;
224
+
225
+ -- register the opclass for indexing (not as default)
226
+ CREATE OPERATOR CLASS gist_hstore_ops
227
+ DEFAULT FOR TYPE hstore USING gist
228
+ AS
229
+ OPERATOR 7 @> ,
230
+ OPERATOR 9 ?(hstore,text) ,
231
+ --OPERATOR 8 <@ ,
232
+ OPERATOR 13 @ ,
233
+ --OPERATOR 14 ~ ,
234
+ FUNCTION 1 ghstore_consistent (internal, internal, int, oid, internal),
235
+ FUNCTION 2 ghstore_union (internal, internal),
236
+ FUNCTION 3 ghstore_compress (internal),
237
+ FUNCTION 4 ghstore_decompress (internal),
238
+ FUNCTION 5 ghstore_penalty (internal, internal, internal),
239
+ FUNCTION 6 ghstore_picksplit (internal, internal),
240
+ FUNCTION 7 ghstore_same (internal, internal, internal),
241
+ STORAGE ghstore;
242
+
243
+ -- define the GIN support methods
244
+
245
+ CREATE OR REPLACE FUNCTION gin_extract_hstore(internal, internal)
246
+ RETURNS internal
247
+ AS '$libdir/hstore'
248
+ LANGUAGE C IMMUTABLE STRICT;
249
+
250
+ CREATE OR REPLACE FUNCTION gin_extract_hstore_query(internal, internal, int2, internal, internal)
251
+ RETURNS internal
252
+ AS '$libdir/hstore'
253
+ LANGUAGE C IMMUTABLE STRICT;
254
+
255
+ CREATE OR REPLACE FUNCTION gin_consistent_hstore(internal, int2, internal, int4, internal, internal)
256
+ RETURNS bool
257
+ AS '$libdir/hstore'
258
+ LANGUAGE C IMMUTABLE STRICT;
259
+
260
+ CREATE OPERATOR CLASS gin_hstore_ops
261
+ DEFAULT FOR TYPE hstore USING gin
262
+ AS
263
+ OPERATOR 7 @> ,
264
+ OPERATOR 9 ?(hstore,text),
265
+ FUNCTION 1 bttextcmp(text,text),
266
+ FUNCTION 2 gin_extract_hstore(internal, internal),
267
+ FUNCTION 3 gin_extract_hstore_query(internal, internal, int2, internal, internal),
268
+ FUNCTION 4 gin_consistent_hstore(internal, int2, internal, int4, internal, internal),
269
+ STORAGE text;
270
+ HSTORE_SQL
271
+ execute sql
272
+ end
273
+
274
+ def self.down
275
+ # Kinda... hard.
276
+ end
277
+ end
@@ -0,0 +1,39 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "ActiverecordPostgresHstore" do
4
+ it "should convert hstore string to hash" do
5
+ {:a => 1, :b => 2}.to_hstore.should == "('a'=>'1') || ('b'=>'2')"
6
+ end
7
+
8
+ it "should convert hash to hstore string" do
9
+ '"a"=>"1", "b"=>"2"'.from_hstore.should == {'a' => '1', 'b' => '2'}
10
+ end
11
+
12
+ it "should quote correctly" do
13
+ {:a => "'a'"}.to_hstore.should == "('a'=>'''a''')"
14
+ end
15
+
16
+ end
17
+
18
+ =begin
19
+
20
+ #---
21
+
22
+ test "should create contact" do
23
+ assert Contact.make :dynamic_values => {:a => 1, :b => 2}
24
+ end
25
+
26
+ test "should raise HstoreTypeMismatch" do
27
+ assert_raises ActiveRecord::HstoreTypeMismatch do
28
+ assert Contact.make :dynamic_values => "bug"
29
+ end
30
+ end
31
+
32
+ test "should read values from contact" do
33
+ contact = Contact.make :dynamic_values => {:a => 1, :b => "Lorem ipsum", 'other stuff' => "'''a'''"}
34
+ assert_equal({'a' => '1', 'b' => 'Lorem ipsum', 'other stuff' => "'''a'''"}, Contact.find(contact.id).dynamic_values)
35
+ end
36
+
37
+ end
38
+
39
+ =end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'activerecord-postgres-hstore'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activerecord-postgres-hstore
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Juan Maiz
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-08 00:00:00 -03:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 9
31
+ version: 1.2.9
32
+ type: :development
33
+ version_requirements: *id001
34
+ description: This gem adds support for the postgres hstore type. It is the _just right_ alternative for storing hashes instead of using seralization or dynamic tables.
35
+ email: juanmaiz@gmail.com
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - LICENSE
42
+ - README.textile
43
+ files:
44
+ - .document
45
+ - .gitignore
46
+ - LICENSE
47
+ - Rakefile
48
+ - VERSION
49
+ - lib/activerecord-postgres-hstore.rb
50
+ - lib/activerecord-postgres-hstore/activerecord.rb
51
+ - lib/activerecord-postgres-hstore/hash.rb
52
+ - lib/activerecord-postgres-hstore/hstore.rb
53
+ - lib/activerecord-postgres-hstore/string.rb
54
+ - lib/templates/setup_hstore.rb
55
+ - spec/activerecord-postgres-hstore_spec.rb
56
+ - README.textile
57
+ has_rdoc: true
58
+ homepage: http://github.com/softa/activerecord-postgres-hstore
59
+ licenses: []
60
+
61
+ post_install_message:
62
+ rdoc_options:
63
+ - --charset=UTF-8
64
+ require_paths:
65
+ - lib
66
+ required_ruby_version: !ruby/object:Gem::Requirement
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ segments:
78
+ - 0
79
+ version: "0"
80
+ requirements: []
81
+
82
+ rubyforge_project:
83
+ rubygems_version: 1.3.6
84
+ signing_key:
85
+ specification_version: 3
86
+ summary: Goodbye serialize, hello hstore
87
+ test_files:
88
+ - spec/spec_helper.rb
89
+ - spec/activerecord-postgres-hstore_spec.rb