simplificator-has_setting 0.2.0 → 0.3.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/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 3
3
+ :patch: 6
4
+ :major: 0
data/lib/has_setting.rb CHANGED
@@ -1,7 +1,13 @@
1
- # Intialises the gem by including some extension to ActiveRecord::Base
1
+ # Intialize the gem by including some extension to ActiveRecord::Base
2
2
  require File.dirname(__FILE__) + '/has_setting/ar_extensions'
3
+ require File.dirname(__FILE__) + '/has_setting/formatters'
3
4
  require File.dirname(__FILE__) + '/has_setting/setting'
4
5
  ActiveRecord::Base.class_eval do
5
6
  include(HasSetting::InstanceMethods)
6
7
  extend(HasSetting::ClassMethods)
7
- end
8
+ end
9
+
10
+
11
+ HasSetting::Formatters.register_formatter(:string, HasSetting::Formatters::StringFormatter.new)
12
+ HasSetting::Formatters.register_formatter(:float, HasSetting::Formatters::FloatFormatter.new)
13
+ HasSetting::Formatters.register_formatter(:int, HasSetting::Formatters::IntFormatter.new)
@@ -1,17 +1,11 @@
1
1
  module HasSetting
2
+
2
3
  module InstanceMethods
3
4
  def write_setting(name, value)
4
5
  # find an existing setting or build a new one
5
- setting = self.settings.find_by_name(name)
6
+ setting = self.settings.detect() {|item| item.name == name }
6
7
  setting = self.settings.build(:name => name) if setting.blank?
7
8
  setting.value = value
8
- # save only if parent has been saved. else we expect a parent.save call
9
- # which will cascade to the children since they were created with build()
10
- unless self.new_record?
11
- setting.save
12
- # reload collection so the read_setting() finds the settings with 'detect()'
13
- self.settings.reload
14
- end
15
9
  end
16
10
 
17
11
  def read_setting(name)
@@ -22,45 +16,61 @@ module HasSetting
22
16
  end
23
17
 
24
18
  module ClassMethods
25
- def self.extended(extender)
26
- extender.class_eval do
27
- # AR association to settings
28
- has_many(:settings, :as => :owner, :class_name => 'HasSetting::Setting', :foreign_key => :owner_id)
29
- # a hash where we can store options for the settings
30
- write_inheritable_attribute(:has_setting_options, {})
31
- class_inheritable_reader(:has_setting_options)
32
- end
33
- end
34
-
35
-
36
-
37
- #
38
19
  # Setup of the getter/setter
39
20
  def has_setting(name, options = {})
40
21
  name = name.to_s
41
- raise ArgumentError.new("Setting name must not be blank") if name.blank?
42
- # default settings
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', :foreign_key => :owner_id)
28
+ after_save(:save_has_setting_association)
29
+ @has_setting_options = {}
30
+ def self.has_setting_options
31
+ @has_setting_options
32
+ end
33
+
34
+ private
35
+ # Callback to save settings
36
+ def save_has_setting_association
37
+ self.settings.each do |setting|
38
+ if setting.changed?
39
+ setting.save
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+
48
+ raise ArgumentError.new("Setting #{name }is already defined on #{self.name}") if self.has_setting_options.has_key?(name)
49
+
50
+ # default options
43
51
  options[:type] ||= :string # treat as string
44
52
  options[:default] ||= nil # no default value
45
- has_setting_options[name] = options
53
+ self.has_setting_options[name] = options
46
54
 
55
+ # setter
47
56
  define_method("#{name}=".intern) do |value|
48
- value = value.nil? ? nil : value.to_s
49
- write_setting(name, value)
57
+ formatter = HasSetting::Formatters.for_type(options[:type])
58
+ write_setting(name, formatter.to_s(value))
50
59
  end
51
60
 
52
61
  # getter
53
- define_method(name) do |*args|
62
+ define_method(name) do |*args|
54
63
  setting = read_setting(name)
55
- options = args.first || has_setting_options[name]
64
+ options = args.first || self.class.has_setting_options[name]
56
65
  return options[:default] if setting.nil?
57
-
58
- case options[:type]
59
- when :string : setting.value
60
- when :int : setting.value.nil? ? nil : setting.value.to_i
61
- when :float : setting.value.nil? ? nil : setting.value.to_f
62
- else raise ArgumentError.new("Unsupported type: #{options[:type]}")
63
- end
66
+ formatter = HasSetting::Formatters.for_type(options[:type])
67
+ formatter.to_type(setting.value)
68
+ #case options[:type]
69
+ # when :string : setting.value
70
+ # when :int : setting.value.nil? ? nil : setting.value.to_i
71
+ # when :float : setting.value.nil? ? nil : setting.value.to_f
72
+ # else raise ArgumentError.new("Unsupported type: #{options[:type]}")
73
+ #end
64
74
  end
65
75
  end
66
76
  end
@@ -0,0 +1,72 @@
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 to_type(value)
40
+ value
41
+ end
42
+ def safe_to_s(value)
43
+ value.to_s
44
+ end
45
+ end
46
+ # Formatter for ints
47
+ # Throws ArgumentError if value can not be converted
48
+ class IntFormatter < NilSafeFormatter
49
+ # Integer() does not treat "2.6" the same as 2.6
50
+ # while 2.6 is a valid Intger() (-> 2), "2.6" is not.
51
+ # Note that "2" is a valid argument for Integer() and that "".to_i is valid
52
+ # while Integer('') is not...
53
+ # Circumvent this by first convert with Float() so everything obeys to the same rules
54
+ def safe_to_type(value)
55
+ Integer(Float(value))
56
+ end
57
+ def safe_to_s(value)
58
+ Integer(Float(value)).to_s
59
+ end
60
+ end
61
+ # Formatter for float values
62
+ # Throws ArgumentError if value can not be converted
63
+ class FloatFormatter < NilSafeFormatter
64
+ def safe_to_type(value)
65
+ Float(value)
66
+ end
67
+ def safe_to_s(value)
68
+ Float(value).to_s
69
+ end
70
+ end
71
+ end
72
+ end
@@ -3,5 +3,5 @@
3
3
  # keyword as column name)
4
4
  #
5
5
  class HasSetting::Setting < ActiveRecord::Base
6
- belongs_to(:owner, :polymorphic => true)
6
+ belongs_to(:owner, :polymorphic => true)
7
7
  end
data/test/baz.rb ADDED
@@ -0,0 +1,2 @@
1
+ class Baz < ActiveRecord::Base
2
+ end
data/test/test_helper.rb CHANGED
@@ -12,8 +12,9 @@ ActiveRecord::Base.establish_connection(
12
12
  )
13
13
 
14
14
  ActiveRecord::Base.connection.drop_table(:settings) rescue ActiveRecord::StatementInvalid
15
- ActiveRecord::Base.connection.drop_table(:foos) rescue ActiveRecord::StatementInvalid
16
- ActiveRecord::Base.connection.drop_table(:bars) rescue ActiveRecord::StatementInvalid
15
+ [:foos, :bars, :bazs].each do |table|
16
+ ActiveRecord::Base.connection.drop_table(table) rescue ActiveRecord::StatementInvalid
17
+ end
17
18
 
18
19
 
19
20
 
@@ -23,13 +24,13 @@ ActiveRecord::Base.connection.create_table(:settings) do |table|
23
24
  table.string(:owner_type, :limit => 255, :null => false)
24
25
  table.integer(:owner_id, :null => false)
25
26
  end
26
- ActiveRecord::Base.connection.create_table(:foos) do |table|
27
- end
28
- ActiveRecord::Base.connection.create_table(:bars) do |table|
27
+ [:foos, :bars, :bazs].each do |table|
28
+ ActiveRecord::Base.connection.create_table(table) do |t|
29
+ end
29
30
  end
30
-
31
31
  ActiveRecord::Base.logger = Logger.new(STDOUT)
32
32
  ActiveRecord::Base.logger.level = Logger::DEBUG # change to DEBUG if you want to see something :-)
33
33
 
34
34
  require File.join(File.dirname(__FILE__), 'foo')
35
- require File.join(File.dirname(__FILE__), 'bar')
35
+ require File.join(File.dirname(__FILE__), 'bar')
36
+ require File.join(File.dirname(__FILE__), 'baz')
@@ -0,0 +1,66 @@
1
+ require File.dirname(__FILE__) + '/../test_helper'
2
+ include HasSetting
3
+ class FormattersTest < Test::Unit::TestCase
4
+ def test_for_type
5
+ [:string, :float, :int].each do |symbol|
6
+ assert(Formatters.for_type(symbol), "No formatter for #{symbol}")
7
+ end
8
+ assert_raises(ArgumentError) do
9
+ Formatters.for_type(:rarararararara_i_do_not_exist)
10
+ end
11
+ end
12
+
13
+ def test_string_formatter()
14
+ f = Formatters::StringFormatter.new
15
+ assert_equal('', f.to_s(''))
16
+ assert_equal('a', f.to_s('a'))
17
+ assert_equal('', f.to_type(''))
18
+ assert_equal('a', f.to_type('a'))
19
+ end
20
+
21
+ def test_int_formatter()
22
+ f = Formatters::IntFormatter.new
23
+ assert_raises(ArgumentError) do
24
+ f.to_s('')
25
+ end
26
+ assert_raises(ArgumentError) do
27
+ f.to_s('asas')
28
+ end
29
+ assert_nil(f.to_s(nil))
30
+ assert_equal('2', f.to_s(2.6))
31
+ assert_equal('2', f.to_s(2))
32
+
33
+ assert_raises(ArgumentError) do
34
+ f.to_type('')
35
+ end
36
+ assert_raises(ArgumentError) do
37
+ f.to_type('asas')
38
+ end
39
+ assert_nil(f.to_type(nil))
40
+ assert_equal(2, f.to_type('2'))
41
+ assert_equal(2, f.to_type('2.6'))
42
+ end
43
+
44
+ def test_float_formatter()
45
+ f = Formatters::FloatFormatter.new
46
+ assert_raises(ArgumentError) do
47
+ f.to_s('')
48
+ end
49
+ assert_raises(ArgumentError) do
50
+ f.to_s('asas')
51
+ end
52
+ assert_nil(f.to_s(nil))
53
+ assert_equal('2.6', f.to_s(2.6))
54
+ assert_equal('2.0', f.to_s(2))
55
+
56
+ assert_raises(ArgumentError) do
57
+ f.to_type('')
58
+ end
59
+ assert_raises(ArgumentError) do
60
+ f.to_type('asas')
61
+ end
62
+ assert_nil(f.to_type(nil))
63
+ assert_equal(2.0, f.to_type('2'))
64
+ assert_equal(2.6, f.to_type('2.6'))
65
+ end
66
+ end
@@ -4,9 +4,10 @@ class HasSettingTest < Test::Unit::TestCase
4
4
  def setup()
5
5
  @foo = Foo.create!
6
6
  @bar = Bar.create!
7
+ @baz = Baz.create!
7
8
  end
8
9
 
9
- def test_setting_has_accessor
10
+ def test_setting_has_accessors
10
11
  assert @foo.respond_to?(:setting_1)
11
12
  assert @foo.respond_to?(:setting_1=)
12
13
  end
@@ -21,6 +22,7 @@ class HasSettingTest < Test::Unit::TestCase
21
22
  def test_write_setting
22
23
  count_before = HasSetting::Setting.count
23
24
  @foo.write_setting('name', 'value1')
25
+ @foo.save!
24
26
  assert_equal(count_before + 1, HasSetting::Setting.count)
25
27
  setting = @foo.read_setting('name')
26
28
  assert setting
@@ -37,6 +39,7 @@ class HasSettingTest < Test::Unit::TestCase
37
39
  count_before = HasSetting::Setting.count
38
40
  assert(!@foo.setting_1)
39
41
  @foo.setting_1 = 'bli'
42
+ @foo.save!
40
43
  assert_equal(count_before + 1, HasSetting::Setting.count)
41
44
  assert_equal('bli', @foo.setting_1)
42
45
  @foo.setting_1 = 'bla'
@@ -46,12 +49,15 @@ class HasSettingTest < Test::Unit::TestCase
46
49
  def test_different_classes_do_not_share_setting
47
50
  count_before = HasSetting::Setting.count
48
51
  @foo.setting_1 = 'foo'
52
+ @foo.save!
49
53
  @bar.setting_1 = 'bar'
54
+ @bar.save!
50
55
  assert_equal(count_before + 2, HasSetting::Setting.count)
51
56
 
52
57
  assert_equal('foo', @foo.setting_1)
53
58
  assert_equal('bar', @bar.setting_1)
54
59
  end
60
+
55
61
 
56
62
  def test_has_nil_setting
57
63
  @foo.setting_1 = nil
@@ -95,4 +101,9 @@ class HasSettingTest < Test::Unit::TestCase
95
101
  assert_equal('radabumm', my_foo.with_default)
96
102
  end
97
103
 
104
+ def test_not_everyone_has_settings_association
105
+ assert_equal(true, @foo.respond_to?(:settings))
106
+ assert_equal(true, @bar.respond_to?(:settings))
107
+ assert_equal(false, @baz.respond_to?(:settings))
108
+ end
98
109
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: simplificator-has_setting
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simplificator GmbH
@@ -9,12 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-02-01 00:00:00 -08:00
12
+ date: 2009-02-04 00:00:00 -08:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description: Stores settings as key/value pairs in a settings table and provides accessors for them on the owning object
17
- email: info@simplificator.com
16
+ description: TODO
17
+ email: pascal.betz@simplificator.com
18
18
  executables: []
19
19
 
20
20
  extensions: []
@@ -22,19 +22,26 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
- - lib/has_setting.rb
25
+ - VERSION.yml
26
+ - lib/has_setting
26
27
  - lib/has_setting/ar_extensions.rb
28
+ - lib/has_setting/formatters.rb
27
29
  - lib/has_setting/setting.rb
30
+ - lib/has_setting.rb
28
31
  - test/bar.rb
32
+ - test/baz.rb
29
33
  - test/foo.rb
30
34
  - test/test_helper.rb
35
+ - test/unit
36
+ - test/unit/formatters_test.rb
31
37
  - test/unit/has_setting_test.rb
32
- - README.rdoc
33
- has_rdoc: false
34
- homepage: http://simplificator.com/en/lab
38
+ - test/unit/test.sqlite3
39
+ has_rdoc: true
40
+ homepage: http://github.com/pascalbetz/has_setting
35
41
  post_install_message:
36
- rdoc_options: []
37
-
42
+ rdoc_options:
43
+ - --inline-source
44
+ - --charset=UTF-8
38
45
  require_paths:
39
46
  - lib
40
47
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -55,6 +62,6 @@ rubyforge_project:
55
62
  rubygems_version: 1.2.0
56
63
  signing_key:
57
64
  specification_version: 2
58
- summary: Basic Setting/Properties for ActiveRecord models
65
+ summary: TODO
59
66
  test_files: []
60
67