activerecord-hstore_properties 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/activerecord-hstore_properties.gemspec +11 -1
- data/lib/active_record/hstore_properties.rb +21 -22
- data/lib/active_record/properties/base.rb +17 -0
- data/lib/active_record/properties/boolean_property.rb +18 -0
- data/lib/active_record/properties/counter_property.rb +13 -1
- data/lib/activerecord-hstore_properties/version.rb +1 -1
- data/spec/hstore_properties_spec.rb +101 -0
- data/spec/spec_helper.rb +14 -1
- metadata +116 -4
- data/lib/active_record/properties/extensions/booleans.rb +0 -15
- data/lib/active_record/properties/extensions/counters.rb +0 -28
@@ -17,5 +17,15 @@ Gem::Specification.new do |gem|
|
|
17
17
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
18
|
gem.require_paths = ["lib"]
|
19
19
|
|
20
|
+
|
21
|
+
gem.add_dependency 'activesupport', '>= 3.2.12'
|
22
|
+
gem.add_dependency 'activerecord-postgres-hstore', '>= 0.7.5'
|
23
|
+
|
20
24
|
gem.add_development_dependency "rspec"
|
21
|
-
|
25
|
+
gem.add_development_dependency "sqlite3"
|
26
|
+
gem.add_development_dependency "activerecord", '>= 3.2.12'
|
27
|
+
gem.add_development_dependency "with_model"
|
28
|
+
gem.add_development_dependency "with_model", "~> 0.3.1"
|
29
|
+
gem.add_development_dependency "pry"
|
30
|
+
|
31
|
+
end
|
@@ -1,35 +1,20 @@
|
|
1
|
+
require 'active_support/core_ext/class'
|
2
|
+
require 'active_support/concern'
|
1
3
|
require "active_record/properties/base"
|
2
4
|
require "active_record/properties/boolean_property"
|
3
5
|
require "active_record/properties/counter_property"
|
4
6
|
require "active_record/properties/number_property"
|
5
7
|
require "active_record/properties/string_property"
|
6
|
-
require "active_record/properties/extensions/booleans"
|
7
|
-
require "active_record/properties/extensions/counters"
|
8
8
|
|
9
9
|
module ActiveRecord
|
10
10
|
module HstoreProperties
|
11
11
|
extend ActiveSupport::Concern
|
12
12
|
|
13
13
|
included do
|
14
|
-
include ActiveRecord::Properties::Extensions::Booleans
|
15
|
-
include ActiveRecord::Properties::Extensions::Counters
|
16
|
-
|
17
14
|
serialize :properties, ::ActiveRecord::Coders::Hstore
|
18
15
|
class_attribute :_properties
|
19
16
|
end
|
20
17
|
|
21
|
-
def define_property_method(method_name, suffix, &block)
|
22
|
-
if method_name.ends_with?(suffix) and (self.class._properties.detect { |pn| pn.name.to_s == method_name.sub(suffix, '') })
|
23
|
-
prop_name = method_name.sub(suffix, '')
|
24
|
-
self.class.send(:define_method, method_name) do
|
25
|
-
block.call(prop_name, self)
|
26
|
-
end
|
27
|
-
[true, block.call(prop_name, self)]
|
28
|
-
else
|
29
|
-
[false, nil]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
18
|
module ClassMethods
|
34
19
|
def properties_set(*args)
|
35
20
|
self._properties.dup.tap do |all_properties|
|
@@ -45,13 +30,9 @@ module ActiveRecord
|
|
45
30
|
self._properties ||= []
|
46
31
|
else
|
47
32
|
self._properties ||= []
|
48
|
-
|
49
33
|
new_properties = extract_properties(args)
|
50
34
|
self._properties += new_properties
|
51
|
-
|
52
|
-
new_properties.each do |property|
|
53
|
-
define_method("#{property.name.to_s}_property") { properties[property.name.to_s] }
|
54
|
-
end
|
35
|
+
define_accessors_for(new_properties)
|
55
36
|
end
|
56
37
|
end
|
57
38
|
|
@@ -61,6 +42,24 @@ module ActiveRecord
|
|
61
42
|
|
62
43
|
private
|
63
44
|
|
45
|
+
def define_accessors_for(properties)
|
46
|
+
properties.each do |property|
|
47
|
+
property.class.property_accessors.each do |suffixes, proc|
|
48
|
+
|
49
|
+
method_names = suffixes.map{|suffix| "#{property.name}#{suffix}"}
|
50
|
+
primary_method_name = method_names.shift
|
51
|
+
|
52
|
+
#Define main method once...
|
53
|
+
define_method(primary_method_name) do
|
54
|
+
self.instance_exec(property, &proc)
|
55
|
+
end
|
56
|
+
|
57
|
+
#... and then define aliases
|
58
|
+
method_names.each { |method_name| alias_method(method_name, primary_method_name) }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
64
63
|
def extract_properties(args)
|
65
64
|
typed_properties = args.extract_options!
|
66
65
|
|
@@ -1,6 +1,23 @@
|
|
1
1
|
module ActiveRecord
|
2
2
|
module Properties
|
3
3
|
class Base < Struct.new(:name)
|
4
|
+
class_attribute :_property_accessors
|
5
|
+
|
6
|
+
class << self
|
7
|
+
def property_accessors
|
8
|
+
self._property_accessors || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def add_property_accessor(*suffixes, &block)
|
12
|
+
self._property_accessors ||= {}
|
13
|
+
self._property_accessors = _property_accessors.merge(suffixes.to_a => block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
add_property_accessor '_property' do |property|
|
18
|
+
properties[property.name.to_s]
|
19
|
+
end
|
20
|
+
|
4
21
|
end
|
5
22
|
end
|
6
23
|
end
|
@@ -4,6 +4,24 @@ module ActiveRecord
|
|
4
4
|
def formtastic_options
|
5
5
|
{:as => :boolean, :checked_value => 'true', :unchecked_value => 'false'}
|
6
6
|
end
|
7
|
+
|
8
|
+
add_property_accessor '_enabled?', '?' do |property|
|
9
|
+
::ActiveRecord::ConnectionAdapters::Column.value_to_boolean(properties[property.name.to_s])
|
10
|
+
end
|
11
|
+
|
12
|
+
add_property_accessor '_raise!', '_enable!' do |property|
|
13
|
+
_properties = self.properties
|
14
|
+
_properties[property.name] = true
|
15
|
+
update_column(:properties, ActiveRecord::Coders::Hstore.new({}).dump(_properties))
|
16
|
+
_properties[property.name]
|
17
|
+
end
|
18
|
+
|
19
|
+
add_property_accessor '_lower!', '_disable!' do |property|
|
20
|
+
_properties = self.properties
|
21
|
+
_properties[property.name] = false
|
22
|
+
update_column(:properties, ActiveRecord::Coders::Hstore.new({}).dump(_properties))
|
23
|
+
_properties[property.name]
|
24
|
+
end
|
7
25
|
end
|
8
26
|
end
|
9
27
|
end
|
@@ -4,6 +4,18 @@ module ActiveRecord
|
|
4
4
|
def formtastic_options
|
5
5
|
{:as => :number, :disabled => true}
|
6
6
|
end
|
7
|
+
|
8
|
+
add_property_accessor '_count' do |property|
|
9
|
+
properties[property.name].to_i
|
10
|
+
end
|
11
|
+
|
12
|
+
add_property_accessor '_bump!' do |property|
|
13
|
+
_properties = self.properties
|
14
|
+
_properties[property.name] = self[property.name].to_i + 1
|
15
|
+
update_column(:properties, ActiveRecord::Coders::Hstore.new({}).dump(_properties))
|
16
|
+
_properties[property.name]
|
17
|
+
end
|
18
|
+
|
7
19
|
end
|
8
20
|
end
|
9
|
-
end
|
21
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
describe ActiveRecord::HstoreProperties do
|
3
|
+
with_model :Comment do
|
4
|
+
table do |t|
|
5
|
+
t.string :text
|
6
|
+
t.hstore :properties
|
7
|
+
end
|
8
|
+
|
9
|
+
model do
|
10
|
+
include ActiveRecord::HstoreProperties
|
11
|
+
|
12
|
+
#We need to do that, as sqlite does not support hstore
|
13
|
+
self.class_eval do
|
14
|
+
def properties
|
15
|
+
self[:properties].is_a?(Hash) ? self[:properties] : ActiveRecord::Coders::Hstore.load(self[:properties])
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
it "it should respond to properties call" do
|
22
|
+
Comment.respond_to?(:properties).should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "it should be possible to define properties" do
|
26
|
+
Comment.properties 'property_one', 'property_two'
|
27
|
+
Comment.properties.should be_an_instance_of(Array)
|
28
|
+
Comment.properties.map(&:name).should include('property_one', 'property_two')
|
29
|
+
end
|
30
|
+
|
31
|
+
it "by default properties should be of String type" do
|
32
|
+
Comment.properties 'property_one'
|
33
|
+
Comment.properties.first.should be_a_kind_of(ActiveRecord::Properties::StringProperty)
|
34
|
+
end
|
35
|
+
|
36
|
+
it "it should be possible to define property types other than string" do
|
37
|
+
Comment.properties 'property_one', 'property_two' => :boolean
|
38
|
+
Comment.properties.find { |p| p.name == 'property_two' }.should be_a_kind_of(ActiveRecord::Properties::BooleanProperty)
|
39
|
+
end
|
40
|
+
|
41
|
+
it "_property accessor should return raw property value" do
|
42
|
+
Comment.properties 'property_two' => :boolean
|
43
|
+
comment = Comment.new
|
44
|
+
comment.properties = {:property_two => true}
|
45
|
+
comment.save
|
46
|
+
comment.property_two_property.should == 'true'
|
47
|
+
end
|
48
|
+
|
49
|
+
it "properties_set should not return underscored properties" do
|
50
|
+
Comment.properties '_property_one', 'property_two'
|
51
|
+
Comment.properties_set.find{|p| p.name == '_property_one'}.should be_nil
|
52
|
+
Comment.properties_set.find{|p| p.name == 'property_two'}.should_not be_nil
|
53
|
+
end
|
54
|
+
|
55
|
+
context "boolean properties" do
|
56
|
+
before(:each) do
|
57
|
+
Comment.properties 'property_two' => :boolean
|
58
|
+
@comment = Comment.new
|
59
|
+
@comment.properties = {:property_two => true}
|
60
|
+
@comment.save
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be possible to be queried for state" do
|
64
|
+
@comment.property_two?.should == true
|
65
|
+
@comment.property_two_enabled?.should == true
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should be possible to be toggled" do
|
69
|
+
@comment.property_two_lower!
|
70
|
+
@comment.property_two?.should == false
|
71
|
+
@comment.property_two_raise!
|
72
|
+
@comment.property_two?.should == true
|
73
|
+
@comment.property_two_disable!
|
74
|
+
@comment.property_two?.should == false
|
75
|
+
@comment.property_two_enable!
|
76
|
+
@comment.property_two?.should == true
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context "counter properties" do
|
81
|
+
before(:each) do
|
82
|
+
Comment.properties 'property_two' => :counter
|
83
|
+
@comment = Comment.new
|
84
|
+
@comment.save
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should be possible to bump counter" do
|
88
|
+
@comment.property_two_bump!
|
89
|
+
@comment.properties['property_two'].should == '1'
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should be possible to query for current count" do
|
93
|
+
@comment.property_two_count.should == 0
|
94
|
+
@comment.property_two_bump!
|
95
|
+
|
96
|
+
@comment.property_two_count.should == 1
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1 +1,14 @@
|
|
1
|
-
require 'activerecord-hstore_properties'
|
1
|
+
require 'activerecord-hstore_properties'
|
2
|
+
require 'with_model'
|
3
|
+
require 'active_record'
|
4
|
+
require 'activerecord-postgres-hstore'
|
5
|
+
require 'pry'
|
6
|
+
|
7
|
+
RSpec.configure do |config|
|
8
|
+
ActiveRecord::Base.establish_connection(
|
9
|
+
:adapter => 'sqlite3',
|
10
|
+
:database => ':memory:'
|
11
|
+
)
|
12
|
+
|
13
|
+
config.extend WithModel
|
14
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: activerecord-hstore_properties
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,40 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-
|
12
|
+
date: 2013-04-11 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: activesupport
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: 3.2.12
|
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: 3.2.12
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: activerecord-postgres-hstore
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: 0.7.5
|
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: 0.7.5
|
14
46
|
- !ruby/object:Gem::Dependency
|
15
47
|
name: rspec
|
16
48
|
requirement: !ruby/object:Gem::Requirement
|
@@ -27,6 +59,86 @@ dependencies:
|
|
27
59
|
- - ! '>='
|
28
60
|
- !ruby/object:Gem::Version
|
29
61
|
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: sqlite3
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: activerecord
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: 3.2.12
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: 3.2.12
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: with_model
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: with_model
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ~>
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: 0.3.1
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ~>
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: 0.3.1
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: pry
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :development
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
30
142
|
description: Allows to describe field names that will be stored within hstore column
|
31
143
|
together with their types. Data can then be retrieved with a set of helpful getters.
|
32
144
|
email:
|
@@ -47,12 +159,11 @@ files:
|
|
47
159
|
- lib/active_record/properties/base.rb
|
48
160
|
- lib/active_record/properties/boolean_property.rb
|
49
161
|
- lib/active_record/properties/counter_property.rb
|
50
|
-
- lib/active_record/properties/extensions/booleans.rb
|
51
|
-
- lib/active_record/properties/extensions/counters.rb
|
52
162
|
- lib/active_record/properties/number_property.rb
|
53
163
|
- lib/active_record/properties/string_property.rb
|
54
164
|
- lib/activerecord-hstore_properties.rb
|
55
165
|
- lib/activerecord-hstore_properties/version.rb
|
166
|
+
- spec/hstore_properties_spec.rb
|
56
167
|
- spec/spec_helper.rb
|
57
168
|
- test/test_helper.rb
|
58
169
|
homepage: https://github.com/stevo/activerecord-hstore_properties
|
@@ -80,5 +191,6 @@ signing_key:
|
|
80
191
|
specification_version: 3
|
81
192
|
summary: Facilitate defining and retrieving information from hstore column
|
82
193
|
test_files:
|
194
|
+
- spec/hstore_properties_spec.rb
|
83
195
|
- spec/spec_helper.rb
|
84
196
|
- test/test_helper.rb
|
@@ -1,15 +0,0 @@
|
|
1
|
-
module ActiveRecord
|
2
|
-
module Properties
|
3
|
-
module Extensions
|
4
|
-
module Booleans
|
5
|
-
def method_missing(*args, &block)
|
6
|
-
method_name = args.first.to_s
|
7
|
-
defined, result = define_property_method(method_name, '_enabled?') do |property_name, object|
|
8
|
-
::ActiveRecord::ConnectionAdapters::Column.value_to_boolean(object.send("#{property_name}_property"))
|
9
|
-
end
|
10
|
-
defined ? result : super
|
11
|
-
end
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
# Allows fetching properties as numerical values by adding _count suffix
|
2
|
-
|
3
|
-
module ActiveRecord
|
4
|
-
module Properties
|
5
|
-
module Extensions
|
6
|
-
module Counters
|
7
|
-
def method_missing(*args, &block)
|
8
|
-
method_name = args.first.to_s
|
9
|
-
|
10
|
-
defined, result = if method_name.ends_with?('_bump!')
|
11
|
-
define_property_method(method_name, '_bump!') do |property_name, object|
|
12
|
-
_properties = object.properties
|
13
|
-
_properties[property_name] = object.send("#{property_name}_count") + 1
|
14
|
-
object.update_column(:properties, ActiveRecord::Coders::Hstore.new({}).dump(_properties))
|
15
|
-
_properties[property_name]
|
16
|
-
end
|
17
|
-
else
|
18
|
-
define_property_method(method_name, '_count') do |property_name, object|
|
19
|
-
object.send("#{property_name}_property").to_i
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
defined ? result : super
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|