property_sets 1.0.4 → 1.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 +77 -58
- data/lib/property_sets/action_view_extension.rb +20 -17
- data/property_sets.gemspec +1 -1
- data/test/test_view_extensions.rb +11 -2
- metadata +2 -2
data/README.md
CHANGED
@@ -6,112 +6,131 @@ This gem is a way for you to use a basic "key/value" store for storing attribute
|
|
6
6
|
|
7
7
|
You configure the allowed stored properties by specifying these in the model:
|
8
8
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
```ruby
|
10
|
+
class Account < ActiveRecord::Base
|
11
|
+
property_set :settings do
|
12
|
+
property :version, :default => "v1.0"
|
13
|
+
property :featured, :protected => true
|
14
|
+
property :activated
|
15
|
+
end
|
16
|
+
|
17
|
+
property_set :texts do
|
18
|
+
property :epilogue
|
19
|
+
end
|
20
|
+
end
|
21
|
+
```
|
20
22
|
|
21
23
|
The declared properties can then be accessed runtime via the defined association:
|
22
24
|
|
23
|
-
|
24
|
-
|
25
|
+
```ruby
|
26
|
+
# Return the value of the version record for this account, or the default value if not set
|
27
|
+
account.settings.version
|
25
28
|
|
26
|
-
|
27
|
-
|
29
|
+
# Update the version record with given value
|
30
|
+
account.settings.version = "v1.1"
|
28
31
|
|
29
|
-
|
30
|
-
|
32
|
+
# Query the truth value of the property
|
33
|
+
account.settings.featured?
|
31
34
|
|
32
|
-
|
33
|
-
|
35
|
+
# Short hand for setting one or more values
|
36
|
+
account.settings.set(:version => "v1.2", :activated => true)
|
37
|
+
```
|
34
38
|
|
35
39
|
### Validations
|
36
40
|
|
37
41
|
Property sets supports standard AR validations, although in a somewhat manual fashion.
|
38
42
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
+
```ruby
|
44
|
+
class Account < ActiveRecord::Base
|
45
|
+
property_set :settings do
|
46
|
+
property :version, :default => "v1.0"
|
47
|
+
property :featured, :protected => true
|
43
48
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
49
|
+
validates_format_of :value, :with => /v\d+\.\d+/, :message => "of version is invalid",
|
50
|
+
:if => Proc.new { |r| r.name.to_sym == :version }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
```
|
48
54
|
|
49
|
-
On
|
50
|
-
setting record using
|
55
|
+
On `account.save` this will result in an error record being added. You can also inspect the
|
56
|
+
setting record using `account.settings.version_record`
|
51
57
|
|
52
58
|
### Bulk operations
|
53
59
|
|
54
60
|
Stored properties can also be updated with the update_attributes and update_attributes! methods by
|
55
61
|
enabling nested attributes. Like this (from the test cases):
|
56
62
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
63
|
+
```ruby
|
64
|
+
@account.texts_attributes = [
|
65
|
+
{ :name => "foo", :value => "1" },
|
66
|
+
{ :name => "bar", :value => "0" }
|
67
|
+
]
|
68
|
+
```
|
61
69
|
|
62
70
|
And for existing records:
|
63
71
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
72
|
+
```ruby
|
73
|
+
@account.update_attributes!(:texts_attributes => [
|
74
|
+
{ :id => @account.texts.foo.id, :name => "foo", :value => "0" },
|
75
|
+
{ :id => @account.texts.bar.id, :name => "bar", :value => "1" }
|
76
|
+
])
|
77
|
+
```
|
68
78
|
|
69
79
|
Using nested attributes is subject to implementing your own security measures for mass update assignments.
|
70
80
|
Alternatively, it is possible to use a custom hash structure:
|
71
81
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
}
|
76
|
-
|
82
|
+
```ruby
|
83
|
+
params = {
|
84
|
+
:settings => { :version => "v4.0", :featured => "1" },
|
85
|
+
:texts => { :epilogue => "Wibble wobble" }
|
86
|
+
}
|
77
87
|
|
78
|
-
|
88
|
+
@account.update_attributes(params)
|
89
|
+
```
|
90
|
+
|
91
|
+
The above will not update `featured` as this has the protected flag set and is hence protected from
|
79
92
|
mass updates.
|
80
93
|
|
81
94
|
### View helpers
|
82
95
|
|
83
96
|
We support a couple of convenience mechanisms for building forms and putting the values into the above hash structure. So far, only support check boxes and radio buttons:
|
84
97
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
98
|
+
```erb
|
99
|
+
<% form_for(:account, :html => { :method => :put }) do |f| %>
|
100
|
+
<h3><%= f.property_set(:settings).check_box :activated %> Activated?</h3>
|
101
|
+
<h3><%= f.property_set(:settings).radio_button :hot, "yes" %> Hot</h3>
|
102
|
+
<h3><%= f.property_set(:settings).radio_button :not, "no" %> Not</h3>
|
103
|
+
<h3><%= f.property_set(:settings).select :level, [["One", 1], ["Two", 2]] %></h3>
|
104
|
+
<% end %>
|
105
|
+
```
|
91
106
|
|
92
107
|
## Installation
|
93
108
|
|
94
109
|
Install the gem in your rails project by putting it in your Gemfile:
|
95
110
|
|
96
|
-
|
111
|
+
```
|
112
|
+
gem "property_sets"
|
113
|
+
```
|
97
114
|
|
98
115
|
Also remember to create the storage table(s), if for example you are going to be using this with an accounts model and a "settings" property set, you can define the table like:
|
99
116
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
117
|
+
```ruby
|
118
|
+
create_table :account_settings do |t|
|
119
|
+
t.integer :account_id, :null => false
|
120
|
+
t.string :name, :null => false
|
121
|
+
t.string :value
|
122
|
+
t.timestamps
|
123
|
+
end
|
106
124
|
|
107
|
-
|
125
|
+
add_index :account_settings, [ :account_id, :name ], :unique => true
|
126
|
+
```
|
108
127
|
|
109
128
|
## Requirements
|
110
129
|
|
111
130
|
* ActiveRecord
|
112
131
|
* ActiveSupport
|
113
132
|
|
114
|
-
## License
|
133
|
+
## License and copyright
|
115
134
|
|
116
135
|
Copyright 2013 Zendesk
|
117
136
|
|
@@ -4,14 +4,13 @@ module ActionView
|
|
4
4
|
module Helpers
|
5
5
|
class FormBuilder
|
6
6
|
class PropertySetFormBuilderProxy
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
self.object_name = object_name
|
7
|
+
attr_reader :property_set, :template, :object_name, :object
|
8
|
+
|
9
|
+
def initialize(property_set, template, object_name, object)
|
10
|
+
@property_set = property_set
|
11
|
+
@template = template
|
12
|
+
@object_name = object_name
|
13
|
+
@object = object
|
15
14
|
end
|
16
15
|
|
17
16
|
def check_box(property, options = {}, checked_value = "1", unchecked_value = "0")
|
@@ -46,29 +45,33 @@ module ActionView
|
|
46
45
|
template.select("#{object_name}[#{property_set}]", property, choices, { :selected => current_value }, html_options )
|
47
46
|
end
|
48
47
|
|
48
|
+
private
|
49
|
+
|
49
50
|
def prepare_id_name(property, options)
|
50
51
|
throw "Invalid options type #{options.inspect}" unless options.is_a?(Hash)
|
51
52
|
|
52
53
|
options.clone.tap do |prepared_options|
|
53
|
-
|
54
|
-
|
55
|
-
throw "No @#{object_name} in scope" if instance.nil?
|
56
|
-
throw "The property_set_check_box only works on models with property set #{property_set}" unless instance.respond_to?(property_set)
|
57
|
-
|
54
|
+
prepared_options[:object] = object || fetch_target_object
|
58
55
|
prepared_options[:id] ||= "#{object_name}_#{property_set}_#{property}"
|
59
56
|
prepared_options[:name] = "#{object_name}[#{property_set}][#{property}]"
|
60
|
-
prepared_options[:object] = instance
|
61
57
|
end
|
62
58
|
end
|
63
59
|
|
60
|
+
def fetch_target_object
|
61
|
+
instance = template.instance_variable_get("@#{object_name}")
|
62
|
+
|
63
|
+
throw "No @#{object_name} in scope" if instance.nil?
|
64
|
+
throw "The property_set_check_box only works on models with property set #{property_set}" unless instance.respond_to?(property_set)
|
65
|
+
|
66
|
+
instance
|
67
|
+
end
|
68
|
+
|
64
69
|
def prepare_options(property, options, &block)
|
65
70
|
options = prepare_id_name(property, options)
|
66
71
|
options[:checked] = yield(options[:object].send(property_set))
|
67
72
|
options
|
68
73
|
end
|
69
74
|
|
70
|
-
private
|
71
|
-
|
72
75
|
def cast_boolean(value)
|
73
76
|
case value
|
74
77
|
when TrueClass then '1'
|
@@ -80,7 +83,7 @@ module ActionView
|
|
80
83
|
end
|
81
84
|
|
82
85
|
def property_set(identifier)
|
83
|
-
PropertySetFormBuilderProxy.new(identifier, @template,
|
86
|
+
PropertySetFormBuilderProxy.new(identifier, @template, object_name, object)
|
84
87
|
end
|
85
88
|
|
86
89
|
end
|
data/property_sets.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Gem::Specification.new "property_sets", "1.0.
|
1
|
+
Gem::Specification.new "property_sets", "1.0.5" do |s|
|
2
2
|
s.summary = "Property sets for ActiveRecord."
|
3
3
|
s.description = "This gem is an ActiveRecord extension which provides a convenient interface for managing per row properties."
|
4
4
|
s.authors = ["Morten Primdahl"]
|
@@ -11,8 +11,6 @@ class TestViewExtensions < ActiveSupport::TestCase
|
|
11
11
|
@template = stub
|
12
12
|
@builder = ActionView::Helpers::FormBuilder.new(@object_name, @object, @template, {}, 'proc')
|
13
13
|
@proxy = @builder.property_set(@property_set)
|
14
|
-
|
15
|
-
@template.stubs(:instance_variable_get).with("@#{@object_name}").returns(@object)
|
16
14
|
end
|
17
15
|
|
18
16
|
should "provide a form builder proxy" do
|
@@ -20,6 +18,17 @@ class TestViewExtensions < ActiveSupport::TestCase
|
|
20
18
|
assert_equal @property_set, @proxy.property_set
|
21
19
|
end
|
22
20
|
|
21
|
+
should "fetch the target object when not available" do
|
22
|
+
@builder = ActionView::Helpers::FormBuilder.new(@object_name, nil, @template, {}, 'proc')
|
23
|
+
@proxy = @builder.property_set(@property_set)
|
24
|
+
|
25
|
+
@object.stubs(@property_set).returns(stub(@property => 'value'))
|
26
|
+
@template.stubs(:hidden_field)
|
27
|
+
|
28
|
+
@template.expects(:instance_variable_get).with("@#{@object_name}").returns(@object)
|
29
|
+
@proxy.hidden_field(@property)
|
30
|
+
end
|
31
|
+
|
23
32
|
context "#check_box" do
|
24
33
|
context "when called with checked true for a truth value" do
|
25
34
|
setup do
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: property_sets
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.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-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|