├── .gitignore ├── Appraisals ├── Gemfile ├── MIT-LICENSE ├── README.md ├── Rakefile ├── gemfiles ├── activerecord_3.gemfile ├── activerecord_3.gemfile.lock ├── activerecord_4.gemfile └── activerecord_4.gemfile.lock ├── lib ├── active_record │ └── coders │ │ └── nested_hstore.rb ├── nested-hstore.rb └── nested_hstore │ ├── serializer.rb │ └── version.rb ├── nested-hstore.gemspec └── spec ├── db ├── connection.yml └── create_posts.rb ├── nested_hstore ├── active_record │ └── coders │ │ └── nested_hstore_spec.rb └── serializer_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .bundle/ 3 | Gemfile.lock 4 | *.gem 5 | log/*.log 6 | pkg/ 7 | -------------------------------------------------------------------------------- /Appraisals: -------------------------------------------------------------------------------- 1 | appraise "activerecord-3" do 2 | gem "activerecord", "3.2.17" 3 | gem "activerecord-postgres-hstore" 4 | end 5 | 6 | appraise "activerecord-4" do 7 | gem "activerecord", "4.1.0" 8 | end 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gemspec 4 | -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2013 Tom Benner 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Nested Hstore 2 | ============= 3 | Store nested hashes and other types in ActiveRecord hstores 4 | 5 | Overview 6 | -------- 7 | 8 | Postgres hstores offer a number of benefits, but they don't natively support multi-level data. Nested Hstore adds this support to ActiveRecord, letting you treat an hstore like a NoSQL-like document. 9 | 10 | [Hstore functions](http://www.postgresql.org/docs/9.1/static/hstore.html) are still supported at the root level. 11 | 12 | It also lets you store data types other than hashes in an hstore. All of the following values will be returned verbatim: 13 | 14 | ```ruby 15 | class User < ActiveRecord::Base 16 | serialize :my_property, ActiveRecord::Coders::NestedHstore 17 | end 18 | 19 | # Nested hash 20 | user.my_property = { 21 | 'name' => 'Jane Doe', 22 | 'comment_ids' => [34, 67, 82], 23 | 'location' => { 24 | 'id' => 15, 25 | 'city' => 'San Francisco', 26 | 'state' => 'CA' 27 | } 28 | } 29 | 30 | # Array 31 | user.my_property = [34, 67, 82] 32 | 33 | # Array of nested hashes 34 | user.my_property = [ 35 | { 36 | 'id' => 15, 37 | 'username' => 'janedoe' 38 | }, 39 | { 40 | 'id' => 16, 41 | 'username' => 'johndoe' 42 | } 43 | ] 44 | 45 | # Boolean 46 | user.my_property = true 47 | 48 | # Integer 49 | user.my_property = 43 50 | 51 | # Float 52 | user.my_property = 43.1 53 | 54 | # String 55 | user.my_property = 'janedoe' 56 | ``` 57 | 58 | Installation 59 | ------------ 60 | 61 | *If you're using ActiveRecord 2.x or 3.x, set up [activerecord-postgres-hstore](https://github.com/diogob/activerecord-postgres-hstore) if you haven't already. This isn't necessary for ActiveRecord 4.x.* 62 | 63 | Include it in your Gemfile: 64 | 65 | gem 'nested-hstore' 66 | 67 | Serialize each property using `ActiveRecord::Coders::NestedHstore`: 68 | 69 | ```ruby 70 | class User < ActiveRecord::Base 71 | serialize :my_property, ActiveRecord::Coders::NestedHstore 72 | end 73 | ``` 74 | 75 | Testing 76 | ------- 77 | 78 | Nested Hstore is tested against ActiveRecord 3 and 4. If you'd like to submit a PR, please be sure to use [Appraisal](https://github.com/thoughtbot/appraisal) to test your changes in both contexts: 79 | 80 | ```bash 81 | appraisal rspec 82 | ``` 83 | 84 | License 85 | ------- 86 | 87 | Nested Hstore is released under the MIT License. Please see the MIT-LICENSE file for details. 88 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require "rake/testtask" 4 | 5 | task :default => :test 6 | 7 | Rake::TestTask.new do |t| 8 | t.libs << "lib" 9 | t.libs << "test" 10 | t.test_files = FileList["test/**/*_test.rb"] 11 | t.verbose = true 12 | end 13 | -------------------------------------------------------------------------------- /gemfiles/activerecord_3.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "3.2.17" 6 | gem "activerecord-postgres-hstore" 7 | 8 | gemspec :path => "../" 9 | -------------------------------------------------------------------------------- /gemfiles/activerecord_3.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../ 3 | specs: 4 | nested-hstore (0.1.2) 5 | activerecord 6 | activesupport 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (3.2.17) 12 | activesupport (= 3.2.17) 13 | builder (~> 3.0.0) 14 | activerecord (3.2.17) 15 | activemodel (= 3.2.17) 16 | activesupport (= 3.2.17) 17 | arel (~> 3.0.2) 18 | tzinfo (~> 0.3.29) 19 | activerecord-postgres-hstore (0.7.7) 20 | activerecord (>= 3.1) 21 | pg-hstore (>= 1.1.5) 22 | rake 23 | activesupport (3.2.17) 24 | i18n (~> 0.6, >= 0.6.4) 25 | multi_json (~> 1.0) 26 | appraisal (1.0.2) 27 | bundler 28 | rake 29 | thor (>= 0.14.0) 30 | arel (3.0.3) 31 | builder (3.0.4) 32 | diff-lcs (1.2.5) 33 | i18n (0.7.0) 34 | multi_json (1.10.1) 35 | pg (0.17.1) 36 | pg-hstore (1.2.0) 37 | rake (10.4.2) 38 | rspec (3.1.0) 39 | rspec-core (~> 3.1.0) 40 | rspec-expectations (~> 3.1.0) 41 | rspec-mocks (~> 3.1.0) 42 | rspec-core (3.1.7) 43 | rspec-support (~> 3.1.0) 44 | rspec-expectations (3.1.2) 45 | diff-lcs (>= 1.2.0, < 2.0) 46 | rspec-support (~> 3.1.0) 47 | rspec-mocks (3.1.3) 48 | rspec-support (~> 3.1.0) 49 | rspec-support (3.1.2) 50 | thor (0.19.1) 51 | tzinfo (0.3.41) 52 | 53 | PLATFORMS 54 | ruby 55 | 56 | DEPENDENCIES 57 | activerecord (= 3.2.17) 58 | activerecord-postgres-hstore 59 | appraisal 60 | nested-hstore! 61 | pg 62 | rspec 63 | -------------------------------------------------------------------------------- /gemfiles/activerecord_4.gemfile: -------------------------------------------------------------------------------- 1 | # This file was generated by Appraisal 2 | 3 | source "https://rubygems.org" 4 | 5 | gem "activerecord", "4.1.0" 6 | 7 | gemspec :path => "../" 8 | -------------------------------------------------------------------------------- /gemfiles/activerecord_4.gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: ../ 3 | specs: 4 | nested-hstore (0.1.2) 5 | activerecord 6 | activesupport 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | activemodel (4.1.0) 12 | activesupport (= 4.1.0) 13 | builder (~> 3.1) 14 | activerecord (4.1.0) 15 | activemodel (= 4.1.0) 16 | activesupport (= 4.1.0) 17 | arel (~> 5.0.0) 18 | activesupport (4.1.0) 19 | i18n (~> 0.6, >= 0.6.9) 20 | json (~> 1.7, >= 1.7.7) 21 | minitest (~> 5.1) 22 | thread_safe (~> 0.1) 23 | tzinfo (~> 1.1) 24 | appraisal (1.0.2) 25 | bundler 26 | rake 27 | thor (>= 0.14.0) 28 | arel (5.0.1.20140414130214) 29 | builder (3.2.2) 30 | diff-lcs (1.2.5) 31 | i18n (0.7.0) 32 | json (1.8.1) 33 | minitest (5.5.0) 34 | pg (0.17.1) 35 | rake (10.4.2) 36 | rspec (3.1.0) 37 | rspec-core (~> 3.1.0) 38 | rspec-expectations (~> 3.1.0) 39 | rspec-mocks (~> 3.1.0) 40 | rspec-core (3.1.7) 41 | rspec-support (~> 3.1.0) 42 | rspec-expectations (3.1.2) 43 | diff-lcs (>= 1.2.0, < 2.0) 44 | rspec-support (~> 3.1.0) 45 | rspec-mocks (3.1.3) 46 | rspec-support (~> 3.1.0) 47 | rspec-support (3.1.2) 48 | thor (0.19.1) 49 | thread_safe (0.3.4) 50 | tzinfo (1.2.2) 51 | thread_safe (~> 0.1) 52 | 53 | PLATFORMS 54 | ruby 55 | 56 | DEPENDENCIES 57 | activerecord (= 4.1.0) 58 | appraisal 59 | nested-hstore! 60 | pg 61 | rspec 62 | -------------------------------------------------------------------------------- /lib/active_record/coders/nested_hstore.rb: -------------------------------------------------------------------------------- 1 | module ActiveRecord 2 | module Coders 3 | if ActiveRecord::VERSION::MAJOR < 4 4 | require 'activerecord-postgres-hstore' 5 | 6 | class NestedHstore < Hstore 7 | def initialize(default=nil) 8 | super(default) 9 | @nested_serializer = ::NestedHstore::Serializer.new 10 | end 11 | 12 | private 13 | 14 | def to_hstore obj 15 | super(@nested_serializer.serialize(obj)) 16 | end 17 | 18 | def from_hstore hstore 19 | @nested_serializer.deserialize(super) 20 | end 21 | end 22 | else 23 | class NestedHstore 24 | def self.load(hstore) 25 | new.load(hstore) 26 | end 27 | 28 | def self.dump(hstore) 29 | new.dump(hstore) 30 | end 31 | 32 | def initialize 33 | @nested_serializer = ::NestedHstore::Serializer.new 34 | end 35 | 36 | def load(hash) 37 | @nested_serializer.deserialize(hash) 38 | end 39 | 40 | def dump(value) 41 | @nested_serializer.serialize(value) 42 | end 43 | end 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /lib/nested-hstore.rb: -------------------------------------------------------------------------------- 1 | # Dependencies 2 | require 'active_record' 3 | require 'active_support/all' 4 | 5 | # Self 6 | require 'active_record/coders/nested_hstore' 7 | require 'nested_hstore/serializer' 8 | require 'nested_hstore/version' 9 | -------------------------------------------------------------------------------- /lib/nested_hstore/serializer.rb: -------------------------------------------------------------------------------- 1 | module NestedHstore 2 | class Serializer 3 | 4 | def initialize 5 | @type_key = '__TYPE__' 6 | @types_map = { 7 | array: '__ARRAY__', 8 | boolean: '__BOOLEAN__', 9 | float: '__FLOAT__', 10 | integer: '__INTEGER__', 11 | string: '__STRING__' 12 | } 13 | @types_map_inverted = @types_map.invert 14 | @value_key = '__VALUE__' 15 | end 16 | 17 | def serialize(value) 18 | return nil if value.nil? 19 | case value 20 | when Array 21 | type = :array 22 | hash = array_to_hash(value) 23 | when FalseClass, TrueClass 24 | type = :boolean 25 | hash = { @value_key => value } 26 | when Float 27 | type = :float 28 | hash = { @value_key => value } 29 | when Hash 30 | type = :hash 31 | hash = standardize_value(value) 32 | when Integer 33 | type = :integer 34 | hash = { @value_key => value } 35 | when String 36 | type = :string 37 | hash = { @value_key => value } 38 | else 39 | raise "Unsupported hstore type: #{value.class}" 40 | end 41 | hash_to_hstore(type, hash) 42 | end 43 | 44 | def deserialize(hash) 45 | return nil if hash.nil? 46 | raise 'Hstore value should be a hash' unless hash.is_a?(Hash) 47 | type_value = hash.delete(@type_key) 48 | type = @types_map_inverted[type_value] 49 | deserialized = case type 50 | when :array 51 | hash.values.map { |v| decode_json_if_json(v) } 52 | when :boolean 53 | hash[@value_key] == 'true' 54 | when :float 55 | hash[@value_key].to_f 56 | when :integer 57 | hash[@value_key].to_i 58 | when :string 59 | hash[@value_key] 60 | else 61 | hash.each do |k, v| 62 | hash[k] = decode_json_if_json(v) 63 | end 64 | hash 65 | end 66 | deserialized 67 | end 68 | 69 | private 70 | 71 | def hash_to_hstore(type, hash) 72 | return {} if type == :hash && hash.blank? 73 | 74 | hstore = hash.dup 75 | hstore.each do |k, v| 76 | if v.is_a?(Array) || v.is_a?(Hash) 77 | hstore[k] = encode_json(v) 78 | else 79 | hstore[k] = v.to_s 80 | end 81 | end 82 | 83 | if type != :hash 84 | hstore.merge!(@type_key => @types_map[type]) 85 | end 86 | 87 | hstore 88 | end 89 | 90 | def standardize_value(value) 91 | if value.is_a?(Array) 92 | value.map! do |v| 93 | standardize_value(v) 94 | end 95 | elsif value.is_a?(Hash) 96 | value.each do |k, v| 97 | value[k] = standardize_value(v) 98 | end 99 | # Standardize Times to an ISO string, as that's what DateTime#to_s evaluates to 100 | elsif value.is_a?(Time) || value.is_a?(ActiveSupport::TimeWithZone) 101 | value = value.iso8601 102 | end 103 | value 104 | end 105 | 106 | def array_to_hash(array) 107 | hash = {} 108 | array.each_with_index do |value, index| 109 | hash[index.to_s] = standardize_value(value) 110 | end 111 | hash 112 | end 113 | 114 | # This isn't ideal: how do we know whether each value in an hstore is JSON or a 115 | # string/integer/etc? 116 | def decode_json_if_json(value) 117 | is_json = (value.start_with?('{') && value.end_with?('}')) || 118 | (value.start_with?('[') && value.end_with?(']')) 119 | return value unless is_json 120 | ActiveSupport::JSON.decode(value) 121 | rescue 122 | value 123 | end 124 | 125 | def encode_json(value) 126 | ActiveSupport::JSON.encode(value) 127 | end 128 | end 129 | end 130 | -------------------------------------------------------------------------------- /lib/nested_hstore/version.rb: -------------------------------------------------------------------------------- 1 | module NestedHstore 2 | VERSION = '0.1.2' 3 | end 4 | -------------------------------------------------------------------------------- /nested-hstore.gemspec: -------------------------------------------------------------------------------- 1 | require File.expand_path('../lib/nested_hstore/version', __FILE__) 2 | 3 | Gem::Specification.new do |s| 4 | s.authors = ['Tom Benner'] 5 | s.email = ['tombenner@gmail.com'] 6 | s.description = s.summary = %q{Store nested hashes and other types in ActiveRecord hstores} 7 | s.homepage = 'https://github.com/tombenner/nested-hstore' 8 | 9 | s.files = Dir['{lib}/**/*'] + ['MIT-LICENSE', 'Rakefile', 'README.md'] 10 | s.name = 'nested-hstore' 11 | s.require_paths = ['lib'] 12 | s.version = NestedHstore::VERSION 13 | s.license = 'MIT' 14 | 15 | s.add_dependency 'activerecord' 16 | s.add_dependency 'activesupport' 17 | 18 | s.add_development_dependency 'appraisal' 19 | s.add_development_dependency 'rspec' 20 | s.add_development_dependency 'pg' 21 | end 22 | -------------------------------------------------------------------------------- /spec/db/connection.yml: -------------------------------------------------------------------------------- 1 | adapter: postgresql 2 | database: nested_hstore_test 3 | username: postgres 4 | password: 5 | -------------------------------------------------------------------------------- /spec/db/create_posts.rb: -------------------------------------------------------------------------------- 1 | class CreatePosts < ActiveRecord::Migration 2 | def self.up 3 | execute "CREATE EXTENSION IF NOT EXISTS hstore" 4 | create_table :posts do |t| 5 | t.hstore :properties 6 | end 7 | end 8 | 9 | def self.down 10 | drop_table :posts 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /spec/nested_hstore/active_record/coders/nested_hstore_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe ActiveRecord::Coders::NestedHstore do 4 | class Post < ActiveRecord::Base 5 | serialize :properties, ActiveRecord::Coders::NestedHstore 6 | end 7 | 8 | before :all do 9 | CreatePosts.up 10 | end 11 | 12 | after :all do 13 | CreatePosts.down 14 | end 15 | 16 | describe "dumping and loading" do 17 | context "with a nested hash" do 18 | let(:value) { { 'foo' => { 'bar' => 'baz' } } } 19 | 20 | it "preserves the value" do 21 | post = Post.new(properties: value) 22 | post.save! 23 | post.reload 24 | post.properties.should == value 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/nested_hstore/serializer_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe NestedHstore::Serializer do 4 | let(:serializer) { NestedHstore::Serializer.new } 5 | 6 | def it_serializes 7 | serialized = serializer.serialize(@deserialized) 8 | serialized.should == @serialized 9 | end 10 | 11 | def it_deserializes 12 | deserialized = serializer.deserialize(@serialized) 13 | deserialized.should == @deserialized 14 | end 15 | 16 | context 'with a nested hash' do 17 | before :each do 18 | @deserialized = { 19 | 'a' => { 20 | 'b' => { 21 | 'c' => 'String 1', 22 | 'd' => 'String 2' 23 | } 24 | }, 25 | 'e' => { 26 | 'f' => 'String 3' 27 | } 28 | } 29 | @serialized = { 30 | "a"=>"{\"b\":{\"c\":\"String 1\",\"d\":\"String 2\"}}", 31 | "e"=>"{\"f\":\"String 3\"}", 32 | } 33 | end 34 | 35 | describe '#serialize' do 36 | it('serializes') { it_serializes } 37 | end 38 | 39 | describe '#deserialize' do 40 | it('deserializes') { it_deserializes } 41 | end 42 | end 43 | 44 | context 'with a nested hash with a type attribute' do 45 | before :each do 46 | @deserialized = { 47 | 'a' => { 48 | 'b' => { 49 | 'c' => 'String 1', 50 | 'd' => 'String 2' 51 | } 52 | }, 53 | 'e' => { 54 | 'f' => 'String 3' 55 | } 56 | } 57 | @serialized = { 58 | "a"=>"{\"b\":{\"c\":\"String 1\",\"d\":\"String 2\"}}", 59 | "e"=>"{\"f\":\"String 3\"}", 60 | "__TYPE__"=>"__HASH__" 61 | } 62 | end 63 | 64 | describe '#deserialize' do 65 | it('deserializes') { it_deserializes } 66 | end 67 | end 68 | 69 | context 'with an array' do 70 | before :each do 71 | @deserialized = ('0'..'10').to_a 72 | @serialized = { 73 | "0"=>"0", 74 | "1"=>"1", 75 | "2"=>"2", 76 | "3"=>"3", 77 | "4"=>"4", 78 | "5"=>"5", 79 | "6"=>"6", 80 | "7"=>"7", 81 | "8"=>"8", 82 | "9"=>"9", 83 | "10"=>"10", 84 | "__TYPE__"=>"__ARRAY__" 85 | } 86 | end 87 | 88 | describe '#serialize' do 89 | it('serializes') { it_serializes } 90 | end 91 | 92 | describe '#deserialize' do 93 | it('deserializes') { it_deserializes } 94 | end 95 | end 96 | 97 | context 'with an array of arrays' do 98 | before do 99 | @deserialized = [ 100 | ['a', 'b', 'c'], 101 | [1, 2] 102 | ] 103 | @serialized = { 104 | "0"=>"[\"a\",\"b\",\"c\"]", 105 | "1"=>"[1,2]", 106 | "__TYPE__"=>"__ARRAY__" 107 | } 108 | end 109 | 110 | describe '#serialize' do 111 | it('serializes') { it_serializes } 112 | end 113 | 114 | describe '#deserialize' do 115 | it('deserializes') { it_deserializes } 116 | end 117 | end 118 | 119 | context 'with an array of nested hashes' do 120 | before do 121 | @deserialized = [ 122 | { 123 | 'a' => { 124 | 'b' => 'String 1' 125 | }, 126 | 'c' => 'String 2' 127 | }, 128 | { 129 | 'd' => 'String 3' 130 | } 131 | ] 132 | @serialized = { 133 | "0"=>"{\"a\":{\"b\":\"String 1\"},\"c\":\"String 2\"}", 134 | "1"=>"{\"d\":\"String 3\"}", 135 | "__TYPE__"=>"__ARRAY__" 136 | } 137 | end 138 | 139 | describe '#serialize' do 140 | it('serializes') { it_serializes } 141 | end 142 | 143 | describe '#deserialize' do 144 | it('deserializes') { it_deserializes } 145 | end 146 | end 147 | 148 | context 'with a boolean true' do 149 | before do 150 | @deserialized = true 151 | @serialized = { 152 | "__VALUE__"=>"true", 153 | "__TYPE__"=>"__BOOLEAN__" 154 | } 155 | end 156 | 157 | describe '#serialize' do 158 | it('serializes') { it_serializes } 159 | end 160 | 161 | describe '#deserialize' do 162 | it('deserializes') { it_deserializes } 163 | end 164 | end 165 | 166 | context 'with a boolean false' do 167 | before do 168 | @deserialized = false 169 | @serialized = { 170 | "__VALUE__"=>"false", 171 | "__TYPE__"=>"__BOOLEAN__" 172 | } 173 | end 174 | 175 | describe '#serialize' do 176 | it('serializes') { it_serializes } 177 | end 178 | 179 | describe '#deserialize' do 180 | it('deserializes') { it_deserializes } 181 | end 182 | end 183 | 184 | context 'with a float' do 185 | before do 186 | @deserialized = 43.1 187 | @serialized = { 188 | "__VALUE__"=>"43.1", 189 | "__TYPE__"=>"__FLOAT__" 190 | } 191 | end 192 | 193 | describe '#serialize' do 194 | it('serializes') { it_serializes } 195 | end 196 | 197 | describe '#deserialize' do 198 | it('deserializes') { it_deserializes } 199 | end 200 | end 201 | 202 | context 'with an integer' do 203 | before do 204 | @deserialized = 43 205 | @serialized = { 206 | "__VALUE__"=>"43", 207 | "__TYPE__"=>"__INTEGER__" 208 | } 209 | end 210 | 211 | describe '#serialize' do 212 | it('serializes') { it_serializes } 213 | end 214 | 215 | describe '#deserialize' do 216 | it('deserializes') { it_deserializes } 217 | end 218 | end 219 | 220 | context 'with a string' do 221 | before do 222 | @deserialized = 'String 1' 223 | @serialized = { 224 | "__VALUE__"=>"String 1", 225 | "__TYPE__"=>"__STRING__" 226 | } 227 | end 228 | 229 | describe '#serialize' do 230 | it('serializes') { it_serializes } 231 | end 232 | 233 | describe '#deserialize' do 234 | it('deserializes') { it_deserializes } 235 | end 236 | end 237 | end 238 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'nested-hstore' 2 | 3 | RSpec.configure do |config| 4 | # ## Mock Framework 5 | # 6 | # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line: 7 | # 8 | # config.mock_with :mocha 9 | # config.mock_with :flexmock 10 | # config.mock_with :rr 11 | 12 | # Run specs in random order to surface order dependencies. If you find an 13 | # order dependency and want to debug it, you can fix the order by providing 14 | # the seed, which is printed after each run. 15 | # --seed 1234 16 | config.order = "random" 17 | end 18 | 19 | db_path = Pathname.new(File.expand_path('../db', __FILE__)) 20 | require db_path.join('create_posts.rb') 21 | ActiveRecord::Base.establish_connection(YAML.load_file(db_path.join('connection.yml'))) 22 | 23 | --------------------------------------------------------------------------------