property_sets 2.0.0 → 2.0.1
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/benchmark/read.rb +82 -0
- data/gemfiles/rails2.3.gemfile.lock +1 -1
- data/gemfiles/rails3.0.gemfile.lock +1 -1
- data/gemfiles/rails3.2.gemfile.lock +1 -1
- data/lib/property_sets/active_record_extension.rb +41 -4
- data/lib/property_sets/casting.rb +5 -0
- data/lib/property_sets/property_set_model.rb +7 -2
- data/property_sets.gemspec +1 -1
- data/test/database.rb +10 -0
- data/test/helper.rb +8 -0
- data/test/test_property_sets.rb +10 -0
- metadata +4 -3
data/benchmark/read.rb
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/../test/helper')
|
2
|
+
|
3
|
+
class Account < ActiveRecord::Base
|
4
|
+
# Benchmark reading from an object with many settings when:
|
5
|
+
# 1. Settings are undefined and use the default value (empty)
|
6
|
+
# 2. Settings are defined and use the database value (fully defined)
|
7
|
+
|
8
|
+
# This is a fairly realastic example of an object that has a lot of settings
|
9
|
+
property_set :benchmark_settings do
|
10
|
+
# 30 simple objects
|
11
|
+
10.times do |i|
|
12
|
+
property "float_prop_#{i}", :type => :float, :default => 3.1415
|
13
|
+
property "int_prop_#{i}", :type => :integer, :default => 22
|
14
|
+
property "string_prop_#{i}", :type => :string, :default => "Sausalito, CA"
|
15
|
+
end
|
16
|
+
|
17
|
+
# 10 complex
|
18
|
+
5.times do |i|
|
19
|
+
property "datetime_prop_#{i}", :type => :datetime, :default => Time.now.to_s
|
20
|
+
property "serialized_prop_#{i}", :type => :serialized, :default => { "Hello" => "There" }
|
21
|
+
end
|
22
|
+
|
23
|
+
# 60 booleans
|
24
|
+
60.times do |i|
|
25
|
+
property "boolean_prop_#{i}", :type => :boolean, :default => true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
class BenchmarkRead < ActiveSupport::TestCase
|
32
|
+
|
33
|
+
context "property sets" do
|
34
|
+
|
35
|
+
setup do
|
36
|
+
@account = Account.create(:name => "Name")
|
37
|
+
end
|
38
|
+
|
39
|
+
should "benchmark fully defined settings" do
|
40
|
+
# Most settings are defined and will come from the database
|
41
|
+
@account.benchmark_settings.keys.each do |key|
|
42
|
+
@account.benchmark_settings.build_default(key)
|
43
|
+
end
|
44
|
+
@account.save!
|
45
|
+
@account.reload
|
46
|
+
assert_equal 100, @account.benchmark_settings.count
|
47
|
+
|
48
|
+
GC.start
|
49
|
+
full_timing = Benchmark.ms do
|
50
|
+
1_000.times do
|
51
|
+
read_settings(@account)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
puts "Reading fully defined settings: #{full_timing}ms"
|
55
|
+
end
|
56
|
+
|
57
|
+
should "benchmark defaults" do
|
58
|
+
assert_equal 0, @account.benchmark_settings.count
|
59
|
+
# Most settings are undefined and will use the default value
|
60
|
+
GC.start
|
61
|
+
empty_timing = Benchmark.ms do
|
62
|
+
1_000.times do
|
63
|
+
read_settings(@account)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
puts "Reading empty settings: #{empty_timing}ms"
|
67
|
+
end
|
68
|
+
|
69
|
+
end
|
70
|
+
|
71
|
+
def read_settings(account)
|
72
|
+
account.benchmark_settings.float_prop_1
|
73
|
+
account.benchmark_settings.int_prop_1
|
74
|
+
account.benchmark_settings.string_prop_1
|
75
|
+
account.benchmark_settings.datetime_prop_1
|
76
|
+
account.benchmark_settings.boolean_prop_20?
|
77
|
+
account.benchmark_settings.boolean_prop_30?
|
78
|
+
account.benchmark_settings.boolean_prop_40?
|
79
|
+
account.benchmark_settings.boolean_prop_50?
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
@@ -38,12 +38,15 @@ module PropertySets
|
|
38
38
|
|
39
39
|
# Reports the coerced truth value of the property
|
40
40
|
define_method "#{key}?" do
|
41
|
-
|
41
|
+
type = property_class.type(key)
|
42
|
+
value = lookup_value(type, key)
|
43
|
+
![ "false", "0", "", "off", "n" ].member?(value.to_s.downcase)
|
42
44
|
end
|
43
45
|
|
44
46
|
# Returns the value of the property
|
45
47
|
define_method "#{key}" do
|
46
|
-
|
48
|
+
type = property_class.type(key)
|
49
|
+
lookup_value(type, key)
|
47
50
|
end
|
48
51
|
|
49
52
|
# Assigns a new value to the property
|
@@ -83,13 +86,47 @@ module PropertySets
|
|
83
86
|
end
|
84
87
|
|
85
88
|
def build_default(arg)
|
86
|
-
build(:name => arg.to_s, :value =>
|
89
|
+
build(:name => arg.to_s, :value => raw_default(arg))
|
87
90
|
end
|
88
91
|
|
89
92
|
def lookup_without_default(arg)
|
90
93
|
detect { |property| property.name.to_sym == arg.to_sym }
|
91
94
|
end
|
92
95
|
|
96
|
+
if ActiveRecord::VERSION::STRING < "3.2.0"
|
97
|
+
def lookup_value(type, key)
|
98
|
+
serialized = property_serialized?(key)
|
99
|
+
|
100
|
+
if instance = lookup_without_default(key)
|
101
|
+
instance.value_serialized = serialized
|
102
|
+
PropertySets::Casting.read(type, instance.value)
|
103
|
+
else
|
104
|
+
value = default(key)
|
105
|
+
if serialized
|
106
|
+
PropertySets::Casting.deserialize(value)
|
107
|
+
else
|
108
|
+
PropertySets::Casting.read(type, value)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
else
|
113
|
+
def lookup_value(type, key)
|
114
|
+
serialized = property_serialized?(key)
|
115
|
+
|
116
|
+
if instance = lookup_without_default(key)
|
117
|
+
instance.value_serialized = serialized
|
118
|
+
PropertySets::Casting.read(type, instance.value)
|
119
|
+
else
|
120
|
+
value = proxy_association.klass.default(key)
|
121
|
+
if serialized
|
122
|
+
PropertySets::Casting.deserialize(value)
|
123
|
+
else
|
124
|
+
PropertySets::Casting.read(type, value)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
93
130
|
# The finder method which returns the property if present, otherwise a new instance with defaults
|
94
131
|
def lookup(arg)
|
95
132
|
instance = lookup_without_default(arg)
|
@@ -116,7 +153,7 @@ module PropertySets
|
|
116
153
|
else
|
117
154
|
association_class = @reflection.klass
|
118
155
|
end
|
119
|
-
association_class.new(:value =>
|
156
|
+
association_class.new(:value => raw_default(arg))
|
120
157
|
end
|
121
158
|
instance.value_serialized = property_serialized?(arg)
|
122
159
|
instance
|
@@ -29,8 +29,7 @@ module PropertySets
|
|
29
29
|
def value
|
30
30
|
if value_serialized
|
31
31
|
v = read_attribute(:value)
|
32
|
-
|
33
|
-
@deserialized_value ||= JSON.parse(v)
|
32
|
+
@deserialized_value ||= PropertySets::Casting.deserialize(v)
|
34
33
|
else
|
35
34
|
super
|
36
35
|
end
|
@@ -100,6 +99,12 @@ module PropertySets
|
|
100
99
|
end
|
101
100
|
|
102
101
|
def default(key)
|
102
|
+
if @properties[key] && @properties[key].key?(:default)
|
103
|
+
PropertySets::Casting.read(type(key), @properties[key][:default])
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def raw_default(key)
|
103
108
|
@properties[key] && @properties[key].key?(:default) ? @properties[key][:default] : nil
|
104
109
|
end
|
105
110
|
|
data/property_sets.gemspec
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
Gem::Specification.new "property_sets", "2.0.
|
1
|
+
Gem::Specification.new "property_sets", "2.0.1" 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"]
|
data/test/database.rb
CHANGED
@@ -65,6 +65,16 @@ ActiveRecord::Schema.define(:version => 1) do
|
|
65
65
|
|
66
66
|
add_index :account_typed_data, [ :account_id, :name ], :unique => true
|
67
67
|
|
68
|
+
create_table "account_benchmark_settings", :force => true do |t|
|
69
|
+
t.integer "account_id"
|
70
|
+
t.string "name"
|
71
|
+
t.string "value"
|
72
|
+
t.datetime "created_at"
|
73
|
+
t.datetime "updated_at"
|
74
|
+
end
|
75
|
+
|
76
|
+
add_index :account_benchmark_settings, [ :account_id, :name ], :unique => true
|
77
|
+
|
68
78
|
create_table "accounts", :force => true do |t|
|
69
79
|
t.string "name"
|
70
80
|
t.datetime "created_at"
|
data/test/helper.rb
CHANGED
@@ -30,6 +30,12 @@ end
|
|
30
30
|
ActiveSupport::TestCase.fixture_path = File.dirname(__FILE__) + "/fixtures/"
|
31
31
|
$LOAD_PATH.unshift(ActiveSupport::TestCase.fixture_path)
|
32
32
|
|
33
|
+
class ActsLikeAnInteger
|
34
|
+
def to_i
|
35
|
+
123
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
33
39
|
class Account < ActiveRecord::Base
|
34
40
|
property_set :settings do
|
35
41
|
property :foo
|
@@ -58,5 +64,7 @@ class Account < ActiveRecord::Base
|
|
58
64
|
property :float_prop, :type => :float
|
59
65
|
property :int_prop, :type => :integer
|
60
66
|
property :serialized_prop, :type => :serialized
|
67
|
+
property :default_prop, :type => :integer, :default => ActsLikeAnInteger.new
|
68
|
+
property :serialized_prop_with_default, :type => :serialized, :default => "[]"
|
61
69
|
end
|
62
70
|
end
|
data/test/test_property_sets.rb
CHANGED
@@ -270,6 +270,11 @@ class TestPropertySets < ActiveSupport::TestCase
|
|
270
270
|
end
|
271
271
|
|
272
272
|
context "typed columns" do
|
273
|
+
|
274
|
+
should "typecast the default value" do
|
275
|
+
assert_equal 123, @account.typed_data.default(:default_prop)
|
276
|
+
end
|
277
|
+
|
273
278
|
context "string data" do
|
274
279
|
should "be writable and readable" do
|
275
280
|
@account.typed_data.string_prop = "foo"
|
@@ -301,6 +306,7 @@ class TestPropertySets < ActiveSupport::TestCase
|
|
301
306
|
should "be writable and readable" do
|
302
307
|
ts = Time.at(Time.now.to_i)
|
303
308
|
@account.typed_data.datetime_prop = ts
|
309
|
+
|
304
310
|
assert_equal ts, @account.typed_data.datetime_prop
|
305
311
|
@account.save!
|
306
312
|
assert_equal ts, @account.typed_data.datetime_prop
|
@@ -324,6 +330,10 @@ class TestPropertySets < ActiveSupport::TestCase
|
|
324
330
|
assert_equal({'a' => 1, 'b' => 2}, @account.typed_data.serialized_prop)
|
325
331
|
end
|
326
332
|
|
333
|
+
should "retrieve default values from JSON" do
|
334
|
+
assert_equal([], @account.typed_data.serialized_prop_with_default)
|
335
|
+
end
|
336
|
+
|
327
337
|
should "not overflow the column" do
|
328
338
|
@account.typed_data.serialized_prop = (1..100_000).to_a
|
329
339
|
assert !@account.typed_data.lookup(:serialized_prop).valid?
|
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: 2.0.
|
4
|
+
version: 2.0.1
|
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-
|
12
|
+
date: 2013-07-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: activesupport
|
@@ -204,6 +204,7 @@ files:
|
|
204
204
|
- LICENSE
|
205
205
|
- README.md
|
206
206
|
- Rakefile
|
207
|
+
- benchmark/read.rb
|
207
208
|
- gemfiles/rails2.3.gemfile
|
208
209
|
- gemfiles/rails2.3.gemfile.lock
|
209
210
|
- gemfiles/rails3.0.gemfile
|
@@ -245,7 +246,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
245
246
|
version: '0'
|
246
247
|
requirements: []
|
247
248
|
rubyforge_project:
|
248
|
-
rubygems_version: 1.8.
|
249
|
+
rubygems_version: 1.8.25
|
249
250
|
signing_key:
|
250
251
|
specification_version: 3
|
251
252
|
summary: Property sets for ActiveRecord.
|