hstore-attributes 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ config.yml
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in hstore-attributes.gemspec
4
+ gemspec
5
+
6
+ gem 'activerecord'
7
+ gem 'activerecord-postgres-hstore'
8
+ gem 'rake'
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2012 Christoph Sassenberg
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,47 @@
1
+ # HstoreAttributes
2
+
3
+ HstoreAttributes is a small extension on top of `activerecord-postgres-hstore`, which creates "fake" attributes that map into the hstore hash. By defining these attributes you get automatic validations and type casting.
4
+ The approach is very similar to the `ActiveRecord::Store` feature, yielding better query and index performance in comparison to simple hash-to-yml-string usage.
5
+
6
+
7
+ A small example:
8
+
9
+ class Product < ActiveRecord::Base
10
+ # hstore column is named 'properties'
11
+ hstore :properties, :accessors => { :width => :integer, :height => :integer, :color => :string }
12
+
13
+ # if you want to dump strings only:
14
+ hstore :properties, :accessors => [:color, :other_property, :third_property]
15
+
16
+ validates_numericality_of :weight
17
+
18
+ # ...
19
+ end
20
+
21
+ ## Installation
22
+
23
+ Add this line to your application's Gemfile:
24
+
25
+ gem 'hstore-attributes'
26
+
27
+ And then execute:
28
+
29
+ $ bundle
30
+
31
+ Or install it yourself as:
32
+
33
+ $ gem install hstore-attributes
34
+
35
+ ## Usage
36
+
37
+ Usage should be pretty straightforward as shown in the example. Be sure to create a hstore column that takes the mapped attributes. Please note that this works for postgresql databses only.
38
+
39
+ See: https://github.com/softa/activerecord-postgres-hstore
40
+
41
+ ## Contributing
42
+
43
+ 1. Fork it
44
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
45
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
46
+ 4. Push to the branch (`git push origin my-new-feature`)
47
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/env rake
2
+ require 'rubygems'
3
+ require 'bundler'
4
+
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+
13
+ require 'rake'
14
+ require 'rake/testtask'
15
+
16
+ Rake::TestTask.new do |t|
17
+ t.libs.push "lib"
18
+ t.test_files = FileList[File.expand_path('../test/**/*_test.rb', __FILE__)]
19
+ t.verbose = true
20
+ end
21
+
22
+ require 'bundler/gem_tasks'
@@ -0,0 +1,4 @@
1
+ # adapter is set to postgresql anyway, as this is a pg only feature.
2
+ database: hstore_test
3
+ user: postgres
4
+ password:
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |gem|
3
+ gem.authors = ["Christoph Sassenberg"]
4
+ gem.email = ["christoph.sassenberg@googlemail.com"]
5
+ gem.description = %q{ActiveRecord meta attributes that are mapped to a single (or multiple) hstore columns in postgres}
6
+ gem.summary = %q{ActiveRecord meta attributes that are mapped to a single (or multiple) hstore columns in postgres}
7
+ gem.homepage = "http://github.com/defsprite/hstore-attributes"
8
+
9
+ gem.files = `git ls-files`.split($\)
10
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
11
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
12
+ gem.name = "hstore-attributes"
13
+ gem.require_paths = ["lib"]
14
+ gem.version = "0.0.2"
15
+
16
+ gem.add_dependency('pg')
17
+ gem.add_dependency('activerecord', '>= 3.0')
18
+ gem.add_dependency('activerecord-postgres-hstore')
19
+
20
+ end
@@ -0,0 +1,6 @@
1
+ require "active_support"
2
+
3
+ ActiveSupport.on_load :active_record do
4
+ require "hstore-attributes/hstore_columns"
5
+ require "hstore-attributes/activerecord"
6
+ end
@@ -0,0 +1,8 @@
1
+ # Extends AR to add meta-columns for easy Hstore usage.
2
+ module ActiveRecord
3
+
4
+ class Base
5
+ include HstoreColumns
6
+
7
+ end
8
+ end
@@ -0,0 +1,43 @@
1
+ require 'active_support/concern'
2
+
3
+ module HstoreColumns
4
+ extend ActiveSupport::Concern
5
+
6
+
7
+ def read_hstore(key, hstore_column)
8
+ new_value = send(hstore_column) || {}
9
+ new_value.fetch(key.to_s, nil)
10
+ end
11
+
12
+ def write_hstore(key, value, hstore_column)
13
+ new_value = send(hstore_column) || {}
14
+ new_value.store(key.to_s, value)
15
+ send("#{hstore_column}=", new_value)
16
+ send("#{hstore_column}_will_change!")
17
+ end
18
+
19
+
20
+ module ClassMethods
21
+
22
+ def hstore_accessor(hstore_column, attr_name, type = :string)
23
+ column = ActiveRecord::ConnectionAdapters::Column.new(attr_name, nil, "#{type}")
24
+ columns_hash[attr_name.to_s]= column
25
+ cast_code = column.type_cast_code('v')
26
+ access_code = "(v=read_hstore('#{attr_name}', '#{hstore_column}')) && #{cast_code}"
27
+
28
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
29
+ def #{attr_name}; #{access_code}; end
30
+ def #{attr_name}= value; write_hstore('#{attr_name}', value, '#{hstore_column}'); end
31
+ RUBY
32
+ end
33
+
34
+ def hstore(store_attribute, options = {})
35
+ accessors = options[:accessors]
36
+ accessors.each do |key, value|
37
+ hstore_accessor store_attribute, key, value.nil? ? :string : value
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,114 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/test_helper')
2
+
3
+ class HstoreModel < ActiveRecord::Base
4
+ hstore :data, accessors: {:color => :string, :homepage => :string, :available_on => :date}
5
+ hstore :data, accessors: [:other_color, :other_homepage]
6
+
7
+ hstore :data, accessors: {:string_test => :string, :date_test => :date, :datetime_test => :datetime, :integer_test => :integer}
8
+
9
+ end
10
+
11
+ class HstoreTest < MiniTest::Unit::TestCase
12
+
13
+ def setup
14
+ @connection = ActiveRecord::Base.connection
15
+ puts @connection.native_database_types
16
+
17
+ begin
18
+ @connection.execute 'drop table if exists hstore_models'
19
+ @connection.transaction do
20
+ @connection.create_table('hstore_models') do |t|
21
+ t.string :name
22
+ t.hstore :data, :default => ''
23
+ end
24
+ end
25
+
26
+ rescue ActiveRecord::StatementInvalid => e
27
+ skip "ActiveRecord::StatementInvalid during table setup. Is your DB hstore capable at all? Error was: #{e}"
28
+ rescue NoMethodError => e
29
+ skip "NoMethodError during table setup. Is your DB hstore capable at all? Error was: #{e}"
30
+ end
31
+ end
32
+
33
+ def teardown
34
+ @connection.execute 'drop table if exists hstore_models'
35
+ end
36
+
37
+ describe "the hstore attribute mapper" do
38
+
39
+ before do
40
+ @record = HstoreModel.create(:name => 'John Doe')
41
+ end
42
+
43
+ after do
44
+ HstoreModel.delete_all
45
+ end
46
+
47
+ it "should have the right column type" do
48
+ column = HstoreModel.columns.find { |c| c.name == 'data' }
49
+ assert_equal :hstore, column.type
50
+ end
51
+
52
+ it "should read attributes through accessors" do
53
+ assert_equal 'John Doe', @record.name
54
+ assert_nil @record.string_test
55
+ assert_nil @record.date_test
56
+ assert_nil @record.datetime_test
57
+ assert_nil @record.integer_test
58
+ end
59
+
60
+ it "should write attributes through accessors" do
61
+ datetime = Time.parse(Time.now.to_s) # why? see http://blog.kyleshipley.com/post/9415090497/a-ruby-time-idiosyncrasy
62
+ date = datetime.to_date
63
+
64
+ @record.string_test = 'red'
65
+ @record.integer_test = 42
66
+ @record.date_test = date
67
+ @record.datetime_test = datetime
68
+
69
+ assert_equal 'red', @record.string_test
70
+ assert_equal 42, @record.integer_test
71
+ assert_equal datetime, @record.datetime_test
72
+ assert_equal date, @record.date_test
73
+
74
+ end
75
+
76
+ it "should restore old types after persisting" do
77
+ datetime = Time.parse(Time.now.to_s)
78
+ date = datetime.to_date
79
+
80
+ @record.string_test = 'red'
81
+ @record.integer_test = 42
82
+ @record.date_test = date
83
+ @record.datetime_test = datetime
84
+
85
+ @record.save
86
+ @record.reload
87
+
88
+ assert_equal 'red', @record.string_test
89
+ assert_equal 42, @record.integer_test
90
+ assert_equal datetime, @record.datetime_test
91
+ assert_equal date, @record.date_test
92
+ end
93
+
94
+ it "should write hstore data directly" do
95
+
96
+ @record.data = {'icecream' => 'graeters'}
97
+ @record.save
98
+ assert_equal({'icecream' => 'graeters'}, @record.reload.data)
99
+ end
100
+
101
+
102
+ it "should update the store and mark it as changed" do
103
+ @record.string_test = 'blue'
104
+ assert @record.data_changed?
105
+ end
106
+
107
+ it "should assign data assigned via new or create" do
108
+ @record = HstoreModel.new(:string_test => "Dame Edna")
109
+ assert_equal "Dame Edna", @record.string_test
110
+ end
111
+
112
+ end
113
+
114
+ end
@@ -0,0 +1,16 @@
1
+ require 'bundler/setup'
2
+ require 'active_record'
3
+
4
+ ActiveSupport.on_load :active_record do
5
+ require 'active_record/connection_adapters/postgresql_adapter'
6
+ require "activerecord-postgres-hstore/activerecord"
7
+ end
8
+
9
+ require 'activerecord-postgres-hstore/hash'
10
+ require 'activerecord-postgres-hstore/string'
11
+
12
+ require 'minitest/autorun'
13
+ require File.expand_path('../lib/hstore-attributes', File.dirname(__FILE__))
14
+
15
+ require 'yaml'
16
+ ActiveRecord::Base.establish_connection(YAML::load(File.open(File.expand_path('../config.yml', File.dirname(__FILE__)))))
metadata ADDED
@@ -0,0 +1,115 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hstore-attributes
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Christoph Sassenberg
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-05-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: pg
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
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: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: activerecord
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '3.0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '3.0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: activerecord-postgres-hstore
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: ActiveRecord meta attributes that are mapped to a single (or multiple)
63
+ hstore columns in postgres
64
+ email:
65
+ - christoph.sassenberg@googlemail.com
66
+ executables: []
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - LICENSE
73
+ - README.md
74
+ - Rakefile
75
+ - config.yml.example
76
+ - hstore-attributes.gemspec
77
+ - lib/hstore-attributes.rb
78
+ - lib/hstore-attributes/activerecord.rb
79
+ - lib/hstore-attributes/hstore_columns.rb
80
+ - test/hstore_test.rb
81
+ - test/test_helper.rb
82
+ homepage: http://github.com/defsprite/hstore-attributes
83
+ licenses: []
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ segments:
95
+ - 0
96
+ hash: 1370101062646711657
97
+ required_rubygems_version: !ruby/object:Gem::Requirement
98
+ none: false
99
+ requirements:
100
+ - - ! '>='
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ segments:
104
+ - 0
105
+ hash: 1370101062646711657
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 1.8.21
109
+ signing_key:
110
+ specification_version: 3
111
+ summary: ActiveRecord meta attributes that are mapped to a single (or multiple) hstore
112
+ columns in postgres
113
+ test_files:
114
+ - test/hstore_test.rb
115
+ - test/test_helper.rb