activerecord2-hstore 1.0.0
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/.gitignore +4 -0
- data/.rspec +2 -0
- data/Gemfile +7 -0
- data/README.markdown +162 -0
- data/Rakefile +4 -0
- data/activerecord2-hstore.gemspec +26 -0
- data/lib/activerecord2-hstore/hash.rb +24 -0
- data/lib/activerecord2-hstore/hstore.rb +78 -0
- data/lib/activerecord2-hstore/string.rb +24 -0
- data/lib/activerecord2-hstore/version.rb +5 -0
- data/lib/activerecord2-hstore.rb +12 -0
- data/spec/hash_spec.rb +33 -0
- data/spec/hstore_spec.rb +179 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/string_spec.rb +23 -0
- metadata +144 -0
data/.gitignore
ADDED
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.markdown
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
activerecord2-hstore
|
2
|
+
====================
|
3
|
+
|
4
|
+
What is this?
|
5
|
+
-------------
|
6
|
+
This is a gem that provides some very basic functionality for working with
|
7
|
+
Postgresql's Hstore columns in ActiveRecord 2.
|
8
|
+
|
9
|
+
Documentation about the Postgresql Hstore feature can be found
|
10
|
+
**[here](http://www.postgresql.org/docs/9.1/static/hstore.html)**.
|
11
|
+
|
12
|
+
Requirements (aka boring stuff)
|
13
|
+
-------------------------------
|
14
|
+
This gem requires:
|
15
|
+
|
16
|
+
* ActiveRecord and ActiveSupport 2.3.x
|
17
|
+
* pg gem
|
18
|
+
* rspec (for running tests, duh)
|
19
|
+
* Ruby (Tested on 1.8.7 MRI, 1.9.2, and REE)
|
20
|
+
* Postgresql (Tested on 9.1)
|
21
|
+
|
22
|
+
Setup (aka more boring stuff)
|
23
|
+
-----------------------------
|
24
|
+
Install the gem like you would normally install any gem.
|
25
|
+
|
26
|
+
Enable the hstore extension on the postgres database(s) you wish to use.
|
27
|
+
Ether by creating a migration in your project that runs this SQL statement
|
28
|
+
or manually running it directly in psql console...
|
29
|
+
|
30
|
+
CREATE EXTENSION IF NOT EXISTS hstore;
|
31
|
+
|
32
|
+
Instead of me trying to hack ActiveRecord to add an actual hstore column type,
|
33
|
+
and risk breaking the universe, just manually write a migration that adds a
|
34
|
+
hstore column to your table. Here, I'll even give you an example:
|
35
|
+
|
36
|
+
ALTER TABLE my_table ADD COLUMN some_field hstore;
|
37
|
+
|
38
|
+
I recommend you add an index to that column. Supported indexes are BTREE, HASH,
|
39
|
+
GIST, and GIN. I'll leave you to handle that on your own.
|
40
|
+
|
41
|
+
Usage (aka the stuff you care about)
|
42
|
+
------------------------------------
|
43
|
+
Now that the boring stuff is out of the way, you need to tell your model that
|
44
|
+
you have a hstore column.
|
45
|
+
|
46
|
+
Assume you have the following table:
|
47
|
+
|
48
|
+
peeps
|
49
|
+
id: integer
|
50
|
+
name: string
|
51
|
+
infos: hstore
|
52
|
+
|
53
|
+
You can tell your model infos is a hstore column thusly...
|
54
|
+
|
55
|
+
class Peep < ActiveRecord::Base
|
56
|
+
hstore_column :infos
|
57
|
+
end
|
58
|
+
|
59
|
+
What does that one line get you?
|
60
|
+
|
61
|
+
* A getter that returns the hstore column as a hash...
|
62
|
+
|
63
|
+
@peep.infos
|
64
|
+
>> {"age" => "25", "haircolor" => "black", "height" => "5'3\"", "likes" => "Cuddling while watching TV"}
|
65
|
+
|
66
|
+
* A setter that takes a hash and converts it to a hstore string (or the method can just take a hstore string)
|
67
|
+
|
68
|
+
@peeps.infos = some_hash
|
69
|
+
|
70
|
+
* These name scopes:
|
71
|
+
* infos\_has\_key (takes a string)
|
72
|
+
* infos\_has\_all\_keys (takes a string or an array of strings)
|
73
|
+
* infos\_has\_any\_keys (takes a string or an array of strings)
|
74
|
+
|
75
|
+
So what about querying the data in that colum? Well, you can always use the
|
76
|
+
standard condisions key in ActiveRecord's find method and use the proper
|
77
|
+
syntax from the Postgresql documentation. But if you're like me and like
|
78
|
+
using searchlogic, that's not an option. So in the same line in your model,
|
79
|
+
you can specifiy some keys you'd want to filter by...
|
80
|
+
|
81
|
+
class Peep < ActiveRecord::Base
|
82
|
+
hstore_column :infos, [:age, :haircolor, :likes]
|
83
|
+
end
|
84
|
+
|
85
|
+
Passing in an array of hstore keys will give you the following named scopes
|
86
|
+
to play with...
|
87
|
+
|
88
|
+
* infos\_age\_eq
|
89
|
+
* infos\_age\_neq
|
90
|
+
* infos\_age\_eq\_any
|
91
|
+
* infos\_age\_neq\_any
|
92
|
+
* infos\_age\_like
|
93
|
+
* infos\_age\_beigns\_with
|
94
|
+
* infos\_age\_ends\_with
|
95
|
+
* (Repeat list for "haircolor" and "likes")
|
96
|
+
|
97
|
+
Which means you can then do...
|
98
|
+
|
99
|
+
Peep.infos_likes_eq("Cuddling while watching TV")
|
100
|
+
Peep.searchlogic(:infos_age_neq => "23")
|
101
|
+
|
102
|
+
But Wait, There's More!
|
103
|
+
-----------------------
|
104
|
+
|
105
|
+
The gem also adds a helper method to the Hash and String objects for converting
|
106
|
+
hashes to hstore strings and back again.
|
107
|
+
|
108
|
+
These methods were originally implemented in a gem by
|
109
|
+
[softa](https://github.com/softa/activerecord-postgres-hstore) to add hstore
|
110
|
+
to ActiveReocord 3 and tweaked slightly for this gem.
|
111
|
+
|
112
|
+
Converting a hash into a hstore string that can be used to direclty store data
|
113
|
+
in a query...
|
114
|
+
|
115
|
+
{"something something" => "something", :dark => "side"}.to_hstore
|
116
|
+
>> "\"something something\"=>something,dark=>side"
|
117
|
+
|
118
|
+
Converting a hstore string that is returned from the database into a hash so
|
119
|
+
you can actually do something with it...
|
120
|
+
|
121
|
+
"\"something something\"=>something,dark=>side".from_hstore
|
122
|
+
>> {"something something" => "something", "dark" => "side"}
|
123
|
+
|
124
|
+
Note that taking a string from hstore into a hash will produce a hash where its
|
125
|
+
keys and values are all strings.
|
126
|
+
|
127
|
+
|
128
|
+
Running Tests
|
129
|
+
-------------
|
130
|
+
For the tests to run, it's assumed there is a Postgres database called
|
131
|
+
activerecord2\_hstore\_test. The specs will create a test table and populate
|
132
|
+
it with data for you. If you want to use a different database, then edit
|
133
|
+
spec/hstore\_spec.rb to your liking.
|
134
|
+
|
135
|
+
Then just run...
|
136
|
+
|
137
|
+
rake spec
|
138
|
+
|
139
|
+
Background / Why make this? / Me Rambling
|
140
|
+
-----------------------------------------
|
141
|
+
At my current employor, I'm helping to support a rather large Rails 2.3 app
|
142
|
+
(that unfortunatly will be stuck in 2.3 for quite some time) that runs on
|
143
|
+
Postgresql. The app's primary purpose is reporting on data from its data
|
144
|
+
warehouse of well... data. Because it's on Postgresql, the development team
|
145
|
+
was interested in using some of Postgresql's special features such as array
|
146
|
+
columns and hstore datatypes. We need the ability to easily take a hash
|
147
|
+
and store that as a hstore and read the column out as a hash. Also because
|
148
|
+
we're big on reporting and use searchlogic for filtering out data, we need
|
149
|
+
some way to search a hstore field for key with certain values.
|
150
|
+
|
151
|
+
To accomplish the first goal, I first needed a way to convert a Postgresql
|
152
|
+
hstore string into a Ruby hash and back to a string. I'm using the hash and
|
153
|
+
string methods from **[softa's gem](https://github.com/softa/activerecord-postgres-hstore)**
|
154
|
+
that provides hstore support for ActiveRecord 3. With those methods, I created
|
155
|
+
a way for you to tell ActiveRecord what columns are hstore columns and this
|
156
|
+
gem will override the default column getter method to return a hash and the
|
157
|
+
setter method to accept a hash which it then converts into a hstore string.
|
158
|
+
|
159
|
+
Part two of this gem is determining a way to query a hstore field. I decided
|
160
|
+
to have the gem generate searchlogic-like named scopes by sepcifying in the
|
161
|
+
model what keys in the hstore column you'll want to filter on. The gem will
|
162
|
+
create scopes in the style of "mycolumn\_key\_eq", "mycolumn\_key\_like", etc.
|
data/Rakefile
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "activerecord2-hstore/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "activerecord2-hstore"
|
7
|
+
s.version = Activerecord2::Hstore::VERSION
|
8
|
+
s.authors = ["Tony Drake"]
|
9
|
+
s.email = ["t27duck@gmail.com"]
|
10
|
+
s.homepage = "https://github.com/t27duck/activerecord2-hstore"
|
11
|
+
s.summary = %q{Some basic support to help integrate Postgresql's HStore into a Rails 2.3 app}
|
12
|
+
s.description = %q{Allows you to mark a column in a model as a HStore column letting you pass in a hash for its setter and retrieve a hash from its getter method. Also provides a series to named scopes for easy querying of data.}
|
13
|
+
|
14
|
+
s.rubyforge_project = "activerecord2-hstore"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
|
21
|
+
# specify any dependencies here; for example:
|
22
|
+
s.add_development_dependency "rspec", ">=2.0.0"
|
23
|
+
s.add_runtime_dependency "activerecord", "~> 2.3.0"
|
24
|
+
s.add_runtime_dependency "activesupport", "~> 2.3.0"
|
25
|
+
s.add_runtime_dependency "pg"
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Hash
|
2
|
+
# Generates an hstore string format. This is the format used to insert or update stuff in the database.
|
3
|
+
#
|
4
|
+
# Original implementation from:
|
5
|
+
# https://github.com/softa/activerecord-postgres-hstore/blob/master/lib/activerecord-postgres-hstore/hash.rb
|
6
|
+
def to_hstore
|
7
|
+
return "" if empty?
|
8
|
+
|
9
|
+
map { |key, value|
|
10
|
+
pair = [key, value].map { |element|
|
11
|
+
item = element.to_s.gsub(/"/, '\"')
|
12
|
+
if element.nil?
|
13
|
+
'NULL'
|
14
|
+
elsif item =~ /[,\s=>]/ || item.blank?
|
15
|
+
'"%s"' % item
|
16
|
+
else
|
17
|
+
item
|
18
|
+
end
|
19
|
+
}
|
20
|
+
|
21
|
+
"%s=>%s" % pair
|
22
|
+
} * ","
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Hstore
|
2
|
+
module ActiveRecord
|
3
|
+
# Called when included in ActiveRecord. Extends the Extensions module.
|
4
|
+
def self.included(base)
|
5
|
+
base.extend Extensions
|
6
|
+
end
|
7
|
+
|
8
|
+
module Extensions
|
9
|
+
# Creates a series of methods and named scopes for a hstore column. This is the primary
|
10
|
+
# method you should use to invoke this gem in a model.
|
11
|
+
#
|
12
|
+
# USAGE IN A MODEL:
|
13
|
+
# class Foo < ActiveRecord::Base
|
14
|
+
# hstore_column :some_column, [:array_of_possible_keys, :you_may_want, :to_query_on]
|
15
|
+
# end
|
16
|
+
def hstore_column(column, keys=[])
|
17
|
+
create_getter_and_setter(column)
|
18
|
+
create_hstore_key_availability_scopes(column)
|
19
|
+
Array(keys).each{ |key| create_hstore_key_search_scopes(column, key) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Creates and overrides ActiveRecord's getter and setter methods for the hstore column.
|
23
|
+
# The getter returns a hash. The setter accepts and converts either a hash or a valid
|
24
|
+
# hstore string.
|
25
|
+
def create_getter_and_setter(column)
|
26
|
+
define_method column.to_sym do
|
27
|
+
read_attribute(column.to_sym).to_s.from_hstore
|
28
|
+
end
|
29
|
+
|
30
|
+
define_method "#{column}=".to_sym do |value|
|
31
|
+
value = value.is_a?(String) ? value : value.to_hstore
|
32
|
+
write_attribute(column.to_sym, value)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Creates named scopes for the hstore column allowing you to determine if the column
|
37
|
+
# contains a given key or set of keys.
|
38
|
+
def create_hstore_key_availability_scopes(column)
|
39
|
+
named_scope "#{column}_has_key".to_sym, Proc.new{ |key|
|
40
|
+
{ :conditions => ["#{column} ? :key", {:key => key}] }
|
41
|
+
}
|
42
|
+
named_scope "#{column}_has_all_keys".to_sym, Proc.new{ |keys|
|
43
|
+
{ :conditions => ["#{column} ?& ARRAY[:keys]", {:keys => Array(keys)}] }
|
44
|
+
}
|
45
|
+
named_scope "#{column}_has_any_key".to_sym, Proc.new{ |keys|
|
46
|
+
{ :conditions => ["#{column} ?| ARRAY[:keys]", {:keys => Array(keys)}] }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
# Creates a slew of searchlogic-like named scopes to query for a key on a hstore column
|
51
|
+
def create_hstore_key_search_scopes(column, key)
|
52
|
+
named_scope "#{column}_#{key}_eq".to_sym, Proc.new{ |value|
|
53
|
+
{ :conditions => ["#{column} -> '#{key}' = ?", value.to_s] }
|
54
|
+
}
|
55
|
+
named_scope "#{column}_#{key}_neq".to_sym, Proc.new{ |value|
|
56
|
+
{ :conditions => ["#{column} -> '#{key}' != ?", value.to_s] }
|
57
|
+
}
|
58
|
+
named_scope "#{column}_#{key}_eq_any".to_sym, Proc.new{ |value|
|
59
|
+
{ :conditions => ["#{column} -> '#{key}' IN(?)", value.map{|v| v.to_s} ] }
|
60
|
+
}
|
61
|
+
named_scope "#{column}_#{key}_neq_any".to_sym, Proc.new{ |value|
|
62
|
+
{ :conditions => ["#{column} -> '#{key}' NOT IN(?)", value.map{|v| v.to_s} ] }
|
63
|
+
}
|
64
|
+
named_scope "#{column}_#{key}_like".to_sym, Proc.new{ |value|
|
65
|
+
{ :conditions => ["#{column} -> '#{key}' ILIKE(?)", "%#{value.to_s}%"] }
|
66
|
+
}
|
67
|
+
named_scope "#{column}_#{key}_begins_with".to_sym, Proc.new{ |value|
|
68
|
+
{ :conditions => ["#{column} -> '#{key}' ILIKE(?)", "#{value.to_s}%"] }
|
69
|
+
}
|
70
|
+
named_scope "#{column}_#{key}_ends_with".to_sym, Proc.new{ |value|
|
71
|
+
{ :conditions => ["#{column} -> '#{key}' ILIKE(?)", "%#{value.to_s}"] }
|
72
|
+
}
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class String
|
2
|
+
# Generates a hash from an hstore string format.
|
3
|
+
#
|
4
|
+
# Original implementation from:
|
5
|
+
# https://github.com/softa/activerecord-postgres-hstore/blob/master/lib/activerecord-postgres-hstore/string.rb
|
6
|
+
def from_hstore
|
7
|
+
quoted_string = /"[^"\\]*(?:\\.[^"\\]*)*"/
|
8
|
+
unquoted_string = /[^\s=,][^\s=,\\]*(?:\\.[^\s=,\\]*|=[^,>])*/
|
9
|
+
string = /(#{quoted_string}|#{unquoted_string})/
|
10
|
+
hstore_pair = /#{string}\s*=>\s*#{string}/
|
11
|
+
|
12
|
+
token_pairs = (scan(hstore_pair)).map { |key, value| [key, value =~ /^NULL$/i ? nil : value] }
|
13
|
+
token_pairs = token_pairs.map { |key, value|
|
14
|
+
[key, value].map { |element|
|
15
|
+
case element
|
16
|
+
when nil then element
|
17
|
+
when /^"(.*)"$/ then $1.gsub(/\\(.)/, '\1')
|
18
|
+
else element.gsub(/\\(.)/, '\1')
|
19
|
+
end
|
20
|
+
}
|
21
|
+
}
|
22
|
+
Hash[ token_pairs ]
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
require "activerecord2-hstore/version"
|
2
|
+
require "activerecord2-hstore/string"
|
3
|
+
require "activerecord2-hstore/hash"
|
4
|
+
require "activerecord2-hstore/hstore"
|
5
|
+
|
6
|
+
module Activerecord2
|
7
|
+
module Hstore
|
8
|
+
# Your code goes here...
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
ActiveRecord::Base.send(:include, Hstore::ActiveRecord)
|
data/spec/hash_spec.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Hash do
|
4
|
+
describe "Converting a hash to a hstore string" do
|
5
|
+
it "#to_hstore" do
|
6
|
+
{:a=>"b"}.to_hstore.should == "a=>b"
|
7
|
+
{"a"=>"b"}.to_hstore.should == "a=>b"
|
8
|
+
{:a=>:b}.to_hstore.should == "a=>b"
|
9
|
+
{"a"=>"2"}.to_hstore.should == "a=>2"
|
10
|
+
{:a=>2}.to_hstore.should == "a=>2"
|
11
|
+
{2=>2}.to_hstore.should == "2=>2"
|
12
|
+
{"a b"=>"b"}.to_hstore.should == "\"a b\"=>b"
|
13
|
+
{"a b"=>"b c"}.to_hstore.should == "\"a b\"=>\"b c\""
|
14
|
+
{"a"=>"b c"}.to_hstore.should == "a=>\"b c\""
|
15
|
+
end
|
16
|
+
|
17
|
+
it "converts a multiball hash to a hstore string" do
|
18
|
+
all_should_match({"a"=>"b", "c"=>"d"}, [/a=>b/, /c=>d/])
|
19
|
+
all_should_match({:a=>"b", "c"=>:d}, [/a=>b/, /c=>d/])
|
20
|
+
all_should_match({"a 1"=>"b 1", "c"=>"d"}, [/\"a 1\"=>\"b 1\"/, /c=>d/])
|
21
|
+
all_should_match({"a"=>"b", "c"=>"d 1"}, [/a=>b/, /c=>\"d 1\"/])
|
22
|
+
all_should_match({"a"=>"b", "c 1"=>"d"}, [/a=>b/, /\"c 1\"=>d/])
|
23
|
+
all_should_match({:a=>:b, "c 1"=>:"d 1"}, [/a=>b/, /\"c 1\"=>\"d 1\"/])
|
24
|
+
all_should_match({"a"=>"b", :c=>:d, "e 1"=>"f 1"}, [/a=>b/, /c=>d/, /\"e 1\"=>\"f 1\"/])
|
25
|
+
end
|
26
|
+
|
27
|
+
def all_should_match(hash, expects)
|
28
|
+
expects.each do |e|
|
29
|
+
hash.to_hstore.should match(e)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/spec/hstore_spec.rb
ADDED
@@ -0,0 +1,179 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
ActiveRecord::Base.establish_connection(
|
4
|
+
:adapter => "postgresql",
|
5
|
+
:database => "activerecord2_hstore_test",
|
6
|
+
:encoding => "unicode"
|
7
|
+
)
|
8
|
+
|
9
|
+
ActiveRecord::Base.connection.execute("DROP TABLE IF EXISTS my_hstores")
|
10
|
+
ActiveRecord::Base.connection.create_table(:my_hstores) do |t|
|
11
|
+
t.string :label
|
12
|
+
end
|
13
|
+
ActiveRecord::Base.connection.execute("CREATE EXTENSION IF NOT EXISTS hstore")
|
14
|
+
ActiveRecord::Base.connection.execute("ALTER TABLE my_hstores ADD COLUMN some_field hstore")
|
15
|
+
|
16
|
+
class MyHstore < ActiveRecord::Base
|
17
|
+
hstore_column :some_field, [:key]
|
18
|
+
end
|
19
|
+
|
20
|
+
describe Hstore do
|
21
|
+
before(:each) do
|
22
|
+
ActiveRecord::Base.connection.increment_open_transactions
|
23
|
+
ActiveRecord::Base.connection.begin_db_transaction
|
24
|
+
end
|
25
|
+
|
26
|
+
after(:each) do
|
27
|
+
ActiveRecord::Base.connection.rollback_db_transaction
|
28
|
+
ActiveRecord::Base.connection.decrement_open_transactions
|
29
|
+
end
|
30
|
+
|
31
|
+
it "creates the correct named scopes" do
|
32
|
+
%w(eq neq eq_any neq_any like begins_with ends_with).each do |predicate|
|
33
|
+
MyHstore.should respond_to("some_field_key_#{predicate}")
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe "available methods" do
|
38
|
+
let(:subject) do
|
39
|
+
MyHstore.new
|
40
|
+
end
|
41
|
+
|
42
|
+
it "creates a getter and setter for the some_field" do
|
43
|
+
subject.should respond_to(:some_field)
|
44
|
+
subject.should respond_to(:some_field=)
|
45
|
+
end
|
46
|
+
|
47
|
+
it "uses ActiveRecord's read_attribute for the getter method" do
|
48
|
+
subject.should_receive(:read_attribute).with(:some_field).and_return("a=>b")
|
49
|
+
subject.some_field
|
50
|
+
end
|
51
|
+
|
52
|
+
describe "setter method" do
|
53
|
+
it "uses ActiveRecord's write_attribute" do
|
54
|
+
subject.should_receive(:write_attribute)
|
55
|
+
subject.some_field = {:a => :b}
|
56
|
+
end
|
57
|
+
|
58
|
+
it "can also accept a hstore string instead of a hash" do
|
59
|
+
subject.stub(:write_attribute)
|
60
|
+
subject.some_field = "a=>b"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe "querying data" do
|
66
|
+
before(:each) do
|
67
|
+
MyHstore.create( :label=> "A", :some_field => {"key" => "1"}.to_hstore )
|
68
|
+
MyHstore.create( :label=> "B", :some_field => {:key => 1, "a" => "b"}.to_hstore )
|
69
|
+
MyHstore.create( :label=> "C", :some_field => {"key" => "2"}.to_hstore )
|
70
|
+
MyHstore.create( :label=> "D", :some_field => {"key" => "3"}.to_hstore )
|
71
|
+
MyHstore.create( :label=> "E", :some_field => {"key" => "123456789"}.to_hstore )
|
72
|
+
MyHstore.create( :label=> "F", :some_field => {"c" => "x"}.to_hstore )
|
73
|
+
end
|
74
|
+
|
75
|
+
it "queries for the records containing a specific key" do
|
76
|
+
result = MyHstore.some_field_has_key("a")
|
77
|
+
result.map(&:label).should include "B"
|
78
|
+
%w(A C D E F).each do |key|
|
79
|
+
result.map(&:label).should_not include key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
it "queries for the records containing a set keys" do
|
84
|
+
result = MyHstore.some_field_has_all_keys(["a", "key"])
|
85
|
+
result.map(&:label).should include "B"
|
86
|
+
%w(A C D E F).each do |key|
|
87
|
+
result.map(&:label).should_not include key
|
88
|
+
end
|
89
|
+
|
90
|
+
result = MyHstore.some_field_has_all_keys("a")
|
91
|
+
result.map(&:label).should include "B"
|
92
|
+
%w(A C D E F).each do |key|
|
93
|
+
result.map(&:label).should_not include key
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
it "queries for the records containing any set keys" do
|
98
|
+
result = MyHstore.some_field_has_any_key(["a", "c"])
|
99
|
+
%w(B F).each do |key|
|
100
|
+
result.map(&:label).should include key
|
101
|
+
end
|
102
|
+
%w(A C D E).each do |key|
|
103
|
+
result.map(&:label).should_not include key
|
104
|
+
end
|
105
|
+
|
106
|
+
result = MyHstore.some_field_has_any_key("a")
|
107
|
+
result.map(&:label).should include "B"
|
108
|
+
%w(A C D E F).each do |key|
|
109
|
+
result.map(&:label).should_not include key
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
it "queries the right data for the named scope *_eq" do
|
114
|
+
result = MyHstore.some_field_key_eq("1")
|
115
|
+
%w(A B).each do |key|
|
116
|
+
result.map(&:label).should include key
|
117
|
+
end
|
118
|
+
%w(C D E F).each do |key|
|
119
|
+
result.map(&:label).should_not include key
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
it "queries the right data for the named scope *_neq" do
|
124
|
+
result = MyHstore.some_field_key_neq("1")
|
125
|
+
%w(A B F).each do |key|
|
126
|
+
result.map(&:label).should_not include key
|
127
|
+
end
|
128
|
+
%w(C D E).each do |key|
|
129
|
+
result.map(&:label).should include key
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
it "queries the right data for the named scope *_eq_any" do
|
134
|
+
result = MyHstore.some_field_key_eq_any(["2", "3"])
|
135
|
+
%w(A B E F).each do |key|
|
136
|
+
result.map(&:label).should_not include key
|
137
|
+
end
|
138
|
+
%w(C D).each do |key|
|
139
|
+
result.map(&:label).should include key
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
it "queries the right data for the named scope *_neq_any" do
|
144
|
+
result = MyHstore.some_field_key_neq_any(["2", "3"])
|
145
|
+
%w(A B E).each do |key|
|
146
|
+
result.map(&:label).should include key
|
147
|
+
end
|
148
|
+
%w(C D F).each do |key|
|
149
|
+
result.map(&:label).should_not include key
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
it "queries the right data for the named scope *_like" do
|
154
|
+
result = MyHstore.some_field_key_like("456")
|
155
|
+
result.map(&:label).should include "E"
|
156
|
+
%w(A B C D F).each do |key|
|
157
|
+
result.map(&:label).should_not include key
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
it "queries the right data for the named scope *_begins_with" do
|
162
|
+
result = MyHstore.some_field_key_begins_with("123")
|
163
|
+
result.map(&:label).should include "E"
|
164
|
+
%w(A B C D F).each do |key|
|
165
|
+
result.map(&:label).should_not include key
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
it "queries the right data for the named scope *_ends_with" do
|
170
|
+
result = MyHstore.some_field_key_ends_with("789")
|
171
|
+
result.map(&:label).should include "E"
|
172
|
+
%w(A B C D F).each do |key|
|
173
|
+
result.map(&:label).should_not include key
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
data/spec/spec_helper.rb
ADDED
data/spec/string_spec.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe String do
|
4
|
+
describe "#from_hstore" do
|
5
|
+
it "handles a single key value pair" do
|
6
|
+
"a=>b".from_hstore.should == {"a" => "b"}
|
7
|
+
"\"a\"=>\"b\"".from_hstore.should == {"a" => "b"}
|
8
|
+
"\"a 1\"=>b".from_hstore.should == {"a 1" => "b"}
|
9
|
+
"\"a 1\"=>\"b 1\"".from_hstore.should == {"a 1" => "b 1"}
|
10
|
+
"1=>b".from_hstore.should == {"1" => "b"}
|
11
|
+
"1=>1".from_hstore.should == {"1" => "1"}
|
12
|
+
end
|
13
|
+
|
14
|
+
it "converts a multikeyed hstore string to a hash" do
|
15
|
+
"a=>b,c=>d".from_hstore.should == {"a" => "b", "c" => "d"}
|
16
|
+
"\"a\"=>\"b\",\"c\"=>\"d\"".from_hstore.should == {"a" => "b", "c" => "d"}
|
17
|
+
"\"a 1\"=>b,c=>d".from_hstore.should == {"a 1" => "b", "c" => "d"}
|
18
|
+
"\"a 1\"=>b,c=>\"d 1\"".from_hstore.should == {"a 1" => "b", "c" => "d 1"}
|
19
|
+
"\"a 1\"=>\"b 1\",c=>\"d 1\"".from_hstore.should == {"a 1" => "b 1", "c" => "d 1"}
|
20
|
+
"1=>b,c=>\"d 1\"".from_hstore.should == {"1" => "b", "c" => "d 1"}
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
metadata
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: activerecord2-hstore
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 23
|
5
|
+
prerelease:
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 0
|
10
|
+
version: 1.0.0
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Tony Drake
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2012-02-16 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 15
|
29
|
+
segments:
|
30
|
+
- 2
|
31
|
+
- 0
|
32
|
+
- 0
|
33
|
+
version: 2.0.0
|
34
|
+
type: :development
|
35
|
+
version_requirements: *id001
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: activerecord
|
38
|
+
prerelease: false
|
39
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
40
|
+
none: false
|
41
|
+
requirements:
|
42
|
+
- - ~>
|
43
|
+
- !ruby/object:Gem::Version
|
44
|
+
hash: 3
|
45
|
+
segments:
|
46
|
+
- 2
|
47
|
+
- 3
|
48
|
+
- 0
|
49
|
+
version: 2.3.0
|
50
|
+
type: :runtime
|
51
|
+
version_requirements: *id002
|
52
|
+
- !ruby/object:Gem::Dependency
|
53
|
+
name: activesupport
|
54
|
+
prerelease: false
|
55
|
+
requirement: &id003 !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ~>
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
hash: 3
|
61
|
+
segments:
|
62
|
+
- 2
|
63
|
+
- 3
|
64
|
+
- 0
|
65
|
+
version: 2.3.0
|
66
|
+
type: :runtime
|
67
|
+
version_requirements: *id003
|
68
|
+
- !ruby/object:Gem::Dependency
|
69
|
+
name: pg
|
70
|
+
prerelease: false
|
71
|
+
requirement: &id004 !ruby/object:Gem::Requirement
|
72
|
+
none: false
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
hash: 3
|
77
|
+
segments:
|
78
|
+
- 0
|
79
|
+
version: "0"
|
80
|
+
type: :runtime
|
81
|
+
version_requirements: *id004
|
82
|
+
description: Allows you to mark a column in a model as a HStore column letting you pass in a hash for its setter and retrieve a hash from its getter method. Also provides a series to named scopes for easy querying of data.
|
83
|
+
email:
|
84
|
+
- t27duck@gmail.com
|
85
|
+
executables: []
|
86
|
+
|
87
|
+
extensions: []
|
88
|
+
|
89
|
+
extra_rdoc_files: []
|
90
|
+
|
91
|
+
files:
|
92
|
+
- .gitignore
|
93
|
+
- .rspec
|
94
|
+
- Gemfile
|
95
|
+
- README.markdown
|
96
|
+
- Rakefile
|
97
|
+
- activerecord2-hstore.gemspec
|
98
|
+
- lib/activerecord2-hstore.rb
|
99
|
+
- lib/activerecord2-hstore/hash.rb
|
100
|
+
- lib/activerecord2-hstore/hstore.rb
|
101
|
+
- lib/activerecord2-hstore/string.rb
|
102
|
+
- lib/activerecord2-hstore/version.rb
|
103
|
+
- spec/hash_spec.rb
|
104
|
+
- spec/hstore_spec.rb
|
105
|
+
- spec/spec_helper.rb
|
106
|
+
- spec/string_spec.rb
|
107
|
+
homepage: https://github.com/t27duck/activerecord2-hstore
|
108
|
+
licenses: []
|
109
|
+
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
|
113
|
+
require_paths:
|
114
|
+
- lib
|
115
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
116
|
+
none: false
|
117
|
+
requirements:
|
118
|
+
- - ">="
|
119
|
+
- !ruby/object:Gem::Version
|
120
|
+
hash: 3
|
121
|
+
segments:
|
122
|
+
- 0
|
123
|
+
version: "0"
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
none: false
|
126
|
+
requirements:
|
127
|
+
- - ">="
|
128
|
+
- !ruby/object:Gem::Version
|
129
|
+
hash: 3
|
130
|
+
segments:
|
131
|
+
- 0
|
132
|
+
version: "0"
|
133
|
+
requirements: []
|
134
|
+
|
135
|
+
rubyforge_project: activerecord2-hstore
|
136
|
+
rubygems_version: 1.8.10
|
137
|
+
signing_key:
|
138
|
+
specification_version: 3
|
139
|
+
summary: Some basic support to help integrate Postgresql's HStore into a Rails 2.3 app
|
140
|
+
test_files:
|
141
|
+
- spec/hash_spec.rb
|
142
|
+
- spec/hstore_spec.rb
|
143
|
+
- spec/spec_helper.rb
|
144
|
+
- spec/string_spec.rb
|