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 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
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/primdahl/Git/property_sets
3
3
  specs:
4
- property_sets (2.0.0)
4
+ property_sets (2.0.1)
5
5
  activerecord (>= 2.3.14, < 3.3)
6
6
  activesupport (>= 2.3.14, < 3.3)
7
7
  json
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/primdahl/Git/property_sets
3
3
  specs:
4
- property_sets (2.0.0)
4
+ property_sets (2.0.1)
5
5
  activerecord (>= 2.3.14, < 3.3)
6
6
  activesupport (>= 2.3.14, < 3.3)
7
7
  json
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: /Users/primdahl/Git/property_sets
3
3
  specs:
4
- property_sets (2.0.0)
4
+ property_sets (2.0.1)
5
5
  activerecord (>= 2.3.14, < 3.3)
6
6
  activesupport (>= 2.3.14, < 3.3)
7
7
  json
@@ -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
- lookup_or_default(key).true?
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
- PropertySets::Casting.read(property_class.type(key), lookup(key).value)
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 => default(arg))
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 => default(arg))
156
+ association_class.new(:value => raw_default(arg))
120
157
  end
121
158
  instance.value_serialized = property_serialized?(arg)
122
159
  instance
@@ -41,5 +41,10 @@ module PropertySets
41
41
  end
42
42
  end
43
43
 
44
+ def self.deserialize(value)
45
+ return nil if value.nil? || value == "null"
46
+ JSON.parse(value)
47
+ end
48
+
44
49
  end
45
50
  end
@@ -29,8 +29,7 @@ module PropertySets
29
29
  def value
30
30
  if value_serialized
31
31
  v = read_attribute(:value)
32
- return nil if v.nil? || v == "null"
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
 
@@ -1,4 +1,4 @@
1
- Gem::Specification.new "property_sets", "2.0.0" do |s|
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
@@ -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.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-05-15 00:00:00.000000000 Z
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.23
249
+ rubygems_version: 1.8.25
249
250
  signing_key:
250
251
  specification_version: 3
251
252
  summary: Property sets for ActiveRecord.