preferencias 0.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.
@@ -0,0 +1,51 @@
1
+ module Preferencias
2
+ module PreferableClassMethods
3
+
4
+ def preference(name, type, *args)
5
+ options = args.extract_options!
6
+ options.assert_valid_keys(:default)
7
+ default = options[:default]
8
+ default = ->{ options[:default] } unless default.is_a?(Proc)
9
+
10
+ # cache_key will be nil for new objects, then if we check if there
11
+ # is a pending preference before going to default
12
+ define_method preference_getter_method(name) do
13
+ preferences.fetch(name) do
14
+ default.call
15
+ end
16
+ end
17
+
18
+ define_method preference_setter_method(name) do |value|
19
+ value = convert_preference_value(value, type)
20
+ preferences[name] = value
21
+
22
+ # If this is an activerecord object, we need to inform
23
+ # ActiveRecord::Dirty that this value has changed, since this is an
24
+ # in-place update to the preferences hash.
25
+ preferences_will_change! if respond_to?(:preferences_will_change!)
26
+ end
27
+
28
+ define_method preference_default_getter_method(name), &default
29
+
30
+ define_method preference_type_getter_method(name) do
31
+ type
32
+ end
33
+ end
34
+
35
+ def preference_getter_method(name)
36
+ "preferred_#{name}".to_sym
37
+ end
38
+
39
+ def preference_setter_method(name)
40
+ "preferred_#{name}=".to_sym
41
+ end
42
+
43
+ def preference_default_getter_method(name)
44
+ "preferred_#{name}_default".to_sym
45
+ end
46
+
47
+ def preference_type_getter_method(name)
48
+ "preferred_#{name}_type".to_sym
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,33 @@
1
+ module Preferencias
2
+ class ScopedStore
3
+ def initialize(prefix, suffix = nil)
4
+ @prefix = prefix
5
+ @suffix = suffix
6
+ end
7
+
8
+ def store
9
+ Preferencias::Store.instance
10
+ end
11
+
12
+ def fetch(key, &block)
13
+ store.fetch(key_for(key), &block)
14
+ end
15
+
16
+ def []=(key, value)
17
+ store[key_for(key)] = value
18
+ end
19
+
20
+ def delete(key)
21
+ store.delete(key_for(key))
22
+ end
23
+
24
+ private
25
+ def key_for(key)
26
+ [rails_cache_id, @prefix, key, @suffix].compact.join('/')
27
+ end
28
+
29
+ def rails_cache_id
30
+ ENV['RAILS_CACHE_ID']
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,98 @@
1
+ # Use singleton class Preferencias::Store.instance to access
2
+ #
3
+ # StoreInstance has a persistence flag that is on by default,
4
+ # but we disable database persistence in testing to speed up tests
5
+ #
6
+
7
+ require 'singleton'
8
+
9
+ module Preferencias
10
+
11
+ class StoreInstance
12
+ attr_accessor :persistence
13
+
14
+ def initialize
15
+ @cache = ::Rails.cache
16
+ @persistence = true
17
+ end
18
+
19
+ def set(key, value)
20
+ @cache.write(key, value)
21
+ persist(key, value)
22
+ end
23
+ alias_method :[]=, :set
24
+
25
+ def exist?(key)
26
+ @cache.exist?(key) ||
27
+ should_persist? && Preference.where(:key => key).exists?
28
+ end
29
+
30
+ def get(key)
31
+ # return the retrieved value, if it's in the cache
32
+ # use unless nil? incase the value is actually boolean false
33
+ #
34
+ unless (val = @cache.read(key)).nil?
35
+ return val
36
+ end
37
+
38
+ if should_persist?
39
+ # If it's not in the cache, maybe it's in the database, but
40
+ # has been cleared from the cache
41
+
42
+ # does it exist in the database?
43
+ if preference = Preference.find_by_key(key)
44
+ # it does exist
45
+ val = preference.value
46
+ else
47
+ # use the fallback value
48
+ val = yield
49
+ end
50
+
51
+ # Cache either the value from the db or the fallback value.
52
+ # This avoids hitting the db with subsequent queries.
53
+ @cache.write(key, val)
54
+
55
+ return val
56
+ else
57
+ yield
58
+ end
59
+ end
60
+ alias_method :fetch, :get
61
+
62
+ def delete(key)
63
+ @cache.delete(key)
64
+ destroy(key)
65
+ end
66
+
67
+ def clear_cache
68
+ @cache.clear
69
+ end
70
+
71
+ private
72
+
73
+ def persist(cache_key, value)
74
+ return unless should_persist?
75
+
76
+ preference = Preference.where(:key => cache_key).first_or_initialize
77
+ preference.value = value
78
+ preference.save
79
+ end
80
+
81
+ def destroy(cache_key)
82
+ return unless should_persist?
83
+
84
+ preference = Preference.find_by_key(cache_key)
85
+ preference.destroy if preference
86
+ end
87
+
88
+ def should_persist?
89
+ @persistence && Preference.table_exists?
90
+ end
91
+
92
+ end
93
+
94
+ class Store < StoreInstance
95
+ include Singleton
96
+ end
97
+
98
+ end
@@ -0,0 +1,3 @@
1
+ module Preferencias
2
+ VERSION = "0.0.1"
3
+ end
File without changes
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'preferencias/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "preferencias"
8
+ spec.version = Preferencias::VERSION
9
+ spec.authors = ["David Silva"]
10
+ spec.email = ["davidslv@gmail.com"]
11
+ spec.summary = %q{Extracted Preferences Model from Spree Ecommerce.}
12
+ spec.description = %q{It's an extraction of the Spree Ecommerce Preferences. All the credits goes to the Spree authors.}
13
+ spec.homepage = "https://github.com/Davidslv/preferences"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "railties", [">= 3.2", "< 4.2"]
22
+ spec.add_dependency "activesupport", [">= 3.2", "< 4.2"]
23
+ spec.add_dependency "activerecord", [">= 3.2", "< 4.2"]
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "rake", "~> 0"
27
+ spec.add_development_dependency "rspec", "3.1.3"
28
+ spec.add_development_dependency "sqlite3", "~> 0"
29
+ end
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+ describe Preferencias::Preference do
4
+
5
+ it "should require a key" do
6
+ preference = Preferencias::Preference.new
7
+ preference.key = :test
8
+ preference.value = true
9
+ expect(preference).to be_valid
10
+ end
11
+
12
+ describe "type coversion for values" do
13
+
14
+ def round_trip_preference(key, value = false)
15
+ p = Preferencias::Preference.new
16
+ p.value = value
17
+ p.key = key
18
+ p.save
19
+
20
+ Preferencias::Preference.find_by_key(key)
21
+ end
22
+
23
+ it ":boolean" do
24
+ value = true
25
+ key = "boolean_key"
26
+ pref = round_trip_preference(key, value)
27
+ expect(pref.value).to eq(value)
28
+ end
29
+
30
+ it "false :boolean" do
31
+ value = false
32
+ key = "boolean_key"
33
+ pref = round_trip_preference(key, value)
34
+ expect(pref.value).to eq(value)
35
+ end
36
+
37
+ it ":integer" do
38
+ value = 10
39
+ key = "integer_key"
40
+ pref = round_trip_preference(key, value)
41
+ expect(pref.value).to eq(value)
42
+ end
43
+
44
+ it ":decimal" do
45
+ value = 1.5
46
+ key = "decimal_key"
47
+ pref = round_trip_preference(key, value)
48
+ expect(pref.value).to eq(value)
49
+ end
50
+
51
+ it ":string" do
52
+ value = "This is a string"
53
+ key = "string_key"
54
+ pref = round_trip_preference(key, value)
55
+ expect(pref.value).to eq(value)
56
+ end
57
+
58
+ it ":text" do
59
+ value = "This is a string stored as text"
60
+ key = "text_key"
61
+ pref = round_trip_preference(key, value)
62
+ expect(pref.value).to eq(value)
63
+ end
64
+
65
+ it ":password" do
66
+ value = "This is a password"
67
+ key = "password_key"
68
+ pref = round_trip_preference(key, value)
69
+ expect(pref.value).to eq(value)
70
+ end
71
+
72
+ it ":any" do
73
+ value = [1, 2]
74
+ key = "any_key"
75
+ pref = round_trip_preference(key, value)
76
+ expect(pref.value).to eq(value)
77
+ end
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ describe Preferencias::Configuration do
4
+
5
+ before :all do
6
+ class AppConfig < Preferencias::Configuration
7
+ preference :color, :string, :default => :blue
8
+ end
9
+ @config = AppConfig.new
10
+ end
11
+
12
+ it "has named methods to access preferences" do
13
+ @config.color = 'orange'
14
+ expect(@config.color).to eq('orange')
15
+ end
16
+
17
+ it "uses [ ] to access preferences" do
18
+ @config[:color] = 'red'
19
+ expect( @config[:color] ).to eq('red')
20
+ end
21
+
22
+ it "uses set/get to access preferences" do
23
+ @config.set :color, 'green'
24
+ expect( @config.get(:color) ).to eq('green')
25
+ end
26
+
27
+ end
@@ -0,0 +1,275 @@
1
+ require 'spec_helper'
2
+
3
+ describe Preferencias::Preferable do
4
+
5
+ before :all do
6
+ class A
7
+ include Preferencias::Preferable
8
+ attr_reader :id
9
+
10
+ def initialize
11
+ @id = rand(999)
12
+ end
13
+
14
+ def preferences
15
+ @preferences ||= default_preferences
16
+ end
17
+
18
+ preference :color, :string, :default => 'green'
19
+ end
20
+
21
+ class B < A
22
+ preference :flavor, :string
23
+ end
24
+ end
25
+
26
+ before :each do
27
+ @a = A.new
28
+ allow(@a).to receive_messages(:persisted? => true)
29
+ @b = B.new
30
+ allow(@b).to receive_messages(:persisted? => true)
31
+
32
+ # ensure we're persisting as that is the default
33
+ #
34
+ store = Preferencias::Store.instance
35
+ store.persistence = true
36
+ end
37
+
38
+ describe "preference definitions" do
39
+ it "parent should not see child definitions" do
40
+ expect( @a.has_preference?(:color) ).to be_truthy
41
+ expect( @a.has_preference?(:flavor) ).to be_falsey
42
+ end
43
+
44
+ it "child should have parent and own definitions" do
45
+ expect( @b.has_preference?(:color) ).to be_truthy
46
+ expect( @b.has_preference?(:flavor) ).to be_truthy
47
+ end
48
+
49
+ it "instances have defaults" do
50
+ expect(@a.preferred_color).to eq('green')
51
+ expect(@b.preferred_color).to eq('green')
52
+ expect(@b.preferred_flavor).to be_nil
53
+ end
54
+
55
+ it "can be asked if it has a preference definition" do
56
+ expect( @a.has_preference?(:color) ).to be_truthy
57
+ expect( @b.has_preference?(:bad) ).to be_falsey
58
+ end
59
+
60
+ it "can be asked and raises" do
61
+ expect {
62
+ @a.has_preference!(:flavor)
63
+ }.to raise_error(NoMethodError, "flavor preference not defined")
64
+ end
65
+
66
+ it "has a type" do
67
+ expect(@a.preferred_color_type).to eq(:string)
68
+ expect( @a.preference_type(:color) ).to eq(:string)
69
+ end
70
+
71
+ it "has a default" do
72
+ expect(@a.preferred_color_default).to eq('green')
73
+ expect( @a.preference_default(:color) ).to eq('green')
74
+ end
75
+
76
+ it "raises if not defined" do
77
+ expect {
78
+ @a.get_preference(:flavor)
79
+ }.to raise_error(NoMethodError, "flavor preference not defined")
80
+ end
81
+
82
+ end
83
+
84
+ describe "preference access" do
85
+ it "handles ghost methods for preferences" do
86
+ @a.preferred_color = 'blue'
87
+ expect(@a.preferred_color).to eq('blue')
88
+ end
89
+
90
+ it "parent and child instances have their own prefs" do
91
+ @a.preferred_color = 'red'
92
+ @b.preferred_color = 'blue'
93
+
94
+ expect(@a.preferred_color).to eq('red')
95
+ expect(@b.preferred_color).to eq('blue')
96
+ end
97
+
98
+ it "raises when preference not defined" do
99
+ expect {
100
+ @a.set_preference(:bad, :bone)
101
+ }.to raise_exception(NoMethodError, "bad preference not defined")
102
+ end
103
+
104
+ it "builds a hash of preferences" do
105
+ @b.preferred_flavor = :strawberry
106
+ expect( @b.preferences[:flavor] ).to eq('strawberry')
107
+ expect( @b.preferences[:color] ).to eq('green') #default from A
108
+ end
109
+
110
+ it "builds a hash of preference defaults" do
111
+ expect(@b.default_preferences).to eq({
112
+ flavor: nil,
113
+ color: 'green'
114
+ })
115
+ end
116
+
117
+ context "converts integer preferences to integer values" do
118
+ before do
119
+ A.preference :is_integer, :integer
120
+ end
121
+
122
+ it "with strings" do
123
+ @a.set_preference(:is_integer, '3')
124
+ expect(@a.preferences[:is_integer]).to eq(3)
125
+
126
+ @a.set_preference(:is_integer, '')
127
+ expect(@a.preferences[:is_integer]).to eq(0)
128
+ end
129
+
130
+ end
131
+
132
+ context "converts decimal preferences to BigDecimal values" do
133
+ before do
134
+ A.preference :if_decimal, :decimal
135
+ end
136
+
137
+ it "returns a BigDecimal" do
138
+ @a.set_preference(:if_decimal, 3.3)
139
+ expect(@a.preferences[:if_decimal].class).to eql(BigDecimal)
140
+ end
141
+
142
+ it "with strings" do
143
+ @a.set_preference(:if_decimal, '3.3')
144
+ expect(@a.preferences[:if_decimal]).to eql(3.3)
145
+
146
+ @a.set_preference(:if_decimal, '')
147
+ expect(@a.preferences[:if_decimal]).to eql(0.0)
148
+ end
149
+ end
150
+
151
+ context "converts boolean preferences to boolean values" do
152
+ before do
153
+ A.preference :is_boolean, :boolean, :default => true
154
+ end
155
+
156
+ it "with strings" do
157
+ @a.set_preference(:is_boolean, '0')
158
+ expect(@a.preferences[:is_boolean]).to be_falsey
159
+ @a.set_preference(:is_boolean, 'f')
160
+ expect(@a.preferences[:is_boolean]).to be_falsey
161
+ @a.set_preference(:is_boolean, 't')
162
+ expect(@a.preferences[:is_boolean]).to be_truthy
163
+ end
164
+
165
+ it "with integers" do
166
+ @a.set_preference(:is_boolean, 0)
167
+ expect(@a.preferences[:is_boolean]).to be_falsey
168
+ @a.set_preference(:is_boolean, 1)
169
+ expect(@a.preferences[:is_boolean]).to be_truthy
170
+ end
171
+
172
+ it "with an empty string" do
173
+ @a.set_preference(:is_boolean, '')
174
+ expect(@a.preferences[:is_boolean]).to be_falsey
175
+ end
176
+
177
+ it "with an empty hash" do
178
+ @a.set_preference(:is_boolean, [])
179
+ expect(@a.preferences[:is_boolean]).to be_falsey
180
+ end
181
+ end
182
+
183
+ context "converts any preferences to any values" do
184
+ before do
185
+ A.preference :product_ids, :any, :default => []
186
+ A.preference :product_attributes, :any, :default => {}
187
+ end
188
+
189
+ it "with array" do
190
+ expect(@a.preferences[:product_ids]).to be_empty
191
+ @a.set_preference(:product_ids, [1, 2])
192
+ expect(@a.preferences[:product_ids]).to eq [1, 2]
193
+ end
194
+
195
+ it "with hash" do
196
+ expect(@a.preferences[:product_attributes]).to be_empty
197
+ @a.set_preference(:product_attributes, {:id => 1, :name => 2})
198
+ expect(@a.preferences[:product_attributes]).to include(:id => 1, :name => 2)
199
+ end
200
+ end
201
+
202
+ end
203
+
204
+ describe "persisted preferables" do
205
+ before(:all) do
206
+ class CreatePrefTest < ActiveRecord::Migration
207
+ def self.up
208
+ create_table :pref_tests do |t|
209
+ t.string :col
210
+ t.text :preferences
211
+ end
212
+ end
213
+
214
+ def self.down
215
+ drop_table :pref_tests
216
+ end
217
+ end
218
+
219
+ @migration_verbosity = ActiveRecord::Migration.verbose
220
+ ActiveRecord::Migration.verbose = false
221
+ CreatePrefTest.migrate(:up)
222
+
223
+ class PrefTest < Preferencias::Base
224
+ preference :pref_test_pref, :string, :default => 'abc'
225
+ preference :pref_test_any, :any, :default => []
226
+ end
227
+ end
228
+
229
+ after(:all) do
230
+ CreatePrefTest.migrate(:down)
231
+ ActiveRecord::Migration.verbose = @migration_verbosity
232
+ end
233
+
234
+ before(:each) do
235
+ @pt = PrefTest.create
236
+ end
237
+
238
+ describe "pending preferences for new activerecord objects" do
239
+ it "saves preferences after record is saved" do
240
+ pr = PrefTest.new
241
+ pr.set_preference(:pref_test_pref, 'XXX')
242
+ expect( pr.get_preference(:pref_test_pref) ).to eql('XXX')
243
+ pr.save!
244
+ expect( pr.get_preference(:pref_test_pref) ).to eql('XXX')
245
+ end
246
+
247
+ it "saves preferences for serialized object" do
248
+ pr = PrefTest.new
249
+ pr.set_preference(:pref_test_any, [1, 2])
250
+ expect( pr.get_preference(:pref_test_any) ).to eql [1, 2]
251
+ pr.save!
252
+ expect( pr.get_preference(:pref_test_any) ).to eql [1, 2]
253
+ end
254
+ end
255
+
256
+ it "clear preferences" do
257
+ @pt.set_preference(:pref_test_pref, 'xyz')
258
+ expect(@pt.preferred_pref_test_pref).to eql('xyz')
259
+ @pt.clear_preferences
260
+ expect(@pt.preferred_pref_test_pref).to eql('abc')
261
+ end
262
+
263
+ it "clear preferences when record is deleted" do
264
+ @pt.save!
265
+ @pt.preferred_pref_test_pref = 'lmn'
266
+ @pt.save!
267
+ @pt.destroy
268
+ @pt1 = PrefTest.new(:col => 'aaaa')
269
+ @pt1.id = @pt.id
270
+ @pt1.save!
271
+ expect( @pt1.get_preference(:pref_test_pref) ).to eql('abc')
272
+ end
273
+ end
274
+
275
+ end