optimeez_preferences 0.4.2

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/CHANGELOG.rdoc ADDED
@@ -0,0 +1,82 @@
1
+ == master
2
+
3
+ == 0.4.2 / 2010-04-17
4
+
5
+ * Fix #preferences lookup not typecasting values
6
+
7
+ == 0.4.1 / 2010-03-07
8
+
9
+ * Add support for per-group default preferences
10
+ * Fix unsaved boolean preferences getting overridden by defaults if value is false
11
+
12
+ == 0.4.0 / 2010-03-07
13
+
14
+ * Add {preference}_changed?, {preference}_was, {preference}_changed, {preference}_will_change!, and reset_{preference}!
15
+ * Add #preferences_changed?, #preferences_changed, and #preference_changes
16
+ * Fix preferences that are reverted externally still getting stored
17
+ * Fix preference definition types not being used to typecast values
18
+ * No longer allow both group and non-group preferences to be looked up at once (except for named scopes)
19
+ * Add support for using Symbols to reference groups
20
+ * Fix #reload not causing unsaved preferences to get reset
21
+ * Raise exception if unknown preference is accessed
22
+ * Rename #set_preference to #write_preference
23
+ * Add caching of preference lookups
24
+ * Fix preferences being stored even if they didn't change
25
+ * Release gems via rake-gemcutter instead of rubyforge
26
+ * Add a generator for db migration to make installation a bit easier [Tim Lowrimore]
27
+ * Add named scopes: #with_preferences and #without_preferences
28
+
29
+ == 0.3.1 / 2009-04-25
30
+
31
+ * Rename Preference#attribute to #name to avoid conflicts with reserved methods in ActiveRecord
32
+
33
+ == 0.3.0 / 2009-04-13
34
+
35
+ * Add dependency on Rails 2.3
36
+ * Remove dependency on plugins_plus
37
+
38
+ == 0.2.0 / 2008-12-14
39
+
40
+ * Remove the PluginAWeek namespace
41
+
42
+ == 0.1.5 / 2008-11-16
43
+
44
+ * Add all prefers/preferred accessors for preferences to be analogous to ActiveRecord column accessors
45
+ * Fix preferences defined in STI subclasses not working [Quinn Shanahan]
46
+
47
+ == 0.1.4 / 2008-10-26
48
+
49
+ * Change how the base module is included to prevent namespacing conflicts
50
+
51
+ == 0.1.3 / 2008-06-29
52
+
53
+ * Add +prefs+ as an alias for +preferences+
54
+ * Fix +preferences+ not properly selecting preferences when a group is specified
55
+ * Improve test coverage
56
+
57
+ == 0.1.2 / 2008-06-22
58
+
59
+ * Remove log files from gems
60
+
61
+ == 0.1.1 / 2008-06-20
62
+
63
+ * Rename preference_values hash to preferences
64
+ * Rename preferences association to stored_preferences
65
+
66
+ == 0.1.0 / 2008-06-19
67
+
68
+ * Avoid string evaluation for dynamic methods
69
+ * Return hashes for the preference_values, e.g.
70
+
71
+ user.preference_values # => {'color' => 'red', 'number' => 11, 'website' => {'background' => 'white', 'foreground' => 'black'}}
72
+ user.preference_values('website') # => {'background' => 'white', 'foreground' => 'black'}
73
+
74
+ * Add more generic grouping of preferences than with just other records, e.g.
75
+
76
+ user.preferred_color('cars')
77
+
78
+ * Remove support for an options hash when specifying :for associations for preference
79
+
80
+ == 0.0.1 / 2008-05-10
81
+
82
+ * Initial public release
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008-2009 Aaron Pfeifer
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,216 @@
1
+ == preferences
2
+
3
+ +preferences+ adds support for easily creating custom preferences for models.
4
+
5
+ == Resources
6
+
7
+ API
8
+
9
+ * http://rdoc.info/projects/pluginaweek/preferences
10
+
11
+ Bugs
12
+
13
+ * http://pluginaweek.lighthouseapp.com/projects/13286-preferences
14
+
15
+ Development
16
+
17
+ * http://github.com/pluginaweek/preferences
18
+
19
+ Source
20
+
21
+ * git://github.com/pluginaweek/preferences.git
22
+
23
+ == Description
24
+
25
+ Preferences for models within an application, such as for users, is a pretty
26
+ common idiom. Although the rule of thumb is to keep the number of preferences
27
+ available to a minimum, sometimes it's necessary if you want users to be able to
28
+ disable things like e-mail notifications.
29
+
30
+ Generally, basic preferences can be accomplished through simple designs, such as
31
+ additional columns or a bit vector described and implemented by preference_fu[http://agilewebdevelopment.com/plugins/preferencefu].
32
+ However, as you find the need for non-binary preferences and the number of
33
+ preferences becomes unmanageable as individual columns in the database, the next
34
+ step is often to create a separate "preferences" table. This is where the
35
+ +preferences+ plugin comes in.
36
+
37
+ +preferences+ encapsulates this design by exposing preferences using simple
38
+ attribute accessors on the model, hiding the fact that preferences are stored in
39
+ a separate table and making it dead-simple to define and manage preferences.
40
+
41
+ == Usage
42
+
43
+ === Installation
44
+
45
+ +preferences+ requires an additional database table to work. You can generate
46
+ a migration for this table like so:
47
+
48
+ script/generate preferences
49
+
50
+ Then simply migrate your database:
51
+
52
+ rake db:migrate
53
+
54
+ === Defining preferences
55
+
56
+ To define the preferences for a model, you can do so right within the model:
57
+
58
+ class User < ActiveRecord::Base
59
+ preference :hot_salsa
60
+ preference :dark_chocolate, :default => true
61
+ preference :color, :string
62
+ preference :favorite_number
63
+ preference :language, :string, :default => 'English', :group_defaults => {:chat => 'Spanish'}
64
+ end
65
+
66
+ In the above model, 5 preferences have been defined:
67
+ * hot_salsa
68
+ * dark_chocolate
69
+ * color
70
+ * favorite_number
71
+ * language
72
+
73
+ For each preference, a data type and default value can be specified. If no
74
+ data type is given, it's assumed to be a boolean value. If no default value is
75
+ given, the default is assumed to be nil.
76
+
77
+ === Accessing preferences
78
+
79
+ Once preferences have been defined for a model, they can be accessed either
80
+ using the accessor methods that are generated for each preference or the generic
81
+ methods that are not specific to a particular preference.
82
+
83
+ ==== Accessors
84
+
85
+ There are several shortcut methods that are generated for each preference
86
+ defined on a model. These reflect the same set of methods (attribute accessors)
87
+ that are generated for a model's columns. Examples of these are shown below:
88
+
89
+ Query methods:
90
+ user.prefers_hot_salsa? # => false
91
+ user.preferred_language? # => true
92
+
93
+ Reader methods:
94
+ user.prefers_hot_salsa # => false
95
+ user.preferred_language # => "English"
96
+
97
+ Writer methods:
98
+ user.prefers_hot_salsa = false # => false
99
+ user.preferred_language = 'English' # => "English"
100
+
101
+ ==== Generic methods
102
+
103
+ Each preference accessor is essentially a wrapper for the various generic methods
104
+ shown below:
105
+
106
+ Query method:
107
+ user.prefers?(:hot_salsa) # => false
108
+ user.preferred?(:language) # => true
109
+
110
+ Reader method:
111
+ user.prefers(:hot_salsa) # => false
112
+ user.preferred(:language) # => "English"
113
+
114
+ Write method:
115
+ user.write_preference(:hot_salsa, false) # => false
116
+ user.write_preference(:language, "English") # => "English"
117
+
118
+ === Accessing all preferences
119
+
120
+ To get the collection of all custom, stored preferences for a particular record,
121
+ you can access the +stored_preferences+ has_many association which is automatically
122
+ generated:
123
+
124
+ user.stored_preferences
125
+
126
+ In addition to this, you can get a hash of all stored preferences *and* default
127
+ preferences, by accessing the +preferences+ helper:
128
+
129
+ user.preferences # => {"language"=>"English", "color"=>nil}
130
+
131
+ This hash will contain the value for every preference that has been defined for
132
+ the model, whether that's the default value or one that has been previously
133
+ stored.
134
+
135
+ A short-hand alternative for preferences is also available:
136
+
137
+ user.prefs # => {"language"=>"English", "color"=>nil}
138
+
139
+ === Grouping preferences
140
+
141
+ In addition to defining generic preferences for the owning record, you can also
142
+ group preferences by ActiveRecord objects or arbitrary names. This is best shown
143
+ through an example:
144
+
145
+ user = User.find(:first)
146
+ car = Car.find(:first)
147
+
148
+ user.preferred_color = 'red', car
149
+ # user.write_preference(:color, 'red', car) # The generic way
150
+
151
+ This will create a color preference of "red" for the given car. In this way,
152
+ you can have "color" preferences for different records.
153
+
154
+ To access the preference for a particular record, you can use the same accessor
155
+ methods as before:
156
+
157
+ user.preferred_color(car)
158
+ # user.preferred(:color, car) # The generic way
159
+
160
+ In addition to grouping preferences for a particular record, you can also group
161
+ preferences by name. For example,
162
+
163
+ user = User.find(:first)
164
+
165
+ user.preferred_color = 'red', :automobiles
166
+ user.preferred_color = 'tan', :clothing
167
+
168
+ user.preferred_color(:automobiles) # => "red"
169
+ user.preferred_color(:clothing) # => "tan"
170
+
171
+ user.preferences(:automobiles) # => {"color"=>"red"}
172
+
173
+ === Saving preferences
174
+
175
+ Note that preferences are not saved until the owning record is saved.
176
+ Preferences are treated in a similar fashion to attributes. For example,
177
+
178
+ user = user.find(:first)
179
+ user.attributes = {:prefers_hot_salsa => false, :preferred_color => 'red'}
180
+ user.save!
181
+
182
+ Preferences are stored in a separate table called "preferences".
183
+
184
+ === Tracking changes
185
+
186
+ Similar to ActiveRecord attributes, unsaved changes to preferences can be
187
+ tracked. For example,
188
+
189
+ user.preferred_language # => "English"
190
+ user.preferred_language_changed? # => false
191
+ user.preferred_language = 'Spanish'
192
+ user.preferred_language_changed? # => true
193
+ user.preferred_language_was # => "English"
194
+ user.preferred_language_change # => ["English", "Spanish"]
195
+ user.reset_preferred_language!
196
+ user.preferred_language # => "English"
197
+
198
+ Assigning the same value leaves the preference unchanged:
199
+
200
+ user.preferred_language # => "English"
201
+ user.preferred_language = 'English'
202
+ user.preferred_language_changed? # => false
203
+ user.preferred_language_change # => nil
204
+
205
+ == Testing
206
+
207
+ Before you can run any tests, the following gem must be installed:
208
+ * plugin_test_helper[http://github.com/pluginaweek/plugin_test_helper]
209
+
210
+ To run against a specific version of Rails:
211
+
212
+ rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
213
+
214
+ == Dependencies
215
+
216
+ * Rails 2.3 or later
data/Rakefile ADDED
@@ -0,0 +1,75 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+ require 'rake/testtask'
4
+ require 'rake/rdoctask'
5
+ require 'rake/gempackagetask'
6
+
7
+ spec = Gem::Specification.new do |s|
8
+ s.name = 'preferences'
9
+ s.version = '0.4.2'
10
+ s.platform = Gem::Platform::RUBY
11
+ s.summary = 'Adds support for easily creating custom preferences for ActiveRecord models'
12
+ s.description = s.summary
13
+
14
+ s.files = FileList['{app,generators,lib,test}/**/*'] + %w(CHANGELOG.rdoc init.rb LICENSE Rakefile README.rdoc) - FileList['test/app_root/{log,log/*,script,script/*}']
15
+ s.require_path = 'lib'
16
+ s.has_rdoc = true
17
+ s.test_files = Dir['test/**/*_test.rb']
18
+
19
+ s.author = 'Aaron Pfeifer'
20
+ s.email = 'aaron@pluginaweek.org'
21
+ s.homepage = 'http://www.pluginaweek.org'
22
+ s.rubyforge_project = 'pluginaweek'
23
+ end
24
+
25
+ desc 'Default: run all tests.'
26
+ task :default => :test
27
+
28
+ desc "Test the #{spec.name} plugin."
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.test_files = spec.test_files
32
+ t.verbose = true
33
+ end
34
+
35
+ begin
36
+ require 'rcov/rcovtask'
37
+ namespace :test do
38
+ desc "Test the #{spec.name} plugin with Rcov."
39
+ Rcov::RcovTask.new(:rcov) do |t|
40
+ t.libs << 'lib'
41
+ t.test_files = spec.test_files
42
+ t.rcov_opts << '--exclude="^(?!lib/|app/)"'
43
+ t.verbose = true
44
+ end
45
+ end
46
+ rescue LoadError
47
+ end
48
+
49
+ desc "Generate documentation for the #{spec.name} plugin."
50
+ Rake::RDocTask.new(:rdoc) do |rdoc|
51
+ rdoc.rdoc_dir = 'rdoc'
52
+ rdoc.title = spec.name
53
+ rdoc.template = '../rdoc_template.rb'
54
+ rdoc.options << '--line-numbers' << '--inline-source'
55
+ rdoc.rdoc_files.include('README.rdoc', 'CHANGELOG.rdoc', 'LICENSE', 'lib/**/*.rb', 'app/**/*.rb')
56
+ end
57
+
58
+ desc 'Generate a gemspec file.'
59
+ task :gemspec do
60
+ File.open("#{spec.name}.gemspec", 'w') do |f|
61
+ f.write spec.to_ruby
62
+ end
63
+ end
64
+
65
+ Rake::GemPackageTask.new(spec) do |p|
66
+ p.gem_spec = spec
67
+ end
68
+
69
+ desc 'Publish the release files to RubyForge.'
70
+ task :release => :package do
71
+ require 'rake/gemcutter'
72
+
73
+ Rake::Gemcutter::Tasks.new(spec)
74
+ Rake::Task['gem:push'].invoke
75
+ end
@@ -0,0 +1,65 @@
1
+ # Represents a preferred value for a particular preference on a model.
2
+ #
3
+ # == Grouped preferences
4
+ #
5
+ # In addition to simple named preferences, preferences can also be grouped by
6
+ # a particular value, be it a string or ActiveRecord object. For example, a
7
+ # User may have a preferred color for a particular Car. In this case, the
8
+ # +owner+ is the User record, the +name+ is "color", and the +group+ is the
9
+ # Car record. This allows preferences to have a sort of context around them.
10
+ class Preference < ActiveRecord::Base
11
+ belongs_to :owner, :polymorphic => true
12
+ belongs_to :group, :polymorphic => true
13
+
14
+ validates_presence_of :name, :owner_id, :owner_type
15
+ validates_presence_of :group_type, :if => :group_id?
16
+
17
+ class << self
18
+ # Splits the given group into its corresponding id and type. For simple
19
+ # primitives, the id will be nil. For complex types, specifically
20
+ # ActiveRecord objects, the id is the unique identifier stored in the
21
+ # database for the record.
22
+ #
23
+ # For example,
24
+ #
25
+ # Preference.split_group('google') # => [nil, "google"]
26
+ # Preference.split_group(1) # => [nil, 1]
27
+ # Preference.split_group(User.find(1)) # => [1, "User"]
28
+ def split_group(group = nil)
29
+ if group.is_a?(ActiveRecord::Base)
30
+ group_id, group_type = group.id, group.class.base_class.name.to_s
31
+ else
32
+ group_id, group_type = nil, group.is_a?(Symbol) ? group.to_s : group
33
+ end
34
+
35
+ [group_id, group_type]
36
+ end
37
+ end
38
+
39
+ # The definition of the preference as defined in the owner's model
40
+ def definition
41
+ # Optimize number of queries to the database by only looking up the actual
42
+ # owner record for STI cases when the definition can't be found in the
43
+ # stored owner type class
44
+ owner_type && (find_definition(owner_type.constantize) || find_definition(owner.class))
45
+ end
46
+
47
+ # Typecasts the value depending on the preference definition's declared type
48
+ def value
49
+ value = read_attribute(:value)
50
+ value = definition.type_cast(value) if definition
51
+ value
52
+ end
53
+
54
+ # Only searches for the group record if the group id is specified
55
+ def group_with_optional_lookup
56
+ group_id ? group_without_optional_lookup : group_type
57
+ end
58
+ alias_method_chain :group, :optional_lookup
59
+
60
+ private
61
+ # Finds the definition for this preference in the given owner class.
62
+ def find_definition(owner_class)
63
+ owner_class.respond_to?(:preference_definitions) && owner_class.preference_definitions[name]
64
+ end
65
+ end