has_setting 0.4.6

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/.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