property_sets 0.0.11 → 0.0.12

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.rdoc CHANGED
@@ -1,18 +1,16 @@
1
1
  = Property sets
2
2
 
3
- The property_set gem is an evolution of the has_settings gem which was an evolution of the features gem. This is getting old.
4
-
5
3
  This gem is a way for you to use a basic "key/value" store for storing attributes for a given model in a relational fashion where there's a row per attribute. Alternatively you'd need to add a new column per attribute to your main table, or serialize the attributes and their values.
6
4
 
7
5
  == Description
8
6
 
9
- You configure the allowed stored properties by specifying these in an initializer:
7
+ You configure the allowed stored properties by specifying these in the model:
10
8
 
11
9
  class Account < ActiveRecord::Base
12
10
  property_set :settings do
13
11
  property :version, :default => "v1.0"
14
- property :featured
15
- property :product
12
+ property :featured, :protected => true
13
+ property :activated
16
14
  end
17
15
 
18
16
  property_set :texts do
@@ -34,25 +32,68 @@ The declared properties can then be accessed runtime via the defined association
34
32
  # Destroy the version record
35
33
  account.settings.version.destroy
36
34
 
37
- On top of the basic access paths, there are some short cuts:
35
+ === Convenience methods
36
+
37
+ On top of the basic access paths, there are some short cuts, mainly convenience methods for dealing with booleans:
38
+
39
+ # immediately changes the value of the setting
40
+ account.settings.version=("v3.0")
41
+
42
+ # coerces the setting to boolean AR style
43
+ account.settings.featured?
38
44
 
39
- account.settings.featured=(1) # immediately changes the value of the setting
40
- account.settings.featured? # coerces the setting to boolean AR style
41
- account.settings.featured.enable # sets the value of this setting to a true value
42
- account.settings.featured.disable # sets the value of this setting to a false value
45
+ # sets the value of this setting to a true value
46
+ account.settings.featured.enable
43
47
 
44
- If the value has never been set, a nil (or default) is returned. And that's pretty much it.
48
+ # sets the value of this setting to a false value
49
+ account.settings.featured.disable
50
+
51
+ === Bulk operations
45
52
 
46
53
  Stored properties can also be updated with the update_attributes and update_attributes! methods by
47
- enabling nested attributes. See the test cases for examples.
54
+ enabling nested attributes. Like this (from the test cases):
55
+
56
+ @account.texts_attributes = [
57
+ { :name => "foo", :value => "1" },
58
+ { :name => "bar", :value => "0" }
59
+ ]
60
+
61
+ And for existing records:
62
+
63
+ @account.update_attributes!(:texts_attributes => [
64
+ { :id => @account.texts.foo.id, :name => "foo", :value => "0" },
65
+ { :id => @account.texts.bar.id, :name => "bar", :value => "1" }
66
+ ])
67
+
68
+ Using nested attributes is subject to implementing your own security measures for mass update assignments.
69
+ Alternatively, it is possible to use a custom hash structure:
70
+
71
+ params = { :property_sets => {
72
+ :settings => { :version => "v4.0", :featured => "1" },
73
+ :texts => { :epilogue => "Wibble wobble" }
74
+ }}
75
+ @account.update_attributes(params)
76
+
77
+ The above will not update +featured+ as this has the protected flag set and is hence protected from
78
+ mass updates.
79
+
80
+ === View helpers
81
+
82
+ We support a single convenience mechanism for building forms and putting the values into the above hash structure. So far, we only support check boxes:
83
+
84
+ <% form_for(:account, :html => { :method => :put }) do |f| %>
85
+ <h3>
86
+ <%= f.property_set(:settings).check_box :activated %> Activated?
87
+ </h3>
88
+ <% end %>
48
89
 
49
90
  == Installation
50
91
 
51
92
  Install the gem in your rails project by putting it in your Gemfile:
52
93
 
53
- gem 'property_set'
94
+ gem "property_sets"
54
95
 
55
- Also remember to create the storage table, if for example you are going to be using this with an accounts model, you can define the table like:
96
+ 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:
56
97
 
57
98
  create_table :account_settings do |t|
58
99
  t.integer :account_id, :null => false
@@ -66,13 +107,13 @@ Also remember to create the storage table, if for example you are going to be us
66
107
  == Requirements
67
108
 
68
109
  * ActiveRecord
69
- * ActionPack
110
+ * ActiveSupport
70
111
 
71
112
  == LICENSE:
72
113
 
73
114
  (The MIT License)
74
115
 
75
- Copyright (c) 2010 Zendesk
116
+ Copyright (c) 2011 Zendesk
76
117
 
77
118
  Permission is hereby granted, free of charge, to any person
78
119
  obtaining a copy of this software and associated documentation
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.11
1
+ 0.0.12
data/lib/property_sets.rb CHANGED
@@ -1,6 +1,6 @@
1
+ require 'property_sets/property_set_helper'
1
2
  require 'property_sets/property_set_model'
2
3
  require 'property_sets/active_record_extension'
3
- require 'property_sets/property_set_helper'
4
4
 
5
5
  module PropertySets
6
6
  def self.ensure_property_set_class(association, owner_class)
@@ -34,10 +34,15 @@ module PropertySets
34
34
  has_many association.to_s.pluralize.to_sym, :class_name => property_class.name, :dependent => :destroy do
35
35
 
36
36
  # Accepts a name value pair hash { :name => 'value', :pairs => true } and builds a property for each key
37
- def bulk(property_pairs)
37
+ def bulk(property_pairs, with_protection = false)
38
38
  property_pairs.keys.each do |name|
39
- value = property_pairs[name]
40
- self << proxy_reflection.klass.new(:name => name.to_s, :value => value)
39
+ record = lookup(name).record
40
+ if with_protection && record.protected?
41
+ logger.warn("Someone tried to update the protected #{name} property to #{property_pairs[name]}")
42
+ else
43
+ record.value = property_pairs[name]
44
+ self << record
45
+ end
41
46
  end
42
47
  end
43
48
 
@@ -73,9 +78,32 @@ module PropertySets
73
78
  end
74
79
  end
75
80
 
81
+ module InstanceMethods
82
+ def update_attributes_with_property_sets(attributes)
83
+ update_property_set_attributes(attributes)
84
+ update_attributes_without_property_sets(attributes)
85
+ end
86
+
87
+ def update_attributes_with_property_sets!(attributes)
88
+ update_property_set_attributes(attributes)
89
+ update_attributes_without_property_sets!(attributes)
90
+ end
91
+
92
+ def update_property_set_attributes(attributes)
93
+ if attributes && property_sets = attributes.delete(:property_sets)
94
+ property_sets.each do |property_set, property_set_attributes|
95
+ send(property_set).bulk(property_set_attributes, true)
96
+ end
97
+ end
98
+ end
99
+ end
76
100
  end
77
101
  end
78
102
 
79
103
  ActiveRecord::Base.class_eval do
80
- extend PropertySets::ActiveRecordExtension::ClassMethods
104
+ include PropertySets::ActiveRecordExtension::InstanceMethods
105
+ extend PropertySets::ActiveRecordExtension::ClassMethods
106
+
107
+ alias_method_chain :update_attributes, :property_sets
108
+ alias_method_chain :update_attributes!, :property_sets
81
109
  end
@@ -0,0 +1,22 @@
1
+ require 'delegate'
2
+
3
+ module PropertySets
4
+ class FormBuilderProxy < Delegator
5
+ attr_accessor :builder
6
+ attr_accessor :property_set
7
+
8
+ def initialize(property_set, builder)
9
+ self.property_set = property_set
10
+ self.builder = builder
11
+ end
12
+
13
+ def __getobj__
14
+ builder
15
+ end
16
+
17
+ def check_box(property, options = {}, checked_value = "1", unchecked_value = "0")
18
+ builder.property_set_check_box(property_set, property, options, checked_value, unchecked_value)
19
+ end
20
+ end
21
+ end
22
+
@@ -1,19 +1,28 @@
1
+ require 'property_sets/form_builder_proxy'
2
+
1
3
  module ActionView
2
4
  module Helpers
3
- def setting_check_box(model_name, method, options = {}, checked_value = "1", unchecked_value = "0")
5
+ # property_set_check_box(:account, :property_association, :property_key, options)
6
+ def property_set_check_box(model_name, property_set, property, options = {}, checked_value = "1", unchecked_value = "0")
4
7
  the_model = @template.instance_variable_get("@#{model_name}")
8
+
5
9
  throw "No @#{model_name} in scope" if the_model.nil?
6
- throw "The setting_check_box only works on models with settings" unless the_model.respond_to?(:settings)
7
- options[:checked] = the_model.settings.send("#{method}?")
8
- options[:id] ||= "#{model_name}_settings_#{method}"
9
- options[:name] = "#{model_name}[settings][#{method}]"
10
- @template.check_box(model_name, "settings_#{method}", options, checked_value, unchecked_value)
10
+ throw "The property_set_check_box only works on models with property set #{property_set}" unless the_model.respond_to?(property_set)
11
+
12
+ options[:checked] = the_model.send(property).send("#{method}?")
13
+ options[:id] ||= "#{model_name}_property_sets_#{property_set}_#{method}"
14
+ options[:name] = "#{model_name}[property_sets][#{property_set}][#{method}]"
15
+ @template.check_box(model_name, "property_sets_#{property_set}_#{method}", options, checked_value, unchecked_value)
16
+ end
17
+ end
18
+
19
+ class FormBuilder
20
+ def property_set(identifier)
21
+ PropertySets::FormBuilderProxy.new(identifier, self)
11
22
  end
12
23
 
13
- class FormBuilder
14
- def setting_check_box(method, options = {}, checked_value = "1", unchecked_value = "0")
15
- @template.setting_check_box(@object_name, method, objectify_options(options), checked_value, unchecked_value)
16
- end
24
+ def property_set_check_box(property_set, property, options, checked_value, unchecked_value)
25
+ @template.property_set_check_box(@object_name, property_set, property, objectify_options(options), checked_value, unchecked_value)
17
26
  end
18
27
  end
19
28
  end
@@ -20,6 +20,10 @@ module PropertySets
20
20
  self
21
21
  end
22
22
 
23
+ def protected?
24
+ self.class.protected?(name.to_sym)
25
+ end
26
+
23
27
  def to_s
24
28
  value.to_s
25
29
  end
@@ -43,7 +47,9 @@ module PropertySets
43
47
  end
44
48
 
45
49
  def update_owner_timestamp
46
- owner_class_instance.update_attribute(:updated_at, Time.now) if owner_class_instance && !owner_class_instance.new_record?
50
+ if owner_class_instance && !owner_class_instance.new_record? && owner_class_instance.updated_at < 1.second.ago
51
+ owner_class_instance.update_attribute(:updated_at, Time.now)
52
+ end
47
53
  end
48
54
 
49
55
  def reset_owner_association
@@ -72,6 +78,10 @@ module PropertySets
72
78
  @properties[key] && @properties[key].key?(:default) ? @properties[key][:default] : nil
73
79
  end
74
80
 
81
+ def protected?(key)
82
+ @properties[key] && !!@properties[key][:protected]
83
+ end
84
+
75
85
  def owner_class=(owner_class)
76
86
  @owner_class_sym = owner_class.name.underscore.to_sym
77
87
  belongs_to owner_class_sym
@@ -5,28 +5,27 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{property_sets}
8
- s.version = "0.0.11"
8
+ s.version = "0.0.12"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Morten Primdahl"]
12
- s.date = %q{2011-01-13}
12
+ s.date = %q{2011-01-17}
13
13
  s.description = %q{This gem is an ActiveRecord extension which provides a convenient interface for managing per row properties}
14
14
  s.email = %q{morten@zendesk.com}
15
15
  s.extra_rdoc_files = [
16
- "LICENSE",
17
16
  "LICENSE.txt",
18
17
  "README.rdoc"
19
18
  ]
20
19
  s.files = [
21
20
  ".document",
22
21
  "Gemfile",
23
- "LICENSE",
24
22
  "LICENSE.txt",
25
23
  "README.rdoc",
26
24
  "Rakefile",
27
25
  "VERSION",
28
26
  "lib/property_sets.rb",
29
27
  "lib/property_sets/active_record_extension.rb",
28
+ "lib/property_sets/form_builder_proxy.rb",
30
29
  "lib/property_sets/property_set_helper.rb",
31
30
  "lib/property_sets/property_set_model.rb",
32
31
  "property_sets.gemspec",
@@ -7,6 +7,7 @@ class Account < ActiveRecord::Base
7
7
  property :baz
8
8
  property :hep, :default => 'skep'
9
9
  property :bob
10
+ property :bla, :protected => true
10
11
  end
11
12
 
12
13
  property_set :texts do
@@ -31,6 +32,10 @@ class TestPropertySets < ActiveSupport::TestCase
31
32
  assert defined?(AccountText)
32
33
  end
33
34
 
35
+ should "support protecting attributes" do
36
+ assert @account.settings.bla.protected?
37
+ end
38
+
34
39
  should "be empty on a new account" do
35
40
  assert @account.settings.empty?
36
41
  assert @account.texts.empty?
@@ -114,40 +119,99 @@ class TestPropertySets < ActiveSupport::TestCase
114
119
  assert @account.settings.foo.value.nil?
115
120
  end
116
121
 
117
- should "support bulk build multiple properties in one go" do
118
- @account.settings.bulk(:foo => "123", :bar => "456")
119
- @account.save!
120
- assert_equal @account.reload.settings.size, 2
121
- assert_equal @account.settings.foo.value, "123"
122
- assert_equal @account.settings.foo.name, "foo"
123
- assert_equal @account.settings.bar.value, "456"
124
- assert_equal @account.settings.bar.name, "bar"
125
- end
126
-
127
- should "be updatable as nested attributes" do
128
- assert !@account.texts.foo?
129
- assert !@account.texts.bar?
130
- assert !@account.texts.foo.id
131
- assert !@account.texts.bar.id
132
- assert @account.texts.empty?
122
+ context "bulk updates" do
123
+ should "support bulk create/update of multiple properties in one go" do
124
+ [ @account, Account.new(:name => "Mibble") ].each do |account|
125
+ account.settings.bulk(:foo => "123", :bar => "456")
126
+ account.save!
127
+
128
+ assert_equal account.reload.settings.size, 2
129
+ assert_equal account.settings.foo.value, "123"
130
+ assert_equal account.settings.foo.name, "foo"
131
+ assert_equal account.settings.bar.value, "456"
132
+ assert_equal account.settings.bar.name, "bar"
133
+
134
+ account.settings.bulk(:bar => "789", :baz => "012")
135
+ account.save!
136
+
137
+ assert_equal account.reload.settings.size, 3
138
+ assert_equal account.settings.foo.value, "123"
139
+ assert_equal account.settings.bar.value, "789"
140
+ assert_equal account.settings.baz.value, "012"
141
+ end
142
+ end
133
143
 
134
- assert @account.texts_attributes = [{ :name => "foo", :value => "1" }, { :name => "bar", :value => "0" }]
135
- @account.save!
144
+ should "be updateable as AR nested attributes" do
145
+ assert !@account.texts.foo?
146
+ assert !@account.texts.bar?
147
+ assert !@account.texts.foo.id
148
+ assert !@account.texts.bar.id
149
+ assert @account.texts.empty?
150
+
151
+ assert @account.texts_attributes = [{ :name => "foo", :value => "1" }, { :name => "bar", :value => "0" }]
152
+ @account.save!
153
+
154
+ assert @account.texts.foo?
155
+ assert !@account.texts.bar?
156
+ assert @account.texts.foo.id
157
+ assert @account.texts.bar.id
158
+
159
+ @account.update_attributes!(:texts_attributes => [
160
+ { :id => @account.texts.foo.id, :name => "foo", :value => "0" },
161
+ { :id => @account.texts.bar.id, :name => "bar", :value => "1" }
162
+ ])
163
+ assert !@account.texts.foo?
164
+ assert @account.texts.bar?
165
+ end
136
166
 
137
- assert @account.texts.foo?
138
- assert !@account.texts.bar?
139
- assert @account.texts.foo.id
140
- assert @account.texts.bar.id
167
+ should "be updateable as a nested structure" do
168
+ assert !@account.settings.foo?
169
+ assert !@account.settings.bar?
170
+ assert !@account.settings.foo.id
171
+ assert !@account.settings.bar.id
172
+ assert @account.settings.empty?
173
+
174
+ attribs = {
175
+ :name => "Kim",
176
+ :property_sets => {
177
+ :settings => { :foo => "1", :bar => "0" }
178
+ }
179
+ }
180
+
181
+ assert @account.update_attributes(attribs)
182
+ @account.save!
183
+
184
+ assert @account.settings.foo?
185
+ assert !@account.settings.bar?
186
+ assert @account.settings.foo.id
187
+ assert @account.settings.bar.id
188
+ assert @account.settings.foo.value == "1"
189
+ assert @account.settings.bar.value == "0"
190
+
191
+ attribs = {
192
+ :name => "Kim",
193
+ :property_sets => {
194
+ :settings => { :foo => "1", :bar => "1", :baz => "1", :bla => "1" }
195
+ }
196
+ }
197
+
198
+ assert @account.update_attributes!(attribs)
199
+
200
+ assert @account.settings.foo?
201
+ assert @account.settings.bar?
202
+ assert @account.settings.baz?
203
+ assert !@account.settings.bla?
204
+ end
205
+ end
141
206
 
142
- @account.update_attributes!(:texts_attributes => [
143
- { :id => @account.texts.foo.id, :name => "foo", :value => "0" },
144
- { :id => @account.texts.bar.id, :name => "bar", :value => "1" }
145
- ])
146
- assert !@account.texts.foo?
147
- assert @account.texts.bar?
207
+ context "view construction" do
208
+ should "provide a form builder proxy" do
209
+ proxy = ActionView::FormBuilder.new.property_set(:foo)
210
+ assert proxy.is_a?(PropertySets::FormBuilderProxy)
211
+ assert_equal :foo, proxy.property_set
212
+ proxy.builder.expects(:property_set_check_box).once.with(:foo, :bar, {}, "1", "0")
213
+ proxy.check_box(:bar)
214
+ end
148
215
  end
149
216
  end
150
217
  end
151
-
152
-
153
-
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: property_sets
3
3
  version: !ruby/object:Gem::Version
4
- hash: 9
4
+ hash: 7
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 11
10
- version: 0.0.11
9
+ - 12
10
+ version: 0.0.12
11
11
  platform: ruby
12
12
  authors:
13
13
  - Morten Primdahl
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-01-13 00:00:00 -08:00
18
+ date: 2011-01-17 00:00:00 -08:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -99,19 +99,18 @@ executables: []
99
99
  extensions: []
100
100
 
101
101
  extra_rdoc_files:
102
- - LICENSE
103
102
  - LICENSE.txt
104
103
  - README.rdoc
105
104
  files:
106
105
  - .document
107
106
  - Gemfile
108
- - LICENSE
109
107
  - LICENSE.txt
110
108
  - README.rdoc
111
109
  - Rakefile
112
110
  - VERSION
113
111
  - lib/property_sets.rb
114
112
  - lib/property_sets/active_record_extension.rb
113
+ - lib/property_sets/form_builder_proxy.rb
115
114
  - lib/property_sets/property_set_helper.rb
116
115
  - lib/property_sets/property_set_model.rb
117
116
  - property_sets.gemspec
data/LICENSE DELETED
@@ -1,20 +0,0 @@
1
- Copyright (c) 2009 Morten Primdahl
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.