activerecord-hstore_properties 0.0.4 → 0.0.5
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/README.md +157 -0
- data/lib/active_record/hstore_properties.rb +4 -2
- data/lib/active_record/properties/base.rb +0 -5
- data/lib/active_record/properties/boolean_property.rb +2 -0
- data/lib/active_record/properties/common_accessors.rb +17 -0
- data/lib/active_record/properties/counter_property.rb +2 -0
- data/lib/active_record/properties/number_property.rb +2 -0
- data/lib/active_record/properties/string_property.rb +2 -0
- data/lib/active_record/properties/translation_property.rb +31 -0
- data/lib/activerecord-hstore_properties/version.rb +1 -1
- data/spec/hstore_properties_spec.rb +49 -7
- data/spec/spec_helper.rb +3 -1
- metadata +5 -3
- data/README.textile +0 -93
data/README.md
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
hstore-properties
|
2
|
+
=================
|
3
|
+
|
4
|
+
Setup
|
5
|
+
-----
|
6
|
+
|
7
|
+
Simply add following to your gemfile
|
8
|
+
|
9
|
+
`gem 'hstore-properties'`
|
10
|
+
|
11
|
+
Create `properties` column in the model you would like to use properties within, i.e.
|
12
|
+
|
13
|
+
`rails g migration AddPropertiesToUsers properties:hstore`
|
14
|
+
|
15
|
+
Apply your migration
|
16
|
+
|
17
|
+
`rake db:migrate`
|
18
|
+
|
19
|
+
Finally, describe your properties in your model file
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
class User < ActiveRecord::Base
|
23
|
+
include ActiveRecord::HstoreProperties
|
24
|
+
properties 'third_name',
|
25
|
+
'some_cool_feature' => :boolean,
|
26
|
+
'comments' => :counter,
|
27
|
+
'age' => :number
|
28
|
+
end
|
29
|
+
```
|
30
|
+
|
31
|
+
Usage
|
32
|
+
-----
|
33
|
+
|
34
|
+
### Retrieving values
|
35
|
+
|
36
|
+
By default, all your properties are of type String. There are number of other property types available though...
|
37
|
+
|
38
|
+
* string
|
39
|
+
* boolean
|
40
|
+
* number
|
41
|
+
* counter
|
42
|
+
* translation
|
43
|
+
|
44
|
+
More will come in near future...
|
45
|
+
|
46
|
+
All properties can be retrieved just as they are written into hstore column, by suffixing them with `_property`, i.e.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
User.last.third_name_property #=> "Jack"
|
50
|
+
```
|
51
|
+
|
52
|
+
#### Booleans
|
53
|
+
|
54
|
+
*Boolean* properties, can be additionaly retrieved by using `_enabled?` and `?` suffixes, that will cast them to boolean value, i.e.
|
55
|
+
|
56
|
+
```ruby
|
57
|
+
User.last.some_cool_feature_enabled? #=> true
|
58
|
+
User.last.some_cool_feature? #=> true
|
59
|
+
```
|
60
|
+
|
61
|
+
What is more, you can toggle value of boolean property using `_enable!` / `_raise!` and `_disable!` / `_lower!` suffixes, e.g.
|
62
|
+
|
63
|
+
```ruby
|
64
|
+
User.last.some_cool_feature_enable! #=> Changes property to true
|
65
|
+
User.last.some_cool_feature_raise! #=> Changes property to true
|
66
|
+
User.last.some_cool_feature_disable! #=> Changes property to false
|
67
|
+
User.last.some_cool_feature_lower! #=> Changes property to false
|
68
|
+
```
|
69
|
+
|
70
|
+
#### Counters
|
71
|
+
|
72
|
+
*Counter* properties, can be retrieved by using `_count` suffix, that will cast them to integer value, i.e.
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
User.last.comments_count #=> 10
|
76
|
+
```
|
77
|
+
|
78
|
+
What is more, it is possible to bump counter properties, i.e. following line will increment comments property by 1
|
79
|
+
|
80
|
+
```ruby
|
81
|
+
User.last.comments_bump!
|
82
|
+
```
|
83
|
+
|
84
|
+
#### Translations
|
85
|
+
|
86
|
+
*Translation* kind of property allows you to store translated strings in properties column.
|
87
|
+
The simplest way to store translation is just assign it a value, and it will save it in current locale, e.g.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
class Category < ActiveRecord::Base
|
91
|
+
include ActiveRecord::HstoreProperties
|
92
|
+
properties 'caption' => :translation
|
93
|
+
end
|
94
|
+
|
95
|
+
c = Category.last
|
96
|
+
c.caption = "Nice product" #this will save 'Nice product' into caption_en property
|
97
|
+
c.save
|
98
|
+
c.caption #this will retrieve 'Nice product' from caption_en property
|
99
|
+
```
|
100
|
+
|
101
|
+
You can always enforce in which locale you would like to store property, by suffixing it with any locale code available in `I18n.available_locales`
|
102
|
+
|
103
|
+
```ruby
|
104
|
+
c = Category.last
|
105
|
+
|
106
|
+
c.caption_en = "Nice product"
|
107
|
+
c.caption_nb_no = "Fint produkt"
|
108
|
+
|
109
|
+
c.save
|
110
|
+
|
111
|
+
c.caption #=> "Nice product"
|
112
|
+
c.caption_nb_no #=> "Fint produkt"
|
113
|
+
|
114
|
+
I18n.locale = :'nb-NO'
|
115
|
+
c.caption #=> "Fint produkt"
|
116
|
+
```
|
117
|
+
|
118
|
+
### Updating through forms
|
119
|
+
|
120
|
+
You obviously need to add `:properties` to yours `attr_accessible`
|
121
|
+
|
122
|
+
Below is an example of building appropriate fields dynamically with formtastic
|
123
|
+
|
124
|
+
|
125
|
+
```erb
|
126
|
+
<%= semantic_form_for @user do |f| %>
|
127
|
+
<%= f.first_name %>
|
128
|
+
<%= f.fields_for :properties, OpenStruct.new(@user.properties) do |builder| %>
|
129
|
+
<% User.properties.each do |property| %>
|
130
|
+
<%= builder.input property.name, property.formtastic_options %>
|
131
|
+
<% end %>
|
132
|
+
<% end %>
|
133
|
+
<%= f.submit %>
|
134
|
+
<% end %>
|
135
|
+
```
|
136
|
+
|
137
|
+
Further customization
|
138
|
+
---------------------
|
139
|
+
|
140
|
+
If most of your properties are of the same type, but other than string, you can overwrite `default_property_klass` to make other type default, i.e.
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
class User < ActiveRecord::Base
|
144
|
+
#...
|
145
|
+
def self.default_property_klass
|
146
|
+
ActiveRecord::Properties::BooleanProperty
|
147
|
+
end
|
148
|
+
end
|
149
|
+
```
|
150
|
+
|
151
|
+
When to use?
|
152
|
+
------------
|
153
|
+
|
154
|
+
* If you consider adding redundant column to your table, that will only sometimes store any data
|
155
|
+
* If you would like to make particular model "configurable"
|
156
|
+
* If you will not likely perform queries on specific field but mostly read from it directly
|
157
|
+
|
@@ -1,10 +1,12 @@
|
|
1
1
|
require 'active_support/core_ext/class'
|
2
2
|
require 'active_support/concern'
|
3
3
|
require "active_record/properties/base"
|
4
|
+
require "active_record/properties/common_accessors"
|
4
5
|
require "active_record/properties/boolean_property"
|
5
6
|
require "active_record/properties/counter_property"
|
6
7
|
require "active_record/properties/number_property"
|
7
8
|
require "active_record/properties/string_property"
|
9
|
+
require "active_record/properties/translation_property"
|
8
10
|
|
9
11
|
module ActiveRecord
|
10
12
|
module HstoreProperties
|
@@ -50,8 +52,8 @@ module ActiveRecord
|
|
50
52
|
primary_method_name = method_names.shift
|
51
53
|
|
52
54
|
#Define main method once...
|
53
|
-
define_method(primary_method_name) do
|
54
|
-
self.instance_exec(property, &proc)
|
55
|
+
define_method(primary_method_name) do |*args|
|
56
|
+
self.instance_exec(property, *args, &proc)
|
55
57
|
end
|
56
58
|
|
57
59
|
#... and then define aliases
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Properties
|
3
|
+
module CommonAccessors
|
4
|
+
extend ActiveSupport::Concern
|
5
|
+
included do
|
6
|
+
add_property_accessor '_property' do |property|
|
7
|
+
properties[property.name.to_s]
|
8
|
+
end
|
9
|
+
|
10
|
+
add_property_accessor '=' do |property, *args|
|
11
|
+
properties_will_change!
|
12
|
+
properties[property.name.to_s] = args.shift
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Properties
|
3
|
+
class TranslationProperty < Base
|
4
|
+
def formtastic_options
|
5
|
+
{:as => :string}
|
6
|
+
end
|
7
|
+
|
8
|
+
add_property_accessor '=' do |property, *args|
|
9
|
+
property_name = "#{property.name.to_s}_#{I18n.locale.to_s.underscore}"
|
10
|
+
properties[property_name] = args.shift
|
11
|
+
end
|
12
|
+
|
13
|
+
add_property_accessor '_property' do |property|
|
14
|
+
property_name = "#{property.name.to_s}_#{I18n.locale.to_s.underscore}"
|
15
|
+
properties[property_name]
|
16
|
+
end
|
17
|
+
|
18
|
+
I18n.available_locales.each do |locale|
|
19
|
+
add_property_accessor "_#{locale.to_s.underscore}" do |property|
|
20
|
+
property_name = "#{property.name.to_s}_#{locale.to_s.underscore}"
|
21
|
+
properties[property_name]
|
22
|
+
end
|
23
|
+
|
24
|
+
add_property_accessor "_#{locale.to_s.underscore}=" do |property, *args|
|
25
|
+
property_name = "#{property.name.to_s}_#{locale.to_s.underscore}"
|
26
|
+
properties[property_name] = args.shift
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -1,4 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
|
+
require 'activerecord-hstore_properties'
|
3
|
+
|
2
4
|
describe ActiveRecord::HstoreProperties do
|
3
5
|
with_model :Comment do
|
4
6
|
table do |t|
|
@@ -8,7 +10,6 @@ describe ActiveRecord::HstoreProperties do
|
|
8
10
|
|
9
11
|
model do
|
10
12
|
include ActiveRecord::HstoreProperties
|
11
|
-
|
12
13
|
#We need to do that, as sqlite does not support hstore
|
13
14
|
self.class_eval do
|
14
15
|
def properties
|
@@ -18,11 +19,11 @@ describe ActiveRecord::HstoreProperties do
|
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
|
-
it "
|
22
|
+
it "should respond to properties call" do
|
22
23
|
Comment.respond_to?(:properties).should be_true
|
23
24
|
end
|
24
25
|
|
25
|
-
it "
|
26
|
+
it "should be possible to define properties" do
|
26
27
|
Comment.properties 'property_one', 'property_two'
|
27
28
|
Comment.properties.should be_an_instance_of(Array)
|
28
29
|
Comment.properties.map(&:name).should include('property_one', 'property_two')
|
@@ -33,7 +34,7 @@ describe ActiveRecord::HstoreProperties do
|
|
33
34
|
Comment.properties.first.should be_a_kind_of(ActiveRecord::Properties::StringProperty)
|
34
35
|
end
|
35
36
|
|
36
|
-
it "
|
37
|
+
it "should be possible to define property types other than string" do
|
37
38
|
Comment.properties 'property_one', 'property_two' => :boolean
|
38
39
|
Comment.properties.find { |p| p.name == 'property_two' }.should be_a_kind_of(ActiveRecord::Properties::BooleanProperty)
|
39
40
|
end
|
@@ -48,8 +49,16 @@ describe ActiveRecord::HstoreProperties do
|
|
48
49
|
|
49
50
|
it "properties_set should not return underscored properties" do
|
50
51
|
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
|
52
|
+
Comment.properties_set.find { |p| p.name == '_property_one' }.should be_nil
|
53
|
+
Comment.properties_set.find { |p| p.name == 'property_two' }.should_not be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should be possible to assign property value using simple accessor" do
|
57
|
+
Comment.properties 'property_two' => :boolean
|
58
|
+
comment = Comment.new
|
59
|
+
comment.property_two = true
|
60
|
+
comment.save
|
61
|
+
comment.property_two_property.should == 'true'
|
53
62
|
end
|
54
63
|
|
55
64
|
context "boolean properties" do
|
@@ -92,10 +101,43 @@ describe ActiveRecord::HstoreProperties do
|
|
92
101
|
it "should be possible to query for current count" do
|
93
102
|
@comment.property_two_count.should == 0
|
94
103
|
@comment.property_two_bump!
|
95
|
-
|
96
104
|
@comment.property_two_count.should == 1
|
97
105
|
end
|
106
|
+
end
|
98
107
|
|
108
|
+
context "translation properties" do
|
109
|
+
before(:each) do
|
110
|
+
Comment.properties 'property_two' => :translation
|
111
|
+
@comment = Comment.new
|
112
|
+
@comment.save
|
99
113
|
end
|
100
114
|
|
115
|
+
it "assigning a value should store it in current language scope" do
|
116
|
+
I18n.locale = :'en-GB'
|
117
|
+
@comment.property_two = "nice"
|
118
|
+
@comment.save
|
119
|
+
|
120
|
+
@comment.properties['property_two_en_gb'].should == 'nice'
|
121
|
+
end
|
122
|
+
|
123
|
+
it "retrieving raw value should take current locale into account" do
|
124
|
+
I18n.locale = :'en-GB'
|
125
|
+
@comment.property_two = "nice"
|
126
|
+
@comment.save
|
127
|
+
|
128
|
+
I18n.locale = :'nb-NO'
|
129
|
+
@comment.property_two_property.should be_nil
|
130
|
+
I18n.locale = :'en-GB'
|
131
|
+
@comment.property_two_property.should == 'nice'
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should be possible to use direct readers and writers for all available locales" do
|
135
|
+
@comment.property_two_nb_no = 'Norwegian'
|
136
|
+
@comment.property_two_pl = 'Polish'
|
137
|
+
@comment.save
|
138
|
+
|
139
|
+
@comment.property_two_nb_no.should == 'Norwegian'
|
140
|
+
@comment.property_two_pl.should == 'Polish'
|
141
|
+
end
|
142
|
+
end
|
101
143
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
|
-
require 'activerecord-hstore_properties'
|
2
1
|
require 'with_model'
|
3
2
|
require 'active_record'
|
4
3
|
require 'activerecord-postgres-hstore'
|
5
4
|
require 'pry'
|
6
5
|
|
6
|
+
#For later testing of translation properties
|
7
|
+
I18n.available_locales = [:"nb-NO", :"en-GB", :pl]
|
8
|
+
|
7
9
|
RSpec.configure do |config|
|
8
10
|
ActiveRecord::Base.establish_connection(
|
9
11
|
:adapter => 'sqlite3',
|
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.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-04-
|
12
|
+
date: 2013-04-12 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -152,15 +152,17 @@ files:
|
|
152
152
|
- Gemfile
|
153
153
|
- LICENSE
|
154
154
|
- LICENSE.txt
|
155
|
-
- README.
|
155
|
+
- README.md
|
156
156
|
- Rakefile
|
157
157
|
- activerecord-hstore_properties.gemspec
|
158
158
|
- lib/active_record/hstore_properties.rb
|
159
159
|
- lib/active_record/properties/base.rb
|
160
160
|
- lib/active_record/properties/boolean_property.rb
|
161
|
+
- lib/active_record/properties/common_accessors.rb
|
161
162
|
- lib/active_record/properties/counter_property.rb
|
162
163
|
- lib/active_record/properties/number_property.rb
|
163
164
|
- lib/active_record/properties/string_property.rb
|
165
|
+
- lib/active_record/properties/translation_property.rb
|
164
166
|
- lib/activerecord-hstore_properties.rb
|
165
167
|
- lib/activerecord-hstore_properties/version.rb
|
166
168
|
- spec/hstore_properties_spec.rb
|
data/README.textile
DELETED
@@ -1,93 +0,0 @@
|
|
1
|
-
h1. hstore-properties
|
2
|
-
|
3
|
-
|
4
|
-
h2. Setup
|
5
|
-
|
6
|
-
|
7
|
-
Simply add following to your gemfile
|
8
|
-
|
9
|
-
@gem 'hstore-properties'@
|
10
|
-
|
11
|
-
Create @properties@ column in the model you would like to use properties within, i.e.
|
12
|
-
|
13
|
-
@rails g migration AddPropertiesToUsers properties:hstore@
|
14
|
-
|
15
|
-
Apply your migration
|
16
|
-
|
17
|
-
@rake db:migrate@
|
18
|
-
|
19
|
-
Finally, describe your properties in your model file
|
20
|
-
|
21
|
-
bc. class User < ActiveRecord::Base
|
22
|
-
include ActiveRecord::HstoreProperties
|
23
|
-
properties 'third_name',
|
24
|
-
'some_cool_feature' => :boolean,
|
25
|
-
'comments' => :counter,
|
26
|
-
'age' => :number
|
27
|
-
end
|
28
|
-
|
29
|
-
|
30
|
-
h2. Usage
|
31
|
-
|
32
|
-
h3. Retrieving values
|
33
|
-
|
34
|
-
By default, all your properties are of type String. There are number of other property types available though...
|
35
|
-
|
36
|
-
* string
|
37
|
-
* boolean
|
38
|
-
* number
|
39
|
-
* counter
|
40
|
-
|
41
|
-
More will come in near future...
|
42
|
-
|
43
|
-
All properties can be retrieved just as they are written into hstore column, by suffixing them with @_property@, i.e.
|
44
|
-
|
45
|
-
@User.last.third_name_property #=> "Jack"@
|
46
|
-
|
47
|
-
*Boolean* properties, can be additionaly retrieved by using @_enabled?@ suffix, that will cast them to boolean value, i.e.
|
48
|
-
|
49
|
-
@User.last.some_cool_feature_enabled? #=> true@
|
50
|
-
|
51
|
-
*Counter* properties, can be retrieved by using @_count@ suffix, that will cast them to integer value, i.e.
|
52
|
-
|
53
|
-
@User.last.comments_count #=> 10@
|
54
|
-
|
55
|
-
What is more, it is possible to bump counter properties, i.e. following line will increment comments property by 1
|
56
|
-
|
57
|
-
@User.last.comments_bump!@
|
58
|
-
|
59
|
-
|
60
|
-
h3. Updating through forms
|
61
|
-
|
62
|
-
You obviously need to add @:properties@ to yours @attr_accessible@
|
63
|
-
|
64
|
-
Below is an example of building appropriate fields dynamically with formtastic
|
65
|
-
|
66
|
-
bc.. <%= semantic_form_for @user do |f| %>
|
67
|
-
<%= f.first_name %>
|
68
|
-
<%= f.fields_for :properties, OpenStruct.new(@user.properties) do |builder| %>
|
69
|
-
<% User.properties.each do |property| %>
|
70
|
-
<%= builder.input property.name, property.formtastic_options %>
|
71
|
-
<% end %>
|
72
|
-
<% end %>
|
73
|
-
<%= f.submit %>
|
74
|
-
<% end %>
|
75
|
-
|
76
|
-
h2. Further customization
|
77
|
-
|
78
|
-
If most of your properties are of the same type, but other than string, you can overwrite @default_property_klass@ to make other type default, i.e.
|
79
|
-
|
80
|
-
bc.
|
81
|
-
class User < ActiveRecord::Base
|
82
|
-
#...
|
83
|
-
def self.default_property_klass
|
84
|
-
ActiveRecord::Properties::BooleanProperty
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
h2. When to use?
|
89
|
-
|
90
|
-
* If you consider adding redundant column to your table, that will only sometimes store any data
|
91
|
-
* If you would like to make particular model "configurable"
|
92
|
-
* If you will not likely perform queries on specific field but mostly read from it directly
|
93
|
-
|