acts_as_preferenced 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README +75 -0
- data/Rakefile +1 -0
- data/lib/acts_as_preferenced.rb +72 -0
- data/lib/acts_as_preferenced/preference.rb +48 -0
- data/lib/acts_as_preferenced/section.rb +23 -0
- data/lib/acts_as_preferenced/store/association.rb +66 -0
- data/lib/acts_as_preferenced/store/base.rb +23 -0
- data/lib/acts_as_preferenced/store/field.rb +61 -0
- data/lib/acts_as_preferenced/version.rb +3 -0
- data/test/acts_as_preferences_test.rb +253 -0
- data/test/test_helper.rb +29 -0
- metadata +60 -0
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 TODO: Write your name
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,75 @@
|
|
1
|
+
ActsAsPreferenced
|
2
|
+
===========
|
3
|
+
|
4
|
+
ActsAsPreferenced helps with handling of some settings you might want to store
|
5
|
+
per-object (usually per-user) in Rails applications. It provides unified API to
|
6
|
+
access those, ways to handle default values and some useful view helpers.
|
7
|
+
Preferences may be divided into sections for easier categorization if many are
|
8
|
+
present.
|
9
|
+
|
10
|
+
Preferences can be stored in one of pluggable stores. Currently two stores are
|
11
|
+
implemented: Field (stores in serialized db column) and Association (stores in
|
12
|
+
associated table). One of the aims of this gem was to provide smooth transition
|
13
|
+
between different stores - API stays the same for all of them, so you may start
|
14
|
+
with preferences being stored in a serialized field and then transition to
|
15
|
+
associated table.
|
16
|
+
|
17
|
+
Example
|
18
|
+
=======
|
19
|
+
|
20
|
+
# Association store - use whatever association you like, there are just 2
|
21
|
+
# requirements:
|
22
|
+
# * associated table needs to have field for every preference (type matching)
|
23
|
+
# * the name of the association cannot be "preferences", as it is used
|
24
|
+
# internally by ActsAsPreferenced
|
25
|
+
|
26
|
+
class User
|
27
|
+
attr_accessor :user_preferences
|
28
|
+
|
29
|
+
acts_as_preferenced( :association => :user_preferences ) do |config|
|
30
|
+
|
31
|
+
# Just an integer preference with allowed range
|
32
|
+
config.preference :items_per_page, :section => :general, :choices => (1..100), :default => 20
|
33
|
+
# Define the whole section of preferences. :prefix => true means
|
34
|
+
# preference names will start with section name, so :flowers will become
|
35
|
+
# "likes_flowers?" method ("?" added because it's boolean)
|
36
|
+
config.section :likes, :prefix => true do |section|
|
37
|
+
section.preference :flowers, :default => true
|
38
|
+
# No default - if not set will be nil
|
39
|
+
section.preference :animals
|
40
|
+
end
|
41
|
+
# Enumerated preference with no section (it belongs to nil section)
|
42
|
+
config.preference :likes_people, :choices => [:no, :little, :yes]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
OR
|
48
|
+
|
49
|
+
# Field store
|
50
|
+
|
51
|
+
class User
|
52
|
+
acts_as_preferenced( :field => :prefs, ...
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# Usage
|
57
|
+
|
58
|
+
user = User.new
|
59
|
+
user.likes_flowers? # True - default; as preference is boolean question mark is appended to getter
|
60
|
+
user.likes_flowers = false
|
61
|
+
user.likes_flowers? # False
|
62
|
+
user.likes_animals? # Nil - no default present
|
63
|
+
user.items_per_page = 120 # ArgumentError - value out of range
|
64
|
+
user.likes_people = :yes
|
65
|
+
user.likes_people # :yes - name might be misleading, but it's not boolean, so no question mark here
|
66
|
+
|
67
|
+
TODO
|
68
|
+
====
|
69
|
+
|
70
|
+
Helpers
|
71
|
+
API documentation
|
72
|
+
Migration generator for Association store.
|
73
|
+
I18n tests.
|
74
|
+
|
75
|
+
Copyright (c) 2009-2013 Marek Janukowicz, released under the MIT license
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'acts_as_preferenced/version'
|
2
|
+
require 'acts_as_preferenced/store/base'
|
3
|
+
require 'acts_as_preferenced/store/association'
|
4
|
+
require 'acts_as_preferenced/store/field'
|
5
|
+
require 'acts_as_preferenced/preference'
|
6
|
+
require 'acts_as_preferenced/section'
|
7
|
+
|
8
|
+
module ActsAsPreferenced
|
9
|
+
|
10
|
+
def self.included( klass )
|
11
|
+
klass.extend( ActMethods )
|
12
|
+
end
|
13
|
+
|
14
|
+
module ActMethods
|
15
|
+
|
16
|
+
def acts_as_preferenced( options = {} )
|
17
|
+
options.assert_valid_keys( :field, :association, :translation_scope )
|
18
|
+
class_attribute :preference_config
|
19
|
+
include InstanceMethods
|
20
|
+
extend ClassMethods
|
21
|
+
if options[:field]
|
22
|
+
field = options[:field]
|
23
|
+
self.module_eval { serialize field, Hash } # TODO: move this line into config initialization
|
24
|
+
config = ActsAsPreferenced::Store::Field.new( self, field, options[:translation_scope] )
|
25
|
+
self.preference_config = config
|
26
|
+
yield config
|
27
|
+
elsif options[:association]
|
28
|
+
config = ActsAsPreferenced::Store::Association.new( self, options[:association], options[:translation_scope] )
|
29
|
+
self.preference_config = config
|
30
|
+
yield config
|
31
|
+
else
|
32
|
+
raise ArgumentError, "Invalid options for acts_as_preferenced: #{options.inspect}"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
|
40
|
+
# TODO: Those methods should only be available when preferences are actually defined
|
41
|
+
def preference_sections
|
42
|
+
preference_config.sections.keys
|
43
|
+
end
|
44
|
+
|
45
|
+
def preferences_for_section( section )
|
46
|
+
preference_config.sections[section]
|
47
|
+
end
|
48
|
+
|
49
|
+
def all_preferences
|
50
|
+
preference_config.all_preferences
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
module InstanceMethods
|
56
|
+
|
57
|
+
def preference_value( name )
|
58
|
+
# TODO: implement
|
59
|
+
end
|
60
|
+
|
61
|
+
#def preferences
|
62
|
+
# Preferences.configs[self.class].all
|
63
|
+
#end
|
64
|
+
|
65
|
+
#def preferences= (prefs)
|
66
|
+
# Preferences.configs[self.class].all = prefs
|
67
|
+
#end
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
ActiveRecord::Base.send( :include, ActsAsPreferenced )
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module ActsAsPreferenced
|
2
|
+
class Preference
|
3
|
+
|
4
|
+
attr_reader :name, :default, :section
|
5
|
+
|
6
|
+
def initialize( store, name, options = {} )
|
7
|
+
options.assert_valid_keys( :section, :label, :choices, :default )
|
8
|
+
@store, @name = store, name
|
9
|
+
@section = options[:section]
|
10
|
+
@label = options[:label]
|
11
|
+
@choices = options[:choices]
|
12
|
+
@default = options[:default]
|
13
|
+
store.sections[@section] << self
|
14
|
+
store.all_preferences << self
|
15
|
+
end
|
16
|
+
|
17
|
+
def label
|
18
|
+
if @store.translation_scope && @label.nil?
|
19
|
+
I18n.t( @name, :scope => [@store.translation_scope, @section] )
|
20
|
+
else
|
21
|
+
@label
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Options translations are stored under hardcoded :choices key in Preferences
|
26
|
+
# translation_scope. TODO: make it not hardcoded and make it work for other
|
27
|
+
# options than Array (eg. Range?)
|
28
|
+
# TODO: it should be cached
|
29
|
+
def choices
|
30
|
+
if @choices.is_a?( Array )
|
31
|
+
# TODO: I don't quite understand checking for an array here
|
32
|
+
@choices.collect { |choice| choice.is_a?( Array ) ? choice : [I18n.t( choice, :scope => [@store.translation_scope, :choices] ), opt] }
|
33
|
+
else
|
34
|
+
@choices
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def value_valid?( value )
|
39
|
+
value = value.to_sym if value.is_a?( String )
|
40
|
+
if @choices
|
41
|
+
return @choices.include?( value )
|
42
|
+
else
|
43
|
+
return [true, false].include?( value )
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActsAsPreferenced
|
2
|
+
|
3
|
+
# This class only helps with defining of preference sections, not with storage
|
4
|
+
# (as I see no reason to make storage more complicated while bringing no real
|
5
|
+
# benefit)
|
6
|
+
class Section
|
7
|
+
|
8
|
+
# Options:
|
9
|
+
# * prefix - if true, section name will be prepended to all preferences
|
10
|
+
# defined within. False by default
|
11
|
+
def initialize( store, name, options = {} )
|
12
|
+
options.assert_valid_keys( :prefix )
|
13
|
+
@store, @name = store, name
|
14
|
+
@prefix = options[:prefix]
|
15
|
+
end
|
16
|
+
|
17
|
+
def preference( name, options = {} )
|
18
|
+
pref_name = (@prefix ? "#{@name}_#{name}".to_sym : name)
|
19
|
+
@store.preference( pref_name, options.merge( :section => @name ))
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module ActsAsPreferenced
|
2
|
+
module Store
|
3
|
+
|
4
|
+
class Association < Base
|
5
|
+
|
6
|
+
attr_reader :association
|
7
|
+
|
8
|
+
def initialize(klass, assoc, translation_scope = nil)
|
9
|
+
super( klass, translation_scope )
|
10
|
+
@association = assoc
|
11
|
+
@klass.send( :define_method, "preferences" ) do
|
12
|
+
prefs = send( preference_config.association ) || send( "build_#{preference_config.association}" )
|
13
|
+
return prefs
|
14
|
+
end
|
15
|
+
@klass.send( :define_method, "preferences=" ) do |new_prefs|
|
16
|
+
pref_object = send( preference_config.association ) || send( "build_#{preference_config.association}" )
|
17
|
+
self.class.all_preferences.each do |pref|
|
18
|
+
old_value = pref_object.send( pref.name )
|
19
|
+
new_value = new_prefs[pref.name]
|
20
|
+
if new_value
|
21
|
+
stringified_value = new_value.is_a?( Symbol ) ? new_value.to_s : new_value
|
22
|
+
new_prefs[pref.name] = stringified_value
|
23
|
+
else
|
24
|
+
# Hack to allow boolean preferences to be set to false. This looks
|
25
|
+
# wrong from model point of view (if you mass assign preferences,
|
26
|
+
# absent boolean ones will be set to false even though default is
|
27
|
+
# true), but it works well for forms.
|
28
|
+
# TODO: find a way to make it work properly both for defaults and
|
29
|
+
# forms (if at all possible)
|
30
|
+
if pref.default == true || old_value == true
|
31
|
+
new_prefs[pref.name] = false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
pref_object.attributes = new_prefs
|
36
|
+
end
|
37
|
+
|
38
|
+
@klass.send( :define_method, "save_preferences" ) do
|
39
|
+
pref_object = send( preference_config.association )
|
40
|
+
pref_object.save! if pref_object
|
41
|
+
end
|
42
|
+
@klass.before_save :save_preferences
|
43
|
+
end
|
44
|
+
|
45
|
+
def preference( name, options = {} )
|
46
|
+
options.assert_valid_keys( :section, :label, :choices, :default )
|
47
|
+
pref = Preference.new( self, name, options )
|
48
|
+
method_name = options[:choices] ? name.to_s : "#{name}?"
|
49
|
+
@klass.send( :define_method, method_name ) do
|
50
|
+
value = preferences[name]
|
51
|
+
value = pref.default if value.nil?
|
52
|
+
value &&= value.to_sym if value.is_a?( String )
|
53
|
+
value
|
54
|
+
end
|
55
|
+
@klass.send( :define_method, "#{name}=" ) do |value|
|
56
|
+
if pref.value_valid?( value )
|
57
|
+
preferences[name] = (value.is_a?( Symbol ) ? value.to_s : value)
|
58
|
+
else
|
59
|
+
raise ArgumentError, "Value #{value} invalid for preference #{pref.name}"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActsAsPreferenced
|
2
|
+
module Store
|
3
|
+
|
4
|
+
# Base class for all stores for preferences
|
5
|
+
class Base
|
6
|
+
|
7
|
+
attr_reader :sections, :all_preferences, :translation_scope
|
8
|
+
|
9
|
+
def initialize( klass, translation_scope = nil )
|
10
|
+
@klass = klass
|
11
|
+
@translation_scope = translation_scope
|
12
|
+
@sections = Hash.new { |h,k| h[k] = [] }
|
13
|
+
@all_preferences = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def section( name, options = {} )
|
17
|
+
section = Section.new( self, name, options )
|
18
|
+
yield section
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module ActsAsPreferenced
|
2
|
+
module Store
|
3
|
+
|
4
|
+
# This store holds preferences in a serialized field
|
5
|
+
class Field < Base
|
6
|
+
|
7
|
+
def initialize( klass, field, translation_scope = nil )
|
8
|
+
super( klass, translation_scope )
|
9
|
+
@field = field
|
10
|
+
@klass.send( :define_method, "preferences" ) do
|
11
|
+
prefs = read_attribute(field)
|
12
|
+
unless prefs
|
13
|
+
prefs = HashWithIndifferentAccess.new
|
14
|
+
write_attribute(field, prefs)
|
15
|
+
end
|
16
|
+
return prefs
|
17
|
+
end
|
18
|
+
|
19
|
+
@klass.send( :define_method, "preferences=" ) do |new_prefs|
|
20
|
+
self.class.all_preferences.each do |pref|
|
21
|
+
# Hack to allow boolean preferences to be set to false. This looks
|
22
|
+
# wrong from model point of view (if you mass assign preferences,
|
23
|
+
# absent boolean ones will be set to false even though default is
|
24
|
+
# true), but it works well for forms.
|
25
|
+
# TODO: find a way to make it work properly both for defaults and
|
26
|
+
# forms (if at all possible)
|
27
|
+
new_prefs[pref.name] ||= false if pref.default == true
|
28
|
+
end
|
29
|
+
write_attribute(field, new_prefs)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def preference( name, options = {} )
|
34
|
+
options.assert_valid_keys( :section, :label, :choices, :default )
|
35
|
+
pref = Preference.new( self, name, options )
|
36
|
+
if options[:choices] # Choice
|
37
|
+
@klass.send( :define_method, "#{name}" ) do
|
38
|
+
# This symbolizes strings and leave other values (eg. numbers)
|
39
|
+
# intact
|
40
|
+
value = preferences[name].is_a?( String ) ? preferences[name].to_sym : preferences[name]
|
41
|
+
value || pref.default
|
42
|
+
end
|
43
|
+
else # Boolean
|
44
|
+
@klass.send( :define_method, "#{name}?" ) do
|
45
|
+
# We check for nil to allow "false" here
|
46
|
+
preferences[name].nil? ? pref.default : preferences[name]
|
47
|
+
end
|
48
|
+
end
|
49
|
+
@klass.send( :define_method, "#{name}=" ) do |value|
|
50
|
+
if pref.value_valid?( value )
|
51
|
+
preferences[name] = value
|
52
|
+
else
|
53
|
+
raise ArgumentError, "Value #{value} invalid for preference #{pref.name}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,253 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
|
3
|
+
class ActsAsPreferencedFieldTest < ActiveSupport::TestCase
|
4
|
+
|
5
|
+
class User < FakeModel
|
6
|
+
|
7
|
+
attr_accessor :hold_preferences_here
|
8
|
+
|
9
|
+
acts_as_preferenced( :field => :hold_preferences_here ) do |config|
|
10
|
+
config.preference :items_per_page, :section => :general, :choices => (1..100), :default => 20
|
11
|
+
config.section :likes, :prefix => true do |section|
|
12
|
+
section.preference :flowers, :default => true
|
13
|
+
section.preference :animals
|
14
|
+
end
|
15
|
+
config.preference :likes_people, :choices => [:no, :little, :yes]
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def setup
|
20
|
+
@user = User.new
|
21
|
+
end
|
22
|
+
|
23
|
+
test "all preferences" do
|
24
|
+
all = User.all_preferences
|
25
|
+
assert_equal [:items_per_page, :likes_flowers, :likes_animals, :likes_people], all.collect(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
test "preference sections" do
|
29
|
+
assert_equal [:general, nil, :likes], User.preference_sections
|
30
|
+
prefs = User.preferences_for_section( :sdjkfajsdf )
|
31
|
+
assert prefs.empty?
|
32
|
+
prefs = User.preferences_for_section( :likes )
|
33
|
+
assert_equal [:likes_flowers, :likes_animals], prefs.collect(&:name)
|
34
|
+
prefs = User.preferences_for_section( nil )
|
35
|
+
assert_equal [:likes_people], prefs.collect(&:name)
|
36
|
+
end
|
37
|
+
|
38
|
+
test "defaults" do
|
39
|
+
assert_equal true, @user.likes_flowers?
|
40
|
+
assert_equal 20, @user.items_per_page
|
41
|
+
assert_nil @user.likes_animals?
|
42
|
+
assert_nil @user.likes_people
|
43
|
+
end
|
44
|
+
|
45
|
+
test "set boolean" do
|
46
|
+
@user.likes_flowers = false
|
47
|
+
assert_equal false, @user.likes_flowers?
|
48
|
+
end
|
49
|
+
|
50
|
+
test "set integer" do
|
51
|
+
@user.items_per_page = 11
|
52
|
+
assert_equal 11, @user.items_per_page
|
53
|
+
end
|
54
|
+
|
55
|
+
test "set enumerable" do
|
56
|
+
@user.likes_people = :little
|
57
|
+
assert_equal :little, @user.likes_people
|
58
|
+
end
|
59
|
+
|
60
|
+
test "set integer as boolean" do
|
61
|
+
assert_raises( ArgumentError ) do
|
62
|
+
@user.items_per_page = true
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
test "set integer out of range" do
|
67
|
+
assert_raises( ArgumentError ) do
|
68
|
+
@user.items_per_page = 0
|
69
|
+
end
|
70
|
+
assert_raises( ArgumentError ) do
|
71
|
+
@user.items_per_page = 101
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
test "set boolean as integer" do
|
76
|
+
assert_raises( ArgumentError ) do
|
77
|
+
@user.likes_flowers = 10
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
test "set enumerable out of range" do
|
82
|
+
assert_raises( ArgumentError ) do
|
83
|
+
@user.likes_people = :lot
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
test "set enumerable as string" do
|
88
|
+
@user.likes_people = "yes"
|
89
|
+
assert_equal :yes, @user.likes_people
|
90
|
+
end
|
91
|
+
|
92
|
+
test "set batch preferences" do
|
93
|
+
@user.likes_flowers = true
|
94
|
+
@user.likes_animals = true
|
95
|
+
@user.preferences = { :items_per_page => 23 }
|
96
|
+
assert_equal 23, @user.items_per_page
|
97
|
+
# No value for boolean defaulting to true - we assume it was unset
|
98
|
+
# TODO: in fact Rails handle this by sending hidden field with the same
|
99
|
+
# name, use the same approach
|
100
|
+
assert_equal false, @user.likes_flowers?
|
101
|
+
# No value for this boolean without default - reset it
|
102
|
+
# TODO: this also doesn't seem correct
|
103
|
+
assert_nil @user.likes_animals?
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class ActsAsPreferencedAssociationTest < ActiveSupport::TestCase
|
108
|
+
|
109
|
+
class UserPreference < FakeModel
|
110
|
+
|
111
|
+
# Tons of fakes here
|
112
|
+
attr_accessor :attributes
|
113
|
+
|
114
|
+
def items_per_page
|
115
|
+
attributes[:items_per_page]
|
116
|
+
end
|
117
|
+
|
118
|
+
def items_per_page= (value)
|
119
|
+
attributes[:items_per_page] = value
|
120
|
+
end
|
121
|
+
|
122
|
+
def likes_flowers?
|
123
|
+
attributes[:likes_flowers]
|
124
|
+
end
|
125
|
+
|
126
|
+
def likes_flowers= (value)
|
127
|
+
attributes[:likes_flowers] = value
|
128
|
+
end
|
129
|
+
|
130
|
+
def likes_animals?
|
131
|
+
attributes[:likes_animals]
|
132
|
+
end
|
133
|
+
|
134
|
+
def likes_animals= (value)
|
135
|
+
attributes[:likes_animals] = value
|
136
|
+
end
|
137
|
+
|
138
|
+
def likes_people?
|
139
|
+
attributes[:likes_people]
|
140
|
+
end
|
141
|
+
|
142
|
+
def likes_people= (value)
|
143
|
+
attributes[:likes_people] = value
|
144
|
+
end
|
145
|
+
|
146
|
+
alias_method :likes_flowers, :likes_flowers?
|
147
|
+
alias_method :likes_animals, :likes_animals?
|
148
|
+
alias_method :likes_people, :likes_people?
|
149
|
+
delegate :[], :[]=, :to => :attributes
|
150
|
+
end
|
151
|
+
|
152
|
+
class User < FakeModel
|
153
|
+
|
154
|
+
attr_accessor :user_preferences
|
155
|
+
|
156
|
+
acts_as_preferenced( :association => :user_preferences ) do |config|
|
157
|
+
config.preference :items_per_page, :section => :general, :choices => (1..100), :default => 20
|
158
|
+
config.section :likes, :prefix => true do |section|
|
159
|
+
section.preference :flowers, :default => true
|
160
|
+
section.preference :animals
|
161
|
+
end
|
162
|
+
config.preference :likes_people, :choices => [:no, :little, :yes]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
def setup
|
167
|
+
@user = User.new
|
168
|
+
@user.user_preferences = UserPreference.new
|
169
|
+
end
|
170
|
+
|
171
|
+
test "all preferences" do
|
172
|
+
all = User.all_preferences
|
173
|
+
assert_equal [:items_per_page, :likes_flowers, :likes_animals, :likes_people], all.collect(&:name)
|
174
|
+
end
|
175
|
+
|
176
|
+
test "preference sections" do
|
177
|
+
assert_equal [:general, nil, :likes], User.preference_sections
|
178
|
+
prefs = User.preferences_for_section( :sdjkfajsdf )
|
179
|
+
assert prefs.empty?
|
180
|
+
prefs = User.preferences_for_section( :likes )
|
181
|
+
assert_equal [:likes_flowers, :likes_animals], prefs.collect(&:name)
|
182
|
+
prefs = User.preferences_for_section( nil )
|
183
|
+
assert_equal [:likes_people], prefs.collect(&:name)
|
184
|
+
end
|
185
|
+
|
186
|
+
test "defaults" do
|
187
|
+
assert_equal true, @user.likes_flowers?
|
188
|
+
assert_equal 20, @user.items_per_page
|
189
|
+
assert_nil @user.likes_animals?
|
190
|
+
assert_nil @user.likes_people
|
191
|
+
end
|
192
|
+
|
193
|
+
test "set boolean" do
|
194
|
+
@user.likes_flowers = false
|
195
|
+
assert_equal false, @user.likes_flowers?
|
196
|
+
end
|
197
|
+
|
198
|
+
test "set integer" do
|
199
|
+
@user.items_per_page = 11
|
200
|
+
assert_equal 11, @user.items_per_page
|
201
|
+
end
|
202
|
+
|
203
|
+
test "set enumerable" do
|
204
|
+
@user.likes_people = :little
|
205
|
+
assert_equal :little, @user.likes_people
|
206
|
+
end
|
207
|
+
|
208
|
+
test "set integer as boolean" do
|
209
|
+
assert_raises( ArgumentError ) do
|
210
|
+
@user.items_per_page = true
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
test "set integer out of range" do
|
215
|
+
assert_raises( ArgumentError ) do
|
216
|
+
@user.items_per_page = 0
|
217
|
+
end
|
218
|
+
assert_raises( ArgumentError ) do
|
219
|
+
@user.items_per_page = 101
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
test "set boolean as integer" do
|
224
|
+
assert_raises( ArgumentError ) do
|
225
|
+
@user.likes_flowers = 10
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
test "set enumerable out of range" do
|
230
|
+
assert_raises( ArgumentError ) do
|
231
|
+
@user.likes_people = :lot
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
test "set enumerable as string" do
|
236
|
+
@user.likes_people = "yes"
|
237
|
+
assert_equal :yes, @user.likes_people
|
238
|
+
end
|
239
|
+
|
240
|
+
test "set batch preferences" do
|
241
|
+
@user.likes_flowers = true
|
242
|
+
@user.likes_animals = false
|
243
|
+
@user.preferences = { :items_per_page => 23 }
|
244
|
+
assert_equal 23, @user.items_per_page
|
245
|
+
# No value for boolean defaulting to true - we assume it was unset
|
246
|
+
# TODO: in fact Rails handle this by sending hidden field with the same
|
247
|
+
# name, use the same approach
|
248
|
+
assert_equal false, @user.likes_flowers?
|
249
|
+
# No value for this boolean without default - reset it
|
250
|
+
# TODO: this also doesn't seem correct
|
251
|
+
assert_nil @user.likes_animals?
|
252
|
+
end
|
253
|
+
end
|
data/test/test_helper.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'test/unit'
|
3
|
+
require 'set'
|
4
|
+
gem 'activesupport', "> 3.0.0"
|
5
|
+
require 'active_support/test_case'
|
6
|
+
require 'active_record'
|
7
|
+
require 'active_record/base'
|
8
|
+
require 'acts_as_preferenced'
|
9
|
+
require 'acts_as_preferenced/preference'
|
10
|
+
require 'acts_as_preferenced/section'
|
11
|
+
require 'acts_as_preferenced/store/base'
|
12
|
+
require 'acts_as_preferenced/store/association'
|
13
|
+
require 'acts_as_preferenced/store/field'
|
14
|
+
|
15
|
+
# Class faking AR model behavior - needed to test the validations without the database
|
16
|
+
class FakeModel < ActiveRecord::Base
|
17
|
+
|
18
|
+
self.abstract_class = true
|
19
|
+
|
20
|
+
def self.columns
|
21
|
+
@columns ||= [];
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.column(name, sql_type = nil, default = nil, null = true)
|
25
|
+
columns << ActiveRecord::ConnectionAdapters::Column.new(name.to_s, default, sql_type.to_s, null)
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: acts_as_preferenced
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.9.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Marek Janukowicz
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-07 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Preferences for Rails
|
15
|
+
email:
|
16
|
+
- marek@janukowicz.net
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- lib/acts_as_preferenced.rb
|
22
|
+
- lib/acts_as_preferenced/version.rb
|
23
|
+
- lib/acts_as_preferenced/section.rb
|
24
|
+
- lib/acts_as_preferenced/store/field.rb
|
25
|
+
- lib/acts_as_preferenced/store/association.rb
|
26
|
+
- lib/acts_as_preferenced/store/base.rb
|
27
|
+
- lib/acts_as_preferenced/preference.rb
|
28
|
+
- Gemfile
|
29
|
+
- LICENSE.txt
|
30
|
+
- Rakefile
|
31
|
+
- README
|
32
|
+
- test/acts_as_preferences_test.rb
|
33
|
+
- test/test_helper.rb
|
34
|
+
homepage: ''
|
35
|
+
licenses: []
|
36
|
+
post_install_message:
|
37
|
+
rdoc_options: []
|
38
|
+
require_paths:
|
39
|
+
- lib
|
40
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
47
|
+
none: false
|
48
|
+
requirements:
|
49
|
+
- - ! '>='
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.8.24
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: ''
|
58
|
+
test_files:
|
59
|
+
- test/acts_as_preferences_test.rb
|
60
|
+
- test/test_helper.rb
|