property_sets 2.0.0 → 2.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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.
|