has_setting 0.4.6

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ *.sqlite3
2
+ pkg/**
data/README ADDED
@@ -0,0 +1,112 @@
1
+ ==What is it?
2
+ has_setting is a simple extension that enables ActiveRecord models to
3
+ store settings in a separate settings table as key/value pairs where the key and value are stored as Strings.
4
+
5
+ ==History
6
+ * 0.4.3:
7
+ * Changed behaviour of :boolean formatters: Now they treat '0', 0, false, nil and '' as false, everything else as true
8
+ This is not the same behaviour as ruby (treating only nil and false as false) but should help with input from web forms (i.e. checkboxes)
9
+ If you want strict ruby boolean behaviour, then use :strict_boolean as :type
10
+ * 0.4.2:
11
+ * bug fixes for boolean types default values
12
+ * 0.3.10:
13
+ * added boolean and booleans formatters
14
+ * 0.3.9:
15
+ * added type :strings, :floats, :ints. They store the contents of an array as a comma separated string.
16
+ * 0.3.8:
17
+ * added dependent destroy option. no more zombie settings lingering around.
18
+ * 0.3.7:
19
+ * Gem is now built using jeweler... after messing around and bumping versions and getting
20
+ strange errors, this is 'it works' feeling coming back
21
+ * 0.3.4:
22
+ * Added custom formatter support. no new formatters though...
23
+ * 0.3.1:
24
+ * Bug Fixed: has_many(:settings) is not added to ActiveRecord::Base but only to the classes with has_setting
25
+ * Bug Fixed: options are not shared between classes
26
+ * Again changed the way settings are saved. Save is now done on parent.save with an after_save callback. (like this the settings are treated as if they were attributes of the owner)
27
+ * 0.2.x:
28
+ * Added :default option
29
+ * changed way settings are saved so that unsaved parents can have settings too
30
+ * changed nameing scheme of setting names (incompatible with versions prior 0.2.x but since nobody uses the gem i dont care :-))
31
+ * 0.1.x: First Version
32
+
33
+
34
+ ==Installation
35
+ sudo gem install simplificator-has_setting
36
+
37
+ ==Setup
38
+ * Add a migration that looks more or less like the one in <em>help/001_create_settings.rb</em>
39
+ * Make sure the gem is loaded when your application starts
40
+
41
+ ==Config
42
+ The model you want to hold settings (i.e. User, Profile, ...):
43
+ <tt>has_setting(:name_of_the_setting)</tt>
44
+ This will create the following methods for you on the owner class:
45
+ * <tt>name_of_the_setting=(value)</tt> a standard setter
46
+ * <tt>name_of_the_setting()</tt> a standard getter (the getter method takes an optional hash to override some options, possible values are the same as the options in has_setting())
47
+
48
+ <tt>has_setting(name, options)</tt> takes an optional hash of options. Following options are supported:
49
+ <em>:type</em> allows you to convert the value:
50
+ * <em>:string</em> (default) Uses the StringFormatter to convert from/to String (actually this formatter just leaves the value as it is)
51
+ * <em>:int</em> Uses the IntFormatter to convert from/to int values.
52
+ * <em>:boolean</em> Uses the BooleanFormatter to convert from/to boolean values; treating 0, '0', false, '' and nil as false.
53
+ * <em>:strict_boolean</em> Uses the StrictBooleanFormatter to convert from/to boolean values; treating false, and nil as false.
54
+ * <em>:float</em> Uses the FloatFormatter to convert from/to float values.
55
+ * <em>:ints</em> Uses the IntsFormatter to convert from/to int[]
56
+ * <em>:floats</em> Uses the FloatsFormatter to convert from/to float[]
57
+ * <em>:strings</em> Uses the StringsFormatter to convert from/to string[]
58
+ * <em>:booleans</em> Uses the BooleansFormatter to convert from/to boolean[]
59
+ * <em>:strict_booleans</em> Uses the BooleansFormatter to convert from/to boolean[]
60
+ <em>:default</em> allows you to specify a default value that will be returned if the setting does not exist (i.e. has never been written). Note that the default value is _ignored_ if the setting exists, no matter what the value of the setting is. The default value is returned as is, no type conversion takes place.
61
+
62
+
63
+
64
+ ==How it works
65
+ A polymorphic parent-child relation is created between Setting and the parent/owning class.
66
+ Getters/setters are added through meta-programming-magic. If the setter is invoked on a unsafed parent then the setting is not saved until the parent is saved, else setting is saved upon creation (i.e. first time the setter is called) / change (subsequent calls).
67
+ The getters/setters can be used in standard AR validations, Rails mass assignments/form helpers and so on.
68
+
69
+ ==Gotchas
70
+ * Values are stored as Strings in the DB. Values are converted with one of the formatters (depending on selected :type). If you try to store an unsupported type or anything other than the type you selected there might be an exception (i.e. if you try to store "foobar" as an :type => :int)
71
+ * Currently there are no length validations on the 'name' and 'value' column of Setting. Take care not to store values to big. Especially when using the array formatters (:floats, :ints, :strings)
72
+
73
+
74
+ ==Example
75
+ <code>
76
+ class Foo < ActiveRecord::Base
77
+ has_setting(:string_setting)
78
+ has_setting(:another_string_setting, :type => :string)
79
+ has_setting(:int_setting, :type => :int)
80
+ has_setting(:float_setting, :type => :float, :default => 3.3)
81
+ end
82
+
83
+
84
+ foo = Foo.create
85
+
86
+ foo.string_setting
87
+ => nil
88
+ foo.string_setting= 'a string'
89
+ foo.string_setting
90
+ => 'a string'
91
+
92
+ foo.int_setting = 123
93
+ foo.int_setting
94
+ => 123
95
+ foo.int_setting = '123'
96
+ foo.int_setting
97
+ => 123
98
+
99
+ foo.float_setting
100
+ => 3.3
101
+ foo.float_setting = nil
102
+ foo.float_setting
103
+ => nil
104
+
105
+ </code>
106
+
107
+
108
+ ==Todo
109
+ has_setting should stay as simple as possible... still some ideas are around:
110
+ * Custom formatter (to convert arbitrary objects, i.e. Date/Time/DateTime...)
111
+ * Add validation options
112
+
data/Rakefile ADDED
@@ -0,0 +1,20 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new(:test) do |t|
6
+ t.libs << 'test'
7
+ t.test_files = FileList['test/**/*_test.rb']
8
+ t.verbose = false
9
+ end
10
+
11
+ # require 'rake/rdoctask'
12
+ # Rake::RDocTask.new do |rdoc|
13
+ # rdoc.rdoc_dir = 'rdoc'
14
+ # rdoc.title = 'has_setting'
15
+ # rdoc.options << '--line-numbers' << '--inline-source'
16
+ # rdoc.rdoc_files.include('README*')
17
+ # rdoc.rdoc_files.include('lib/**/*.rb')
18
+ # end
19
+
20
+ task :default => :test
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 4
3
+ :patch: 3
4
+ :major: 0
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ require File.expand_path('../lib/has_setting/version', __FILE__)
4
+
5
+ Gem::Specification.new do |gem|
6
+ gem.authors = ["Simplificator GmbH", "Nico Ritsche"]
7
+ gem.email = ['info@simplificator.com', "ncrdevmail@gmail.com"]
8
+ gem.description = %q{Stores settings as key/value pairs in a settings table and provides accessors for them on the owning object}
9
+ gem.summary = %q{Simple setting extension to AR}
10
+ gem.homepage = "http://github.com/ncri/has_setting"
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "has_setting"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = HasSetting::VERSION
18
+ gem.has_rdoc = true
19
+ gem.rdoc_options = ["--charset=UTF-8"]
20
+ gem.test_files = [
21
+ "test/bar.rb",
22
+ "test/baz.rb",
23
+ "test/foo.rb",
24
+ "test/test_helper.rb",
25
+ "test/unit/formatters_test.rb",
26
+ "test/unit/has_setting_test.rb"
27
+ ]
28
+
29
+ end
@@ -0,0 +1,15 @@
1
+ class CreateSettings < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :settings do |t|
4
+ t.string(:owner_type, :null => false)
5
+ t.integer(:owner_id, :null => false)
6
+ t.string(:name, :null => false, :length => 64)
7
+ t.string(:value, :null => true, :length => 255)
8
+ t.timestamps
9
+ end
10
+ end
11
+
12
+ def self.down
13
+ drop_table(:settings)
14
+ end
15
+ end
@@ -0,0 +1,70 @@
1
+ module HasSetting
2
+
3
+ module InstanceMethods
4
+ def write_setting(name, value)
5
+ # find an existing setting or build a new one
6
+ setting = self.settings.detect() {|item| item.name == name }
7
+ setting = self.settings.build(:name => name) if setting.blank?
8
+ setting.value = value
9
+ end
10
+
11
+ def read_setting(name)
12
+ # use detect instead of SQL find. like this the 'cached' has_many-collection is inited
13
+ # only once
14
+ self.settings.detect() {|item| item.name == name }
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ # Setup of the getter/setter
20
+ def has_setting(name, options = {})
21
+ name = name.to_s
22
+ raise ArgumentError.new('Setting name must not be blank') if name.blank?
23
+
24
+ self.class_eval do
25
+ unless @has_setting_options # define only once
26
+ # AR association to settings
27
+ has_many( :settings, :as => :owner, :class_name => 'HasSetting::Setting',
28
+ :foreign_key => :owner_id, :dependent => :destroy)
29
+ after_save(:save_has_setting_association)
30
+ @has_setting_options = {}
31
+ def self.has_setting_options
32
+ @has_setting_options
33
+ end
34
+
35
+ private
36
+ # Callback to save settings
37
+ def save_has_setting_association
38
+ self.settings.each do |setting|
39
+ setting.save if setting.changed?
40
+ end
41
+ end
42
+ end
43
+ end
44
+
45
+
46
+ raise ArgumentError.new("Setting #{name }is already defined on #{self.name}") if self.has_setting_options.has_key?(name)
47
+
48
+ # default options
49
+ options[:type] ||= :string # treat as string
50
+ # default could be false, thats why we use has_key?
51
+ options[:default] = options.has_key?(:default) ? options[:default] : nil # no default
52
+ self.has_setting_options[name] = options
53
+
54
+ # setter
55
+ define_method("#{name}=".intern) do |value|
56
+ formatter = HasSetting::Formatters.for_type(options[:type])
57
+ write_setting(name, formatter.to_s(value))
58
+ end
59
+
60
+ # getter
61
+ define_method(name) do |*args|
62
+ setting = read_setting(name)
63
+ options = args.first || self.class.has_setting_options[name]
64
+ return options[:default] if setting.nil?
65
+ formatter = Formatters.for_type(options[:type])
66
+ formatter.to_type(setting.value)
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,140 @@
1
+ module HasSetting
2
+ module Formatters
3
+ @@formatters = {}
4
+ #
5
+ # register a formatter:
6
+ # * type: a Symbol that is used to identify this formatter in the has_setting options hash via the :type option
7
+ # * formatter: the formatter, an object which supports to_type and to_s methods
8
+ def self.register_formatter(type, formatter)
9
+ if !formatter.respond_to?(:to_s) || !formatter.respond_to?(:to_type)
10
+ raise ArgumentError.new('Formatter does not support to_s/to_type')
11
+ end
12
+ @@formatters[type] = formatter
13
+ end
14
+
15
+ # Lookup a Formatter by type symbol
16
+ # raises ArgumentError if the formatter is not found
17
+ def self.for_type(type)
18
+ formatter = @@formatters[type]
19
+ raise ArgumentError.new("Can not find a formatter for #{type}") unless formatter
20
+ formatter
21
+ end
22
+
23
+ # Helper class which handles nil values
24
+ class NilSafeFormatter
25
+ # Converts the String from DB to the specified type
26
+ # Nil is returned for nil values
27
+ def to_type(value)
28
+ safe_to_type(value) unless value == nil
29
+ end
30
+ # Converts the value to String for storing in DB
31
+ # Nil is returned for nil values
32
+ def to_s(value)
33
+ safe_to_s(value) unless value == nil
34
+ end
35
+ end
36
+
37
+ # Formatter for Strings
38
+ class StringFormatter < NilSafeFormatter
39
+ def safe_to_type(value)
40
+ value
41
+ end
42
+ def safe_to_s(value)
43
+ value.to_s
44
+ end
45
+ end
46
+ # Convert a Boolean to String and Back
47
+ # nil, '0', false, 0 and '' are considered __false__, everything else is __true__
48
+ # This is not like ruby where only nil and false are considered __false__
49
+ class BooleanFormatter < NilSafeFormatter
50
+ def safe_to_type(value)
51
+ value == '1'
52
+ end
53
+ def safe_to_s(value)
54
+ value && value != '0' && value != 0 && value != '' ? '1' : '0'
55
+ end
56
+ end
57
+ class BooleansFormatter < NilSafeFormatter
58
+ def safe_to_type(value)
59
+ value.split(',').map() {|item| Formatters.for_type(:boolean).to_type(item)}
60
+ end
61
+ def safe_to_s(value)
62
+ Array(value).map() {|item| Formatters.for_type(:boolean).to_s(item)}.join(',')
63
+ end
64
+ end
65
+
66
+ class StrictBooleanFormatter < NilSafeFormatter
67
+ def safe_to_type(value)
68
+ value == '1'
69
+ end
70
+ def safe_to_s(value)
71
+ value ? '1' : '0'
72
+ end
73
+ end
74
+ class StrictBooleansFormatter < NilSafeFormatter
75
+ def safe_to_type(value)
76
+ value.split(',').map() {|item| Formatters.for_type(:strict_boolean).to_type(item)}
77
+ end
78
+ def safe_to_s(value)
79
+ Array(value).map() {|item| Formatters.for_type(:strict_boolean).to_s(item)}.join(',')
80
+ end
81
+ end
82
+
83
+ class IntsFormatter < NilSafeFormatter
84
+ def safe_to_type(value)
85
+ value.split(',').map() {|item| Formatters.for_type(:int).to_type(item)}
86
+ end
87
+ def safe_to_s(value)
88
+ Array(value).map() {|item| Formatters.for_type(:int).to_s(item)}.join(',')
89
+ end
90
+ end
91
+
92
+ class FloatsFormatter < NilSafeFormatter
93
+ def safe_to_type(value)
94
+ value.split(',').map() {|item| Formatters.for_type(:float).to_type(item)}
95
+ end
96
+ def safe_to_s(value)
97
+ Array(value).map() {|item| Formatters.for_type(:float).to_s(item)}.join(',')
98
+ end
99
+ end
100
+
101
+ class StringsFormatter < NilSafeFormatter
102
+ def safe_to_type(value)
103
+ # Ruby does not know "negative look before". Or i dont know how to do it in ruby. Thus
104
+ # i ended up using some reverse calls... ugly. Anyone out there eager to help me out?
105
+ value.reverse.split(/,(?!\\)/).map() {|item| item.reverse.gsub('\,', ',')}.reverse
106
+ end
107
+ def safe_to_s(value)
108
+ # Escape the separator character ',' with a backslash
109
+ Array(value).map() {|item| item.gsub(',', '\,')}.join(',')
110
+ end
111
+ end
112
+
113
+
114
+ # Formatter for ints
115
+ # Throws ArgumentError if value can not be converted
116
+ class IntFormatter < NilSafeFormatter
117
+ # Integer() does not treat "2.6" the same as 2.6
118
+ # while 2.6 is a valid Intger() (-> 2), "2.6" is not.
119
+ # Note that "2" is a valid argument for Integer() and that "".to_i is valid
120
+ # while Integer('') is not...
121
+ # Circumvent this by first convert with Float() so everything obeys to the same rules
122
+ def safe_to_type(value)
123
+ Integer(Float(value))
124
+ end
125
+ def safe_to_s(value)
126
+ Integer(Float(value)).to_s
127
+ end
128
+ end
129
+ # Formatter for float values
130
+ # Throws ArgumentError if value can not be converted
131
+ class FloatFormatter < NilSafeFormatter
132
+ def safe_to_type(value)
133
+ Float(value)
134
+ end
135
+ def safe_to_s(value)
136
+ Float(value).to_s
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,7 @@
1
+ # ActiveRecord model to store key/value pairs (though the
2
+ # columns are called name/value to avoid upsetting a Database for using a
3
+ # keyword as column name)
4
+ #
5
+ class HasSetting::Setting < ActiveRecord::Base
6
+ belongs_to(:owner, :polymorphic => true)
7
+ end
@@ -0,0 +1,3 @@
1
+ module HasSetting
2
+ VERSION = "0.4.6"
3
+ end
@@ -0,0 +1,20 @@
1
+ # Intialize the gem by including some extension to ActiveRecord::Base
2
+ require File.dirname(__FILE__) + '/has_setting/ar_extensions'
3
+ require File.dirname(__FILE__) + '/has_setting/formatters'
4
+ require File.dirname(__FILE__) + '/has_setting/setting'
5
+ ActiveRecord::Base.class_eval do
6
+ include(HasSetting::InstanceMethods)
7
+ extend(HasSetting::ClassMethods)
8
+ end
9
+
10
+
11
+ HasSetting::Formatters.register_formatter(:string, HasSetting::Formatters::StringFormatter.new)
12
+ HasSetting::Formatters.register_formatter(:strings, HasSetting::Formatters::StringsFormatter.new)
13
+ HasSetting::Formatters.register_formatter(:float, HasSetting::Formatters::FloatFormatter.new)
14
+ HasSetting::Formatters.register_formatter(:floats, HasSetting::Formatters::FloatsFormatter.new)
15
+ HasSetting::Formatters.register_formatter(:int, HasSetting::Formatters::IntFormatter.new)
16
+ HasSetting::Formatters.register_formatter(:ints, HasSetting::Formatters::IntsFormatter.new)
17
+ HasSetting::Formatters.register_formatter(:boolean, HasSetting::Formatters::BooleanFormatter.new)
18
+ HasSetting::Formatters.register_formatter(:booleans, HasSetting::Formatters::BooleansFormatter.new)
19
+ HasSetting::Formatters.register_formatter(:strict_boolean, HasSetting::Formatters::StrictBooleanFormatter.new)
20
+ HasSetting::Formatters.register_formatter(:strict_booleans, HasSetting::Formatters::StrictBooleansFormatter.new)
data/test/bar.rb ADDED
@@ -0,0 +1,4 @@
1
+ class Bar < ActiveRecord::Base
2
+ has_setting(:setting_1)
3
+ has_setting(:setting_2, :type => :int)
4
+ end
data/test/baz.rb ADDED
@@ -0,0 +1,2 @@
1
+ class Baz < ActiveRecord::Base
2
+ end
data/test/foo.rb ADDED
@@ -0,0 +1,8 @@
1
+ class Foo < ActiveRecord::Base
2
+ has_setting(:setting_1)
3
+ has_setting(:setting_2, :type => :float)
4
+ has_setting(:with_default, :default => 'def')
5
+
6
+ has_setting(:setting_3, :type => :boolean)
7
+ has_setting(:setting_4, :type => :boolean, :default => false)
8
+ end
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'active_record'
3
+ require 'test/unit'
4
+
5
+ require File.dirname(__FILE__) + '/../lib/has_setting'
6
+
7
+
8
+ ActiveRecord::Base.establish_connection(
9
+ :adapter => 'sqlite3',
10
+ :database => 'test.sqlite3',
11
+ :timeout => 5000
12
+ )
13
+
14
+ ActiveRecord::Base.connection.drop_table(:settings) rescue ActiveRecord::StatementInvalid
15
+ [:foos, :bars, :bazs].each do |table|
16
+ ActiveRecord::Base.connection.drop_table(table) rescue ActiveRecord::StatementInvalid
17
+ end
18
+
19
+
20
+
21
+ ActiveRecord::Base.connection.create_table(:settings) do |table|
22
+ table.string(:value, :limit => 255)
23
+ table.string(:name, :limit => 64, :null => false)
24
+ table.string(:owner_type, :limit => 255, :null => false)
25
+ table.integer(:owner_id, :null => false)
26
+ end
27
+ [:foos, :bars, :bazs].each do |table|
28
+ ActiveRecord::Base.connection.create_table(table) do |t|
29
+ end
30
+ end
31
+ ActiveRecord::Base.logger = Logger.new(STDOUT)
32
+ ActiveRecord::Base.logger.level = Logger::DEBUG # change to DEBUG if you want to see something :-)
33
+
34
+ require File.join(File.dirname(__FILE__), 'foo')
35
+ require File.join(File.dirname(__FILE__), 'bar')
36
+ require File.join(File.dirname(__FILE__), 'baz')
@@ -0,0 +1,183 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ include HasSetting
3
+ class FormattersTest < Test::Unit::TestCase
4
+ def test_for_type
5
+ [:string, :float, :floats, :int, :ints, :strings, :boolean, :booleans, :strict_boolean, :strict_booleans].each do |symbol|
6
+ assert(Formatters.for_type(symbol), "No formatter for #{symbol}")
7
+ if symbol == :strict_boolean
8
+ assert_equal(Formatters.for_type(symbol).class.to_s, "HasSetting::Formatters::StrictBooleanFormatter")
9
+ elsif symbol == :strict_booleans
10
+ assert_equal(Formatters.for_type(symbol).class.to_s, "HasSetting::Formatters::StrictBooleansFormatter")
11
+ else
12
+ assert_equal(Formatters.for_type(symbol).class.to_s, "HasSetting::Formatters::#{symbol.to_s.capitalize}Formatter")
13
+ end
14
+ end
15
+ assert_raises(ArgumentError) do
16
+ Formatters.for_type(:rarararararara_i_do_not_exist)
17
+ end
18
+ end
19
+
20
+ def test_strings_formatter
21
+ f = Formatters::StringsFormatter.new
22
+ assert_equal(nil, f.to_s(nil))
23
+ assert_equal('bla', f.to_s('bla'))
24
+ assert_equal('bla', f.to_s(['bla']))
25
+ assert_equal('bla,bli', f.to_s(['bla', 'bli']))
26
+ assert_equal('\,schni\,schna\,,bli,\,bla', f.to_s([',schni,schna,', 'bli', ',bla']))
27
+ assert_equal('\,\,\,\,,\,\,\,,\,\,,\,', f.to_s([',,,,', ',,,', ',,', ',']))
28
+
29
+ assert_equal(nil, f.to_type(nil))
30
+ assert_equal([], f.to_type(''))
31
+ assert_equal(['bli'], f.to_type('bli'))
32
+ assert_equal(['bli','', 'bla'], f.to_type('bli,,bla'))
33
+ assert_equal([',schni,schna,', 'bli', ',bla'], f.to_type('\,schni\,schna\,,bli,\,bla'))
34
+ assert_equal([',,,,', ',,,', ',,', ','], f.to_type('\,\,\,\,,\,\,\,,\,\,,\,'))
35
+ end
36
+
37
+
38
+ def test_string_formatter()
39
+ f = Formatters::StringFormatter.new
40
+ assert_equal('', f.to_s(''))
41
+ assert_equal('a', f.to_s('a'))
42
+ assert_equal('', f.to_type(''))
43
+ assert_equal('a', f.to_type('a'))
44
+ end
45
+
46
+ def test_boolean_formatter
47
+ f = Formatters::BooleanFormatter.new
48
+ assert_equal('0', f.to_s(''))
49
+ assert_equal('1', f.to_s(true))
50
+ assert_equal('0', f.to_s(false))
51
+ assert_equal('0', f.to_s('0'))
52
+ assert_equal('0', f.to_s(0))
53
+ assert_equal('0', f.to_s(''))
54
+ assert_equal(nil, f.to_s(nil))
55
+
56
+ assert_equal(true, f.to_type('1'))
57
+ assert_equal(false, f.to_type('0'))
58
+ assert_equal(nil, f.to_type(nil))
59
+ end
60
+
61
+ def test_strict_boolean_formatter
62
+ f = Formatters::StrictBooleanFormatter.new
63
+ assert_equal('1', f.to_s(''))
64
+ assert_equal('1', f.to_s(true))
65
+ assert_equal('0', f.to_s(false))
66
+ assert_equal('1', f.to_s('0'))
67
+ assert_equal(nil, f.to_s(nil))
68
+
69
+ assert_equal(true, f.to_type('1'))
70
+ assert_equal(false, f.to_type('0'))
71
+ assert_equal(nil, f.to_type(nil))
72
+ end
73
+ def test_int_formatter()
74
+ f = Formatters::IntFormatter.new
75
+ assert_raises(ArgumentError) do
76
+ f.to_s('')
77
+ end
78
+ assert_raises(ArgumentError) do
79
+ f.to_s('asas')
80
+ end
81
+ assert_nil(f.to_s(nil))
82
+ assert_equal('2', f.to_s(2.6))
83
+ assert_equal('2', f.to_s(2))
84
+
85
+ assert_raises(ArgumentError) do
86
+ f.to_type('')
87
+ end
88
+ assert_raises(ArgumentError) do
89
+ f.to_type('asas')
90
+ end
91
+ assert_nil(f.to_type(nil))
92
+ assert_equal(2, f.to_type('2'))
93
+ assert_equal(2, f.to_type('2.6'))
94
+ assert_equal(2, f.to_type(' 2.6 '))
95
+ end
96
+
97
+ def test_float_formatter()
98
+ f = Formatters::FloatFormatter.new
99
+ assert_raises(ArgumentError) do
100
+ f.to_s('')
101
+ end
102
+ assert_raises(ArgumentError) do
103
+ f.to_s('asas')
104
+ end
105
+ assert_nil(f.to_s(nil))
106
+ assert_equal('2.6', f.to_s(2.6))
107
+ assert_equal('2.0', f.to_s(2))
108
+
109
+ assert_raises(ArgumentError) do
110
+ f.to_type('')
111
+ end
112
+ assert_raises(ArgumentError) do
113
+ f.to_type('asas')
114
+ end
115
+ assert_nil(f.to_type(nil))
116
+ assert_equal(2.0, f.to_type('2'))
117
+ assert_equal(2.6, f.to_type('2.6'))
118
+ end
119
+
120
+ def test_ints_formatter
121
+ f = Formatters::IntsFormatter.new
122
+ assert_equal(nil, f.to_s(nil))
123
+ assert_equal('1', f.to_s(1))
124
+ assert_equal('1', f.to_s([1]))
125
+ assert_equal('1,2', f.to_s([1,2]))
126
+
127
+ assert_equal(nil, f.to_type(nil))
128
+ assert_equal([], f.to_type(''))
129
+ assert_equal([1], f.to_type('1'))
130
+ assert_equal([1,2], f.to_type('1,2'))
131
+ end
132
+ def test_floats_formatter
133
+ f = Formatters::FloatsFormatter.new
134
+ assert_equal(nil, f.to_s(nil))
135
+ assert_equal('1.2', f.to_s(1.2))
136
+ assert_equal('1.2', f.to_s([1.2]))
137
+ assert_equal('1.2,1.3', f.to_s([1.2,1.3]))
138
+
139
+ assert_equal(nil, f.to_type(nil))
140
+ assert_equal([], f.to_type(''))
141
+ assert_equal([1.2], f.to_type('1.2'))
142
+ assert_equal([1.2,1.3], f.to_type('1.2,1.3'))
143
+ assert_equal([1.2,1.3], f.to_type('1.2, 1.3'))
144
+ end
145
+ def test_booleans_formatter
146
+ f = Formatters::BooleansFormatter.new
147
+ assert_equal(nil, f.to_s(nil))
148
+ assert_equal('1', f.to_s(true))
149
+ assert_equal('1', f.to_s([true]))
150
+ assert_equal('1,0', f.to_s([true,false]))
151
+ assert_equal('0,0,0,0,', f.to_s(['', 0, false, '0', nil]))
152
+
153
+ assert_equal(nil, f.to_type(nil))
154
+ assert_equal([], f.to_type(''))
155
+ assert_equal([true], f.to_type('1'))
156
+ assert_equal([true, false], f.to_type('1,0'))
157
+
158
+
159
+ # test boolean with values != true|false
160
+ assert_equal('1', f.to_s('true'))
161
+ assert_equal('1', f.to_s(555))
162
+ end
163
+
164
+
165
+ def test_strict_booleans_formatter
166
+ f = Formatters::StrictBooleansFormatter.new
167
+ assert_equal(nil, f.to_s(nil))
168
+ assert_equal('1', f.to_s(true))
169
+ assert_equal('1', f.to_s([true]))
170
+ assert_equal('1,0', f.to_s([true,false]))
171
+ assert_equal('1,1', f.to_s([1,0]))
172
+
173
+ assert_equal(nil, f.to_type(nil))
174
+ assert_equal([], f.to_type(''))
175
+ assert_equal([true], f.to_type('1'))
176
+ assert_equal([true, false], f.to_type('1,0'))
177
+
178
+
179
+ # test boolean with values != true|false
180
+ assert_equal('1', f.to_s('true'))
181
+ assert_equal('1', f.to_s(555))
182
+ end
183
+ end
@@ -0,0 +1,148 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+
3
+ class HasSettingTest < Test::Unit::TestCase
4
+ def setup()
5
+ @foo = Foo.create!
6
+ @bar = Bar.create!
7
+ @baz = Baz.create!
8
+ end
9
+
10
+
11
+ def test_setting_has_accessors
12
+ assert @foo.respond_to?(:setting_1)
13
+ assert @foo.respond_to?(:setting_1=)
14
+ end
15
+
16
+
17
+ def test_has_many
18
+ assert @foo.respond_to?(:settings)
19
+ assert @foo.settings.is_a?(Array)
20
+ end
21
+
22
+ def test_settings_are_destroyed
23
+ count_before = HasSetting::Setting.count
24
+ @foo.setting_1 = 10
25
+ @foo.save!
26
+ assert_equal(count_before + 1, HasSetting::Setting.count)
27
+ @foo.destroy
28
+ assert_equal(count_before, HasSetting::Setting.count)
29
+
30
+ end
31
+
32
+ def test_write_setting
33
+ count_before = HasSetting::Setting.count
34
+ @foo.write_setting('name', 'value1')
35
+ @foo.save!
36
+ assert_equal(count_before + 1, HasSetting::Setting.count)
37
+ setting = @foo.read_setting('name')
38
+ assert setting
39
+ assert_equal('value1', setting.value)
40
+
41
+ @foo.write_setting('name', 'value2')
42
+ assert_equal(count_before + 1, HasSetting::Setting.count)
43
+ setting = @foo.read_setting('name')
44
+ assert setting
45
+ assert_equal('value2', setting.value)
46
+ end
47
+
48
+ def test_setting_accessors
49
+ count_before = HasSetting::Setting.count
50
+ assert(!@foo.setting_1)
51
+ @foo.setting_1 = 'bli'
52
+ @foo.save!
53
+ assert_equal(count_before + 1, HasSetting::Setting.count)
54
+ assert_equal('bli', @foo.setting_1)
55
+ @foo.setting_1 = 'bla'
56
+ assert_equal('bla', @foo.setting_1)
57
+ end
58
+
59
+ def test_different_classes_do_not_share_setting
60
+ count_before = HasSetting::Setting.count
61
+ @foo.setting_1 = 'foo'
62
+ @foo.save!
63
+ @bar.setting_1 = 'bar'
64
+ @bar.save!
65
+ assert_equal(count_before + 2, HasSetting::Setting.count)
66
+
67
+ assert_equal('foo', @foo.setting_1)
68
+ assert_equal('bar', @bar.setting_1)
69
+ end
70
+
71
+
72
+ def test_has_nil_setting
73
+ @foo.setting_1 = nil
74
+ assert(@foo.read_setting('setting_1'))
75
+ assert(!@foo.setting_1)
76
+ end
77
+
78
+ def test_options_on_getter
79
+ @foo.setting_1 = '12.3'
80
+ assert_equal('12.3', @foo.setting_1)
81
+ assert_equal(12, @foo.setting_1(:type => :int))
82
+ assert_equal(12.3, @foo.setting_1(:type => :float))
83
+
84
+ # Foo.setting_2 is a float setting. Override and get as string
85
+ @foo.setting_2 = 12.3
86
+ assert_equal('12.3', @foo.setting_2(:type => :string))
87
+ end
88
+
89
+ def test_different_classes_do_not_share_options()
90
+ @foo.setting_2 = 12.3
91
+ assert_equal(12.3, @foo.setting_2)
92
+ @bar.setting_2 = 12.3
93
+ assert_equal(12, @bar.setting_2)
94
+ end
95
+
96
+ def test_default_values()
97
+ assert_equal('def', @foo.with_default)
98
+ assert_equal('override def', @foo.with_default(:default => 'override def'))
99
+ @foo.with_default = 'not def'
100
+ assert_equal('not def', @foo.with_default)
101
+ end
102
+
103
+ def test_write_settings_without_saved_parent
104
+ my_foo = Foo.new
105
+ count_before = HasSetting::Setting.count
106
+ my_foo.with_default = 'radabumm'
107
+ assert_equal(count_before, HasSetting::Setting.count)
108
+ assert_equal('radabumm', my_foo.with_default)
109
+ my_foo.save!
110
+ assert_equal(count_before + 1, HasSetting::Setting.count)
111
+ assert_equal('radabumm', my_foo.with_default)
112
+ end
113
+
114
+ def test_not_everyone_has_settings_association
115
+ assert_equal(true, @foo.respond_to?(:settings))
116
+ assert_equal(true, @bar.respond_to?(:settings))
117
+ assert_equal(false, @baz.respond_to?(:settings))
118
+ end
119
+
120
+
121
+ def test_boolean_setting_without_default
122
+ assert_equal nil, @foo.setting_3
123
+ @foo.setting_3 = true
124
+ @foo.save!
125
+
126
+ @foo = Foo.find @foo.id
127
+ assert_equal true, @foo.setting_3
128
+ end
129
+ def test_boolean_setting_with_default
130
+ assert_equal false, @foo.setting_4
131
+ @foo.setting_4 = true
132
+ @foo.save!
133
+ @foo = Foo.find @foo.id
134
+ assert_equal true, @foo.setting_4
135
+ end
136
+
137
+ def test_boolean_setting_with_default_and_no_saving
138
+ assert_equal false, @foo.setting_4
139
+ @foo.setting_4 = true
140
+ assert_equal true, @foo.setting_4
141
+ @foo.setting_4 = nil
142
+ assert_equal nil, @foo.setting_4
143
+ @foo.setting_4 = false
144
+ assert_equal false, @foo.setting_4
145
+ end
146
+
147
+
148
+ end
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: has_setting
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.4.6
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Simplificator GmbH
9
+ - Nico Ritsche
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-04-10 00:00:00.000000000 Z
14
+ dependencies: []
15
+ description: Stores settings as key/value pairs in a settings table and provides accessors
16
+ for them on the owning object
17
+ email:
18
+ - info@simplificator.com
19
+ - ncrdevmail@gmail.com
20
+ executables: []
21
+ extensions: []
22
+ extra_rdoc_files: []
23
+ files:
24
+ - .gitignore
25
+ - README
26
+ - Rakefile
27
+ - VERSION.yml
28
+ - has_setting.gemspec
29
+ - help/001_create_settings.rb
30
+ - lib/has_setting.rb
31
+ - lib/has_setting/ar_extensions.rb
32
+ - lib/has_setting/formatters.rb
33
+ - lib/has_setting/setting.rb
34
+ - lib/has_setting/version.rb
35
+ - test/bar.rb
36
+ - test/baz.rb
37
+ - test/foo.rb
38
+ - test/test_helper.rb
39
+ - test/unit/formatters_test.rb
40
+ - test/unit/has_setting_test.rb
41
+ homepage: http://github.com/ncri/has_setting
42
+ licenses: []
43
+ post_install_message:
44
+ rdoc_options:
45
+ - --charset=UTF-8
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ requirements: []
61
+ rubyforge_project:
62
+ rubygems_version: 1.8.24
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Simple setting extension to AR
66
+ test_files:
67
+ - test/bar.rb
68
+ - test/baz.rb
69
+ - test/foo.rb
70
+ - test/test_helper.rb
71
+ - test/unit/formatters_test.rb
72
+ - test/unit/has_setting_test.rb
73
+ has_rdoc: true