optimeez_preferences 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'preferences'
@@ -0,0 +1,5 @@
1
+ Usage:
2
+
3
+ script/generate preferences
4
+
5
+ This will create a migration that will add the proper table to store preferences.
@@ -0,0 +1,17 @@
1
+ require 'rails/generators/migration'
2
+
3
+ class PreferencesGenerator < Rails::Generators::NamedBase
4
+ include Rails::Generators::Migration
5
+
6
+ def install_preferences
7
+ migration_template "001_create_preferences.rb", "db/migrate/create_preferences"
8
+ end
9
+
10
+ def self.next_migration_number(path)
11
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
12
+ end
13
+
14
+ def self.source_root
15
+ @_devise_source_root ||= File.expand_path("../templates", __FILE__)
16
+ end
17
+ end
@@ -0,0 +1,16 @@
1
+ class CreatePreferences < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :preferences do |t|
4
+ t.string :name, :null => false
5
+ t.references :owner, :polymorphic => true, :null => false
6
+ t.references :group, :polymorphic => true
7
+ t.string :value
8
+ t.timestamps
9
+ end
10
+ add_index :preferences, [:owner_id, :owner_type, :name, :group_id, :group_type], :unique => true, :name => 'index_preferences_on_owner_and_name_and_preference'
11
+ end
12
+
13
+ def self.down
14
+ drop_table :preferences
15
+ end
16
+ end
@@ -0,0 +1,619 @@
1
+ require 'preferences/preference_definition'
2
+
3
+ # Adds support for defining preferences on ActiveRecord models.
4
+ #
5
+ # == Saving preferences
6
+ #
7
+ # Preferences are not automatically saved when they are set. You must save
8
+ # the record that the preferences were set on.
9
+ #
10
+ # For example,
11
+ #
12
+ # class User < ActiveRecord::Base
13
+ # preference :notifications
14
+ # end
15
+ #
16
+ # u = User.new(:login => 'admin', :prefers_notifications => false)
17
+ # u.save!
18
+ #
19
+ # u = User.find_by_login('admin')
20
+ # u.attributes = {:prefers_notifications => true}
21
+ # u.save!
22
+ #
23
+ # == Validations
24
+ #
25
+ # Since the generated accessors for a preference allow the preference to be
26
+ # treated just like regular ActiveRecord attributes, they can also be
27
+ # validated against in the same way. For example,
28
+ #
29
+ # class User < ActiveRecord::Base
30
+ # preference :color, :string
31
+ #
32
+ # validates_presence_of :preferred_color
33
+ # validates_inclusion_of :preferred_color, :in => %w(red green blue)
34
+ # end
35
+ #
36
+ # u = User.new
37
+ # u.valid? # => false
38
+ # u.errors.on(:preferred_color) # => "can't be blank"
39
+ #
40
+ # u.preferred_color = 'white'
41
+ # u.valid? # => false
42
+ # u.errors.on(:preferred_color) # => "is not included in the list"
43
+ #
44
+ # u.preferred_color = 'red'
45
+ # u.valid? # => true
46
+ module Preferences
47
+ # Make sure your Gem loads the railtie.rb file if Rails is loaded first.
48
+ require 'preferences/engine' if defined?(Rails::Railtie)
49
+ module MacroMethods
50
+ # Defines a new preference for all records in the model. By default,
51
+ # preferences are assumed to have a boolean data type, so all values will
52
+ # be typecasted to true/false based on ActiveRecord rules.
53
+ #
54
+ # Configuration options:
55
+ # * <tt>:default</tt> - The default value for the preference. Default is nil.
56
+ # * <tt>:group_defaults</tt> - Defines the default values to use for various
57
+ # groups. This should map group_name -> defaults. For ActiveRecord groups,
58
+ # use the class name.
59
+ #
60
+ # == Examples
61
+ #
62
+ # The example below shows the various ways to define a preference for a
63
+ # particular model.
64
+ #
65
+ # class User < ActiveRecord::Base
66
+ # preference :notifications, :default => false
67
+ # preference :color, :string, :default => 'red', :group_defaults => {:car => 'black'}
68
+ # preference :favorite_number, :integer
69
+ # preference :data, :any # Allows any data type to be stored
70
+ # end
71
+ #
72
+ # All preferences are also inherited by subclasses.
73
+ #
74
+ # == Associations
75
+ #
76
+ # After the first preference is defined, the following associations are
77
+ # created for the model:
78
+ # * +stored_preferences+ - A collection of all the custom preferences
79
+ # specified for a record. This will not include default preferences
80
+ # unless they have been explicitly set.
81
+ #
82
+ # == Named scopes
83
+ #
84
+ # In addition to the above associations, the following named scopes get
85
+ # generated for the model:
86
+ # * +with_preferences+ - Finds all records with a given set of preferences
87
+ # * +without_preferences+ - Finds all records without a given set of preferences
88
+ #
89
+ # In addition to utilizing preferences stored in the database, each of the
90
+ # above scopes also take into account the defaults that have been defined
91
+ # for each preference.
92
+ #
93
+ # Example:
94
+ #
95
+ # User.with_preferences(:notifications => true)
96
+ # User.with_preferences(:notifications => true, :color => 'blue')
97
+ #
98
+ # # Searching with group preferences
99
+ # car = Car.find(:first)
100
+ # User.with_preferences(car => {:color => 'blue'})
101
+ # User.with_preferences(:notifications => true, car => {:color => 'blue'})
102
+ #
103
+ # == Generated accessors
104
+ #
105
+ # In addition to calling <tt>prefers?</tt> and +preferred+ on a record,
106
+ # you can also use the shortcut accessor methods that are generated when a
107
+ # preference is defined. For example,
108
+ #
109
+ # class User < ActiveRecord::Base
110
+ # preference :notifications
111
+ # end
112
+ #
113
+ # ...generates the following methods:
114
+ # * <tt>prefers_notifications?</tt> - Whether a value has been specified, i.e. <tt>record.prefers?(:notifications)</tt>
115
+ # * <tt>prefers_notifications</tt> - The actual value stored, i.e. <tt>record.prefers(:notifications)</tt>
116
+ # * <tt>prefers_notifications=(value)</tt> - Sets a new value, i.e. <tt>record.write_preference(:notifications, value)</tt>
117
+ # * <tt>prefers_notifications_changed?</tt> - Whether the preference has unsaved changes
118
+ # * <tt>prefers_notifications_was</tt> - The last saved value for the preference
119
+ # * <tt>prefers_notifications_change</tt> - A list of [original_value, new_value] if the preference has changed
120
+ # * <tt>prefers_notifications_will_change!</tt> - Forces the preference to get updated
121
+ # * <tt>reset_prefers_notifications!</tt> - Reverts any unsaved changes to the preference
122
+ #
123
+ # ...and the equivalent +preferred+ methods:
124
+ # * <tt>preferred_notifications?</tt>
125
+ # * <tt>preferred_notifications</tt>
126
+ # * <tt>preferred_notifications=(value)</tt>
127
+ # * <tt>preferred_notifications_changed?</tt>
128
+ # * <tt>preferred_notifications_was</tt>
129
+ # * <tt>preferred_notifications_change</tt>
130
+ # * <tt>preferred_notifications_will_change!</tt>
131
+ # * <tt>reset_preferred_notifications!</tt>
132
+ #
133
+ # Notice that there are two tenses used depending on the context of the
134
+ # preference. Conventionally, <tt>prefers_notifications?</tt> is better
135
+ # for accessing boolean preferences, while +preferred_color+ is better for
136
+ # accessing non-boolean preferences.
137
+ #
138
+ # Example:
139
+ #
140
+ # user = User.find(:first)
141
+ # user.prefers_notifications? # => false
142
+ # user.prefers_notifications # => false
143
+ # user.preferred_color? # => true
144
+ # user.preferred_color # => 'red'
145
+ # user.preferred_color = 'blue' # => 'blue'
146
+ #
147
+ # user.prefers_notifications = true
148
+ #
149
+ # car = Car.find(:first)
150
+ # user.preferred_color = 'red', car # => 'red'
151
+ # user.preferred_color(car) # => 'red'
152
+ # user.preferred_color?(car) # => true
153
+ #
154
+ # user.save! # => true
155
+ def preference(name, *args)
156
+ unless included_modules.include?(InstanceMethods)
157
+ class_inheritable_hash :preference_definitions
158
+ self.preference_definitions = {}
159
+
160
+ has_many :stored_preferences, :as => :owner, :class_name => 'Preference'
161
+
162
+ after_save :update_preferences
163
+
164
+ # Named scopes
165
+ scope :with_preferences, lambda {|preferences| build_preference_scope(preferences)}
166
+ scope :without_preferences, lambda {|preferences| build_preference_scope(preferences, true)}
167
+
168
+ extend Preferences::ClassMethods
169
+ include Preferences::InstanceMethods
170
+ end
171
+
172
+ # Create the definition
173
+ name = name.to_s
174
+ definition = PreferenceDefinition.new(name, *args)
175
+ self.preference_definitions[name] = definition
176
+
177
+ ######################################################## ALLEGER LA GEM PREFERENCE POUR ACCELERER LE CHARGEMENT DES MODELES #########
178
+ ######################################################## Gain de 70% (de 1,6s à 0,5s sur mac adrien avec 1 worker) #########
179
+ # # Create short-hand accessor methods, making sure that the name
180
+ # # is method-safe in terms of what characters are allowed
181
+ # name = name.gsub(/[^A-Za-z0-9_-]/, '').underscore
182
+ #
183
+ # # Query lookup
184
+ # define_method("preferred_#{name}?") do |*group|
185
+ # preferred?(name, group.first)
186
+ # end
187
+ # alias_method "prefers_#{name}?", "preferred_#{name}?"
188
+ #
189
+ # # Reader
190
+ # define_method("preferred_#{name}") do |*group|
191
+ # preferred(name, group.first)
192
+ # end
193
+ # alias_method "prefers_#{name}", "preferred_#{name}"
194
+ #
195
+ # # Writer
196
+ # define_method("preferred_#{name}=") do |*args|
197
+ # write_preference(*args.flatten.unshift(name))
198
+ # end
199
+ # alias_method "prefers_#{name}=", "preferred_#{name}="
200
+ #
201
+ # # Changes
202
+ # define_method("preferred_#{name}_changed?") do |*group|
203
+ # preference_changed?(name, group.first)
204
+ # end
205
+ # alias_method "prefers_#{name}_changed?", "preferred_#{name}_changed?"
206
+ #
207
+ # define_method("preferred_#{name}_was") do |*group|
208
+ # preference_was(name, group.first)
209
+ # end
210
+ # alias_method "prefers_#{name}_was", "preferred_#{name}_was"
211
+ #
212
+ # define_method("preferred_#{name}_change") do |*group|
213
+ # preference_change(name, group.first)
214
+ # end
215
+ # alias_method "prefers_#{name}_change", "preferred_#{name}_change"
216
+ #
217
+ # define_method("preferred_#{name}_will_change!") do |*group|
218
+ # preference_will_change!(name, group.first)
219
+ # end
220
+ # alias_method "prefers_#{name}_will_change!", "preferred_#{name}_will_change!"
221
+ #
222
+ # define_method("reset_preferred_#{name}!") do |*group|
223
+ # reset_preference!(name, group.first)
224
+ # end
225
+ # alias_method "reset_prefers_#{name}!", "reset_preferred_#{name}!"
226
+ #########################################################################################
227
+ definition
228
+ end
229
+ end
230
+
231
+ module ClassMethods #:nodoc:
232
+ # Generates the scope for looking under records with a specific set of
233
+ # preferences associated with them.
234
+ #
235
+ # Note thate this is a bit more complicated than usual since the preference
236
+ # definitions aren't in the database for joins, defaults need to be accounted
237
+ # for, and querying for the the presence of multiple preferences requires
238
+ # multiple joins.
239
+ def build_preference_scope(preferences, inverse = false)
240
+ joins = []
241
+ statements = []
242
+ values = []
243
+
244
+ # Flatten the preferences for easier processing
245
+ preferences = preferences.inject({}) do |result, (group, value)|
246
+ if value.is_a?(Hash)
247
+ value.each {|preference, value| result[[group, preference]] = value}
248
+ else
249
+ result[[nil, group]] = value
250
+ end
251
+ result
252
+ end
253
+
254
+ preferences.each do |(group, preference), value|
255
+ group_id, group_type = Preference.split_group(group)
256
+ preference = preference.to_s
257
+ definition = preference_definitions[preference.to_s]
258
+ value = definition.type_cast(value)
259
+ is_default = definition.default_value(group_type) == value
260
+
261
+ table = "preferences_#{group_id}_#{group_type}_#{preference}"
262
+
263
+ # Since each preference is a different record, they need their own
264
+ # join so that the proper conditions can be set
265
+ joins << "LEFT JOIN preferences AS #{table} ON #{table}.owner_id = #{table_name}.#{primary_key} AND " + sanitize_sql(
266
+ "#{table}.owner_type" => base_class.name.to_s,
267
+ "#{table}.group_id" => group_id,
268
+ "#{table}.group_type" => group_type,
269
+ "#{table}.name" => preference
270
+ )
271
+
272
+ if inverse
273
+ statements << "#{table}.id IS NOT NULL AND #{table}.value " + (value.nil? ? ' IS NOT NULL' : ' != ?') + (!is_default ? " OR #{table}.id IS NULL" : '')
274
+ else
275
+ statements << "#{table}.id IS NOT NULL AND #{table}.value " + (value.nil? ? ' IS NULL' : ' = ?') + (is_default ? " OR #{table}.id IS NULL" : '')
276
+ end
277
+ values << value unless value.nil?
278
+ end
279
+
280
+ sql = statements.map! {|statement| "(#{statement})"} * ' AND '
281
+ {:joins => joins, :conditions => values.unshift(sql)}
282
+ end
283
+ end
284
+
285
+ module InstanceMethods
286
+ def self.included(base) #:nodoc:
287
+ base.class_eval do
288
+ alias_method :prefs, :preferences
289
+ end
290
+ end
291
+
292
+ # Finds all preferences, including defaults, for the current record. If
293
+ # looking up custom group preferences, then this will include all default
294
+ # preferences within that particular group as well.
295
+ #
296
+ # == Examples
297
+ #
298
+ # A user with no stored values:
299
+ #
300
+ # user = User.find(:first)
301
+ # user.preferences
302
+ # => {"language"=>"English", "color"=>nil}
303
+ #
304
+ # A user with stored values for a particular group:
305
+ #
306
+ # user.preferred_color = 'red', :cars
307
+ # user.preferences(:cars)
308
+ # => {"language=>"English", "color"=>"red"}
309
+ def preferences(group = nil)
310
+ preferences = preferences_group(group)
311
+
312
+ unless preferences_group_loaded?(group)
313
+ group_id, group_type = Preference.split_group(group)
314
+ find_preferences(:group_id => group_id, :group_type => group_type).each do |preference|
315
+ preferences[preference.name] = preference.value unless preferences.include?(preference.name)
316
+ end
317
+
318
+ # Add defaults
319
+ preference_definitions.each do |name, definition|
320
+ preferences[name] = definition.default_value(group_type) unless preferences.include?(name)
321
+ end
322
+ end
323
+
324
+ preferences.inject({}) do |typed_preferences, (name, value)|
325
+ typed_preferences[name] = value.nil? ? value : preference_definitions[name].type_cast(value)
326
+ typed_preferences
327
+ end
328
+ end
329
+
330
+ # Queries whether or not a value is present for the given preference.
331
+ # This is dependent on how the value is type-casted.
332
+ #
333
+ # == Examples
334
+ #
335
+ # class User < ActiveRecord::Base
336
+ # preference :color, :string, :default => 'red'
337
+ # end
338
+ #
339
+ # user = User.create
340
+ # user.preferred(:color) # => "red"
341
+ # user.preferred?(:color) # => true
342
+ # user.preferred?(:color, 'cars') # => true
343
+ # user.preferred?(:color, Car.first) # => true
344
+ #
345
+ # user.write_preference(:color, nil)
346
+ # user.preferred(:color) # => nil
347
+ # user.preferred?(:color) # => false
348
+ def preferred?(name, group = nil)
349
+ name = name.to_s
350
+ assert_valid_preference(name)
351
+
352
+ value = preferred(name, group)
353
+ preference_definitions[name].query(value)
354
+ end
355
+ alias_method :prefers?, :preferred?
356
+
357
+ # Gets the actual value stored for the given preference, or the default
358
+ # value if nothing is present.
359
+ #
360
+ # == Examples
361
+ #
362
+ # class User < ActiveRecord::Base
363
+ # preference :color, :string, :default => 'red'
364
+ # end
365
+ #
366
+ # user = User.create
367
+ # user.preferred(:color) # => "red"
368
+ # user.preferred(:color, 'cars') # => "red"
369
+ # user.preferred(:color, Car.first) # => "red"
370
+ #
371
+ # user.write_preference(:color, 'blue')
372
+ # user.preferred(:color) # => "blue"
373
+ def preferred(name, group = nil)
374
+ name = name.to_s
375
+ assert_valid_preference(name)
376
+
377
+ if preferences_group(group).include?(name)
378
+ # Value for this group/name has been written, but not saved yet:
379
+ # grab from the pending values
380
+ value = preferences_group(group)[name]
381
+ else
382
+ # Grab the first preference; if it doesn't exist, use the default value
383
+ group_id, group_type = Preference.split_group(group)
384
+ preference = find_preferences(:name => name, :group_id => group_id, :group_type => group_type).first unless preferences_group_loaded?(group)
385
+
386
+ value = preference ? preference.value : preference_definitions[name].default_value(group_type)
387
+ preferences_group(group)[name] = value
388
+ end
389
+
390
+ definition = preference_definitions[name]
391
+ value = definition.type_cast(value) unless value.nil?
392
+ value
393
+ end
394
+ alias_method :prefers, :preferred
395
+
396
+ # Sets a new value for the given preference. The actual Preference record
397
+ # is *not* created until this record is saved. In this way, preferences
398
+ # act *exactly* the same as attributes. They can be written to and
399
+ # validated against, but won't actually be written to the database until
400
+ # the record is saved.
401
+ #
402
+ # == Examples
403
+ #
404
+ # user = User.find(:first)
405
+ # user.write_preference(:color, 'red') # => "red"
406
+ # user.save!
407
+ #
408
+ # user.write_preference(:color, 'blue', Car.first) # => "blue"
409
+ # user.save!
410
+ def write_preference(name, value, group = nil)
411
+ name = name.to_s
412
+ assert_valid_preference(name)
413
+
414
+ preferences_changed = preferences_changed_group(group)
415
+ if preferences_changed.include?(name)
416
+ old = preferences_changed[name]
417
+ preferences_changed.delete(name) unless preference_value_changed?(name, old, value)
418
+ else
419
+ old = clone_preference_value(name, group)
420
+ preferences_changed[name] = old if preference_value_changed?(name, old, value)
421
+ end
422
+
423
+ value = convert_number_column_value(value) if preference_definitions[name].number?
424
+ preferences_group(group)[name] = value
425
+
426
+ value
427
+ end
428
+
429
+ # Whether any attributes have unsaved changes.
430
+ #
431
+ # == Examples
432
+ #
433
+ # user = User.find(:first)
434
+ # user.preferences_changed? # => false
435
+ # user.write_preference(:color, 'red')
436
+ # user.preferences_changed? # => true
437
+ # user.save
438
+ # user.preferences_changed? # => false
439
+ #
440
+ # # Groups
441
+ # user.preferences_changed?(:car) # => false
442
+ # user.write_preference(:color, 'red', :car)
443
+ # user.preferences_changed(:car) # => true
444
+ def preferences_changed?(group = nil)
445
+ !preferences_changed_group(group).empty?
446
+ end
447
+
448
+ # A list of the preferences that have unsaved changes.
449
+ #
450
+ # == Examples
451
+ #
452
+ # user = User.find(:first)
453
+ # user.preferences_changed # => []
454
+ # user.write_preference(:color, 'red')
455
+ # user.preferences_changed # => ["color"]
456
+ # user.save
457
+ # user.preferences_changed # => []
458
+ #
459
+ # # Groups
460
+ # user.preferences_changed(:car) # => []
461
+ # user.write_preference(:color, 'red', :car)
462
+ # user.preferences_changed(:car) # => ["color"]
463
+ def preferences_changed(group = nil)
464
+ preferences_changed_group(group).keys
465
+ end
466
+
467
+ # A map of the preferences that have changed in the current object.
468
+ #
469
+ # == Examples
470
+ #
471
+ # user = User.find(:first)
472
+ # user.preferred(:color) # => nil
473
+ # user.preference_changes # => {}
474
+ #
475
+ # user.write_preference(:color, 'red')
476
+ # user.preference_changes # => {"color" => [nil, "red"]}
477
+ # user.save
478
+ # user.preference_changes # => {}
479
+ #
480
+ # # Groups
481
+ # user.preferred(:color, :car) # => nil
482
+ # user.preference_changes(:car) # => {}
483
+ # user.write_preference(:color, 'red', :car)
484
+ # user.preference_changes(:car) # => {"color" => [nil, "red"]}
485
+ def preference_changes(group = nil)
486
+ preferences_changed(group).inject({}) do |changes, preference|
487
+ changes[preference] = preference_change(preference, group)
488
+ changes
489
+ end
490
+ end
491
+
492
+ # Reloads the pereferences of this object as well as its attributes
493
+ def reload(*args) #:nodoc:
494
+ result = super
495
+
496
+ @preferences.clear if @preferences
497
+ @preferences_changed.clear if @preferences_changed
498
+
499
+ result
500
+ end
501
+
502
+ private
503
+ # Asserts that the given name is a valid preference in this model. If it
504
+ # is not, then an ArgumentError exception is raised.
505
+ def assert_valid_preference(name)
506
+ raise(ArgumentError, "Unknown preference: #{name}") unless preference_definitions.include?(name)
507
+ end
508
+
509
+ # Gets the set of preferences identified by the given group
510
+ def preferences_group(group)
511
+ @preferences ||= {}
512
+ @preferences[group.is_a?(Symbol) ? group.to_s : group] ||= {}
513
+ end
514
+
515
+ # Determines whether the given group of preferences has already been
516
+ # loaded from the database
517
+ def preferences_group_loaded?(group)
518
+ preference_definitions.length == preferences_group(group).length
519
+ end
520
+
521
+ # Generates a clone of the current value stored for the preference with
522
+ # the given name / group
523
+ def clone_preference_value(name, group)
524
+ value = preferred(name, group)
525
+ value.duplicable? ? value.clone : value
526
+ rescue TypeError, NoMethodError
527
+ value
528
+ end
529
+
530
+ # Keeps track of all preferences that have been changed so that they can
531
+ # be properly updated in the database. Maps group -> preference -> value.
532
+ def preferences_changed_group(group)
533
+ @preferences_changed ||= {}
534
+ @preferences_changed[group.is_a?(Symbol) ? group.to_s : group] ||= {}
535
+ end
536
+
537
+ # Determines whether a preference changed in the given group
538
+ def preference_changed?(name, group)
539
+ preferences_changed_group(group).include?(name)
540
+ end
541
+
542
+ # Builds an array of [original_value, new_value] for the given preference.
543
+ # If the perference did not change, this will return nil.
544
+ def preference_change(name, group)
545
+ [preferences_changed_group(group)[name], preferred(name, group)] if preference_changed?(name, group)
546
+ end
547
+
548
+ # Gets the last saved value for the given preference
549
+ def preference_was(name, group)
550
+ preference_changed?(name, group) ? preferences_changed_group(group)[name] : preferred(name, group)
551
+ end
552
+
553
+ # Forces the given preference to be saved regardless of whether the value
554
+ # is actually diferent
555
+ def preference_will_change!(name, group)
556
+ preferences_changed_group(group)[name] = clone_preference_value(name, group)
557
+ end
558
+
559
+ # Reverts any unsaved changes to the given preference
560
+ def reset_preference!(name, group)
561
+ write_preference(name, preferences_changed_group(group)[name], group) if preference_changed?(name, group)
562
+ end
563
+
564
+ # Determines whether the old value is different from the new value for the
565
+ # given preference. This will use the typecasted value to determine
566
+ # equality.
567
+ def preference_value_changed?(name, old, value)
568
+ definition = preference_definitions[name]
569
+ if definition.type == :integer && (old.nil? || old == 0)
570
+ # For nullable numeric columns, NULL gets stored in database for blank (i.e. '') values.
571
+ # Hence we don't record it as a change if the value changes from nil to ''.
572
+ # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
573
+ # be typecast back to 0 (''.to_i => 0)
574
+ value = nil if value.blank?
575
+ else
576
+ value = definition.type_cast(value)
577
+ end
578
+
579
+ old != value
580
+ end
581
+
582
+ # Updates any preferences that have been changed/added since the record
583
+ # was last saved
584
+ def update_preferences
585
+ if @preferences_changed
586
+ @preferences_changed.each do |group, preferences|
587
+ group_id, group_type = Preference.split_group(group)
588
+
589
+ preferences.keys.each do |name|
590
+ # Find an existing preference or build a new one
591
+ attributes = {:name => name, :group_id => group_id, :group_type => group_type}
592
+ preference = find_preferences(attributes).first || stored_preferences.build(attributes)
593
+ preference.value = preferred(name, group)
594
+ preference.save!
595
+ end
596
+ end
597
+
598
+ @preferences_changed.clear
599
+ end
600
+ end
601
+
602
+ # Finds all stored preferences with the given attributes. This will do a
603
+ # smart lookup by looking at the in-memory collection if it was eager-
604
+ # loaded.
605
+ def find_preferences(attributes)
606
+ if stored_preferences.loaded?
607
+ stored_preferences.select do |preference|
608
+ attributes.all? {|attribute, value| preference[attribute] == value}
609
+ end
610
+ else
611
+ stored_preferences.find(:all, :conditions => attributes)
612
+ end
613
+ end
614
+ end
615
+ end
616
+
617
+ ActiveRecord::Base.class_eval do
618
+ extend Preferences::MacroMethods
619
+ end