rails-properties 3.4.3
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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/.travis.yml +74 -0
- data/Gemfile +4 -0
- data/MIT-LICENSE +22 -0
- data/README.md +135 -0
- data/Rakefile +6 -0
- data/ci/Gemfile-rails-3-1 +5 -0
- data/ci/Gemfile-rails-3-2 +5 -0
- data/ci/Gemfile-rails-4-0 +6 -0
- data/ci/Gemfile-rails-4-1 +6 -0
- data/ci/Gemfile-rails-4-2 +6 -0
- data/ci/Gemfile-rails-5-0 +5 -0
- data/ci/Gemfile-rails-5-1 +5 -0
- data/ci/Gemfile-rails-5-2 +5 -0
- data/lib/generators/rails_properties/migration/migration_generator.rb +23 -0
- data/lib/generators/rails_properties/migration/templates/migration.rb +21 -0
- data/lib/rails-properties.rb +23 -0
- data/lib/rails-properties/base.rb +48 -0
- data/lib/rails-properties/configuration.rb +32 -0
- data/lib/rails-properties/property_object.rb +84 -0
- data/lib/rails-properties/scopes.rb +34 -0
- data/lib/rails-properties/version.rb +3 -0
- data/rails-properties.gemspec +29 -0
- data/spec/configuration_spec.rb +108 -0
- data/spec/database.yml +3 -0
- data/spec/properties_spec.rb +248 -0
- data/spec/property_object_spec.rb +153 -0
- data/spec/queries_spec.rb +101 -0
- data/spec/scopes_spec.rb +31 -0
- data/spec/serialize_spec.rb +40 -0
- data/spec/spec_helper.rb +111 -0
- data/spec/support/matchers/perform_queries.rb +22 -0
- data/spec/support/query_counter.rb +17 -0
- metadata +172 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
module RailsProperties
|
2
|
+
class PropertyObject < ActiveRecord::Base
|
3
|
+
self.table_name = 'properties'
|
4
|
+
|
5
|
+
belongs_to :target, :polymorphic => true
|
6
|
+
|
7
|
+
validates_presence_of :var, :target_type
|
8
|
+
validate do
|
9
|
+
errors.add(:value, "Invalid property value") unless value.is_a? Hash
|
10
|
+
|
11
|
+
unless _target_class.default_properties[var.to_sym]
|
12
|
+
errors.add(:var, "#{var} is not defined!")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
serialize :value, Hash
|
17
|
+
|
18
|
+
if RailsProperties.can_protect_attributes?
|
19
|
+
# attr_protected can not be used here because it touches the database which is not connected yet.
|
20
|
+
# So allow no attributes and override <tt>#sanitize_for_mass_assignment</tt>
|
21
|
+
attr_accessible
|
22
|
+
end
|
23
|
+
|
24
|
+
REGEX_SETTER = /\A([a-z]\w+)=\Z/i
|
25
|
+
REGEX_GETTER = /\A([a-z]\w+)\Z/i
|
26
|
+
|
27
|
+
def respond_to?(method_name, include_priv=false)
|
28
|
+
super || method_name.to_s =~ REGEX_SETTER || _property?(method_name)
|
29
|
+
end
|
30
|
+
|
31
|
+
def method_missing(method_name, *args, &block)
|
32
|
+
if block_given?
|
33
|
+
super
|
34
|
+
else
|
35
|
+
if attribute_names.include?(method_name.to_s.sub('=',''))
|
36
|
+
super
|
37
|
+
elsif method_name.to_s =~ REGEX_SETTER && args.size == 1
|
38
|
+
_set_value($1, args.first)
|
39
|
+
elsif method_name.to_s =~ REGEX_GETTER && args.size == 0
|
40
|
+
_get_value($1)
|
41
|
+
else
|
42
|
+
super
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
protected
|
48
|
+
if RailsProperties.can_protect_attributes?
|
49
|
+
# Simulate attr_protected by removing all regular attributes
|
50
|
+
def sanitize_for_mass_assignment(attributes, role = nil)
|
51
|
+
attributes.except('id', 'var', 'value', 'target_id', 'target_type', 'created_at', 'updated_at')
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def _get_value(name)
|
57
|
+
if value[name].nil?
|
58
|
+
_target_class.default_properties[var.to_sym][name]
|
59
|
+
else
|
60
|
+
value[name]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def _set_value(name, v)
|
65
|
+
if value[name] != v
|
66
|
+
value_will_change!
|
67
|
+
|
68
|
+
if v.nil?
|
69
|
+
value.delete(name)
|
70
|
+
else
|
71
|
+
value[name] = v
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def _target_class
|
77
|
+
target_type.constantize
|
78
|
+
end
|
79
|
+
|
80
|
+
def _property?(method_name)
|
81
|
+
_target_class.default_properties[var.to_sym].keys.include?(method_name.to_s)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module RailsProperties
|
2
|
+
module Scopes
|
3
|
+
def with_properties
|
4
|
+
result = joins("INNER JOIN properties ON #{properties_join_condition}")
|
5
|
+
|
6
|
+
if ActiveRecord::VERSION::MAJOR < 5
|
7
|
+
result.uniq
|
8
|
+
else
|
9
|
+
result.distinct
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def with_properties_for(var)
|
14
|
+
raise ArgumentError.new('Symbol expected!') unless var.is_a?(Symbol)
|
15
|
+
joins("INNER JOIN properties ON #{properties_join_condition} AND properties.var = '#{var}'")
|
16
|
+
end
|
17
|
+
|
18
|
+
def without_properties
|
19
|
+
joins("LEFT JOIN properties ON #{properties_join_condition}").
|
20
|
+
where('properties.id IS NULL')
|
21
|
+
end
|
22
|
+
|
23
|
+
def without_properties_for(var)
|
24
|
+
raise ArgumentError.new('Symbol expected!') unless var.is_a?(Symbol)
|
25
|
+
joins("LEFT JOIN properties ON #{properties_join_condition} AND properties.var = '#{var}'").
|
26
|
+
where('properties.id IS NULL')
|
27
|
+
end
|
28
|
+
|
29
|
+
def properties_join_condition
|
30
|
+
"properties.target_id = #{table_name}.#{primary_key} AND
|
31
|
+
properties.target_type = '#{base_class.name}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'rails-properties/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = 'rails-properties'
|
8
|
+
gem.version = RailsProperties::VERSION
|
9
|
+
gem.licenses = ['MIT']
|
10
|
+
gem.authors = ['Fletcher Fowler']
|
11
|
+
gem.email = ['fletch@fzf.me']
|
12
|
+
gem.description = %q{Properties gem for Ruby on Rails}
|
13
|
+
gem.summary = %q{Ruby gem to handle properties for ActiveRecord instances by storing them as serialized Hash in a separate database table. Namespaces and defaults included.}
|
14
|
+
gem.homepage = 'https://github.com/1debit/rails-properties'
|
15
|
+
gem.required_ruby_version = '>= 1.9.3'
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
19
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
20
|
+
gem.require_paths = ['lib']
|
21
|
+
|
22
|
+
gem.add_dependency 'activerecord', '>= 3.1'
|
23
|
+
|
24
|
+
gem.add_development_dependency 'rake'
|
25
|
+
gem.add_development_dependency 'sqlite3'
|
26
|
+
gem.add_development_dependency 'rspec'
|
27
|
+
gem.add_development_dependency 'coveralls'
|
28
|
+
gem.add_development_dependency 'simplecov', RUBY_VERSION < '2' ? '~> 0.11.2' : '>= 0.11.2'
|
29
|
+
end
|
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module RailsProperties
|
4
|
+
class Dummy
|
5
|
+
end
|
6
|
+
|
7
|
+
describe Configuration, 'successful' do
|
8
|
+
it "should define single key" do
|
9
|
+
Configuration.new(Dummy, :dashboard)
|
10
|
+
|
11
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {} })
|
12
|
+
expect(Dummy.property_object_class_name).to eq('RailsProperties::PropertyObject')
|
13
|
+
end
|
14
|
+
|
15
|
+
it "should define multiple keys" do
|
16
|
+
Configuration.new(Dummy, :dashboard, :calendar)
|
17
|
+
|
18
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {}, :calendar => {} })
|
19
|
+
expect(Dummy.property_object_class_name).to eq('RailsProperties::PropertyObject')
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should define single key with class_name" do
|
23
|
+
Configuration.new(Dummy, :dashboard, :class_name => 'MyClass')
|
24
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {} })
|
25
|
+
expect(Dummy.property_object_class_name).to eq('MyClass')
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should define multiple keys with class_name" do
|
29
|
+
Configuration.new(Dummy, :dashboard, :calendar, :class_name => 'MyClass')
|
30
|
+
|
31
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {}, :calendar => {} })
|
32
|
+
expect(Dummy.property_object_class_name).to eq('MyClass')
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should define using block" do
|
36
|
+
Configuration.new(Dummy) do |c|
|
37
|
+
c.key :dashboard
|
38
|
+
c.key :calendar
|
39
|
+
end
|
40
|
+
|
41
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {}, :calendar => {} })
|
42
|
+
expect(Dummy.property_object_class_name).to eq('RailsProperties::PropertyObject')
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should define using block with defaults" do
|
46
|
+
Configuration.new(Dummy) do |c|
|
47
|
+
c.key :dashboard, :defaults => { :theme => 'red' }
|
48
|
+
c.key :calendar, :defaults => { :scope => 'all' }
|
49
|
+
end
|
50
|
+
|
51
|
+
expect(Dummy.default_properties).to eq({ :dashboard => { 'theme' => 'red' }, :calendar => { 'scope' => 'all'} })
|
52
|
+
expect(Dummy.property_object_class_name).to eq('RailsProperties::PropertyObject')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should define using block and class_name" do
|
56
|
+
Configuration.new(Dummy, :class_name => 'MyClass') do |c|
|
57
|
+
c.key :dashboard
|
58
|
+
c.key :calendar
|
59
|
+
end
|
60
|
+
|
61
|
+
expect(Dummy.default_properties).to eq({ :dashboard => {}, :calendar => {} })
|
62
|
+
expect(Dummy.property_object_class_name).to eq('MyClass')
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe Configuration, 'failure' do
|
67
|
+
it "should fail without args" do
|
68
|
+
expect {
|
69
|
+
Configuration.new
|
70
|
+
}.to raise_error(ArgumentError)
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should fail without keys" do
|
74
|
+
expect {
|
75
|
+
Configuration.new(Dummy)
|
76
|
+
}.to raise_error(ArgumentError)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should fail without keys in block" do
|
80
|
+
expect {
|
81
|
+
Configuration.new(Dummy) do |c|
|
82
|
+
end
|
83
|
+
}.to raise_error(ArgumentError)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should fail with keys not being symbols" do
|
87
|
+
expect {
|
88
|
+
Configuration.new(Dummy, 42, "string")
|
89
|
+
}.to raise_error(ArgumentError)
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should fail with keys not being symbols" do
|
93
|
+
expect {
|
94
|
+
Configuration.new(Dummy) do |c|
|
95
|
+
c.key 42, "string"
|
96
|
+
end
|
97
|
+
}.to raise_error(ArgumentError)
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should fail with unknown option" do
|
101
|
+
expect {
|
102
|
+
Configuration.new(Dummy) do |c|
|
103
|
+
c.key :dashboard, :foo => {}
|
104
|
+
end
|
105
|
+
}.to raise_error(ArgumentError)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/spec/database.yml
ADDED
@@ -0,0 +1,248 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Defaults" do
|
4
|
+
it "should be stored for simple class" do
|
5
|
+
expect(Account.default_properties).to eq(:portal => {})
|
6
|
+
end
|
7
|
+
|
8
|
+
it "should be stored for parent class" do
|
9
|
+
expect(User.default_properties).to eq(:dashboard => { 'theme' => 'blue', 'view' => 'monthly', 'filter' => true },
|
10
|
+
:calendar => { 'scope' => 'company'})
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should be stored for child class" do
|
14
|
+
expect(GuestUser.default_properties).to eq(:dashboard => { 'theme' => 'red', 'view' => 'monthly', 'filter' => true })
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "Getter/Setter" do
|
19
|
+
let(:account) { Account.new :subdomain => 'foo' }
|
20
|
+
|
21
|
+
it "should handle method syntax" do
|
22
|
+
account.properties(:portal).enabled = true
|
23
|
+
account.properties(:portal).template = 'black'
|
24
|
+
|
25
|
+
expect(account.properties(:portal).enabled).to eq(true)
|
26
|
+
expect(account.properties(:portal).template).to eq('black')
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should return nil for not existing key" do
|
30
|
+
expect(account.properties(:portal).foo).to eq(nil)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe 'Objects' do
|
35
|
+
context 'without defaults' do
|
36
|
+
let(:account) { Account.new :subdomain => 'foo' }
|
37
|
+
|
38
|
+
it 'should have blank properties' do
|
39
|
+
expect(account.properties(:portal).value).to eq({})
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should allow saving a blank value' do
|
43
|
+
account.save!
|
44
|
+
expect(account.properties(:portal).save).to be_truthy
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should allow removing all values' do
|
48
|
+
account.properties(:portal).premium = true
|
49
|
+
account.properties(:portal).fee = 42.5
|
50
|
+
account.save!
|
51
|
+
|
52
|
+
account.properties(:portal).premium = nil
|
53
|
+
expect(account.save).to be_truthy
|
54
|
+
|
55
|
+
account.properties(:portal).fee = nil
|
56
|
+
expect(account.save).to be_truthy
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'should not add properties on saving' do
|
60
|
+
account.save!
|
61
|
+
expect(RailsProperties::PropertyObject.count).to eq(0)
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should save object with properties" do
|
65
|
+
account.properties(:portal).premium = true
|
66
|
+
account.properties(:portal).fee = 42.5
|
67
|
+
account.save!
|
68
|
+
|
69
|
+
account.reload
|
70
|
+
expect(account.properties(:portal).premium).to eq(true)
|
71
|
+
expect(account.properties(:portal).fee).to eq(42.5)
|
72
|
+
|
73
|
+
expect(RailsProperties::PropertyObject.count).to eq(1)
|
74
|
+
expect(RailsProperties::PropertyObject.first.value).to eq({ 'premium' => true, 'fee' => 42.5 })
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should save properties separated" do
|
78
|
+
account.save!
|
79
|
+
|
80
|
+
properties = account.properties(:portal)
|
81
|
+
properties.enabled = true
|
82
|
+
properties.template = 'black'
|
83
|
+
properties.save!
|
84
|
+
|
85
|
+
account.reload
|
86
|
+
expect(account.properties(:portal).enabled).to eq(true)
|
87
|
+
expect(account.properties(:portal).template).to eq('black')
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
context 'with defaults' do
|
92
|
+
let(:user) { User.new :name => 'Mr. Brown' }
|
93
|
+
|
94
|
+
it 'should have default properties' do
|
95
|
+
expect(user.properties(:dashboard).theme).to eq('blue')
|
96
|
+
expect(user.properties(:dashboard).view).to eq('monthly')
|
97
|
+
expect(user.properties(:dashboard).filter).to eq(true)
|
98
|
+
expect(user.properties(:calendar).scope).to eq('company')
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should have default properties after changing one' do
|
102
|
+
user.properties(:dashboard).theme = 'gray'
|
103
|
+
|
104
|
+
expect(user.properties(:dashboard).theme).to eq('gray')
|
105
|
+
expect(user.properties(:dashboard).view).to eq('monthly')
|
106
|
+
expect(user.properties(:dashboard).filter).to eq(true)
|
107
|
+
expect(user.properties(:calendar).scope).to eq('company')
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should overwrite properties" do
|
111
|
+
user.properties(:dashboard).theme = 'brown'
|
112
|
+
user.properties(:dashboard).filter = false
|
113
|
+
user.save!
|
114
|
+
|
115
|
+
user.reload
|
116
|
+
expect(user.properties(:dashboard).theme).to eq('brown')
|
117
|
+
expect(user.properties(:dashboard).filter).to eq(false)
|
118
|
+
expect(RailsProperties::PropertyObject.count).to eq(1)
|
119
|
+
expect(RailsProperties::PropertyObject.first.value).to eq({ 'theme' => 'brown', 'filter' => false })
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should merge properties with defaults" do
|
123
|
+
user.properties(:dashboard).theme = 'brown'
|
124
|
+
user.save!
|
125
|
+
|
126
|
+
user.reload
|
127
|
+
expect(user.properties(:dashboard).theme).to eq('brown')
|
128
|
+
expect(user.properties(:dashboard).filter).to eq(true)
|
129
|
+
expect(RailsProperties::PropertyObject.count).to eq(1)
|
130
|
+
expect(RailsProperties::PropertyObject.first.value).to eq({ 'theme' => 'brown' })
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
describe "Object without properties" do
|
136
|
+
let!(:user) { User.create! :name => 'Mr. White' }
|
137
|
+
|
138
|
+
it "should respond to #properties?" do
|
139
|
+
expect(user.properties?).to eq(false)
|
140
|
+
expect(user.properties?(:dashboard)).to eq(false)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should have no property objects" do
|
144
|
+
expect(RailsProperties::PropertyObject.count).to eq(0)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should add properties" do
|
148
|
+
user.properties(:dashboard).update_attributes! :smart => true
|
149
|
+
|
150
|
+
user.reload
|
151
|
+
expect(user.properties(:dashboard).smart).to eq(true)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should not save properties if assigned nil" do
|
155
|
+
expect {
|
156
|
+
user.properties = nil
|
157
|
+
user.save!
|
158
|
+
}.to_not change(RailsProperties::PropertyObject, :count)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
describe "Object with properties" do
|
163
|
+
let!(:user) do
|
164
|
+
User.create! :name => 'Mr. White' do |user|
|
165
|
+
user.properties(:dashboard).theme = 'white'
|
166
|
+
user.properties(:calendar).scope = 'all'
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
it "should respond to #properties?" do
|
171
|
+
expect(user.properties?).to eq(true)
|
172
|
+
|
173
|
+
expect(user.properties?(:dashboard)).to eq(true)
|
174
|
+
expect(user.properties?(:calendar)).to eq(true)
|
175
|
+
end
|
176
|
+
|
177
|
+
it "should have two property objects" do
|
178
|
+
expect(RailsProperties::PropertyObject.count).to eq(2)
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should update properties" do
|
182
|
+
user.properties(:dashboard).update_attributes! :smart => true
|
183
|
+
user.reload
|
184
|
+
|
185
|
+
expect(user.properties(:dashboard).smart).to eq(true)
|
186
|
+
expect(user.properties(:dashboard).theme).to eq('white')
|
187
|
+
expect(user.properties(:calendar).scope).to eq('all')
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should update properties by saving object" do
|
191
|
+
user.properties(:dashboard).smart = true
|
192
|
+
user.save!
|
193
|
+
|
194
|
+
user.reload
|
195
|
+
expect(user.properties(:dashboard).smart).to eq(true)
|
196
|
+
end
|
197
|
+
|
198
|
+
it "should destroy properties with nil" do
|
199
|
+
expect {
|
200
|
+
user.properties = nil
|
201
|
+
user.save!
|
202
|
+
}.to change(RailsProperties::PropertyObject, :count).by(-2)
|
203
|
+
|
204
|
+
expect(user.properties?).to eq(false)
|
205
|
+
end
|
206
|
+
|
207
|
+
it "should raise exception on assigning other than nil" do
|
208
|
+
expect {
|
209
|
+
user.properties = :foo
|
210
|
+
user.save!
|
211
|
+
}.to raise_error(ArgumentError)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
describe "Customized PropertyObject" do
|
216
|
+
let(:project) { Project.create! :name => 'Heist' }
|
217
|
+
|
218
|
+
it "should not accept invalid attributes" do
|
219
|
+
project.properties(:info).owner_name = 42
|
220
|
+
expect(project.properties(:info)).not_to be_valid
|
221
|
+
|
222
|
+
project.properties(:info).owner_name = ''
|
223
|
+
expect(project.properties(:info)).not_to be_valid
|
224
|
+
end
|
225
|
+
|
226
|
+
it "should accept valid attributes" do
|
227
|
+
project.properties(:info).owner_name = 'Mr. Brown'
|
228
|
+
expect(project.properties(:info)).to be_valid
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
describe "to_properties_hash" do
|
233
|
+
let(:user) do
|
234
|
+
User.new :name => 'Mrs. Fin' do |user|
|
235
|
+
user.properties(:dashboard).theme = 'green'
|
236
|
+
user.properties(:dashboard).sound = 11
|
237
|
+
user.properties(:calendar).scope = 'some'
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
it "should return defaults" do
|
242
|
+
expect(User.new.to_properties_hash).to eq({:dashboard=>{"theme"=>"blue", "view"=>"monthly", "filter"=>true}, :calendar=>{"scope"=>"company"}})
|
243
|
+
end
|
244
|
+
|
245
|
+
it "should return merged properties" do
|
246
|
+
expect(user.to_properties_hash).to eq({:dashboard=>{"theme"=>"green", "view"=>"monthly", "filter"=>true, "sound" => 11}, :calendar=>{"scope"=>"some"}})
|
247
|
+
end
|
248
|
+
end
|