settings_on_rails 0.1.30 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1902e352d4b86e9b07fb40e0b3364a32ebf810f2
4
- data.tar.gz: 0ff9fa4d7d46470da596aab09983f07415b58980
3
+ metadata.gz: fbb69ae794bd085f9996cb04e3246afec69c2d50
4
+ data.tar.gz: 1994e3ead98a2075bdd71d0b406ffcd9a98e3487
5
5
  SHA512:
6
- metadata.gz: a23b884b80b9b6fc85920e42cc5b368a680176bd78e8e6910570ec6cf1b907bb39d42bc17f16523f99024a4994689f47b5b878a4a3a14bae5e13247543f700f8
7
- data.tar.gz: 49efc61fc8d8f6efabdcfaee240db7f47032084c622fbf75ba79d4899385f6944326cf4de84a1a66d8cfa7d5c68caf77c7846e2555df543647498ee5fecc7a90
6
+ metadata.gz: 5cfd24abffdbc9c209d7e921e19f33e0170b10fafa25682546119402c7c0897a6706388fc0172b029dce06a4504827eb2f7d9de3c69ba675deda69e2e2d787ee
7
+ data.tar.gz: cc40d8e46d5ed04e2757ce10f6b5c58d33d099bce48f9680a03dd3b578e8f83108b0c95774cfb290568f8a52856b3bda09dc8b6f6668c9b86e31def8bc121ac7
data/Gemfile CHANGED
@@ -4,4 +4,4 @@ source 'https://rubygems.org'
4
4
  gemspec
5
5
 
6
6
  gem 'coveralls', require: false
7
-
7
+ gem 'byebug'
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # SettingsOnRails
1
+ # Settings on Rails
2
2
  [![Build Status](https://travis-ci.org/allenwq/settings_on_rails.svg?branch=master)](https://travis-ci.org/allenwq/settings_on_rails)
3
3
  [![Coverage Status](https://coveralls.io/repos/allenwq/settings_on_rails/badge.svg?branch=master)](https://coveralls.io/r/allenwq/settings_on_rails?branch=master)
4
4
 
@@ -46,7 +46,6 @@ Get settings
46
46
  @blog.settings(:theme).background_color # returns 'blue'
47
47
 
48
48
  @blog.settings(:post).pagination # returns nil if not set
49
-
50
49
  ```
51
50
 
52
51
  ## Default Values
@@ -56,7 +55,8 @@ class Blog < ActiveRecord::Base
56
55
  has_settings_on :column
57
56
 
58
57
  has_settings do |s|
59
- s.has_key :theme, defaults:{ background_color: 'red', text_size: 50 }
58
+ s.key :theme, defaults:{ background_color: 'red', text_size: 50 }
59
+ s.attr :title, default: 'My Space'
60
60
  end
61
61
  end
62
62
  ```
@@ -64,24 +64,38 @@ OR
64
64
  ```ruby
65
65
  class Blog < ActiveRecord::Base
66
66
  has_settings_on :column do |s|
67
- s.define :theme, defaults:{ background_color: 'red', text_size: 50 }
67
+ s.key :theme, defaults:{ background_color: 'red', text_size: 50 }
68
+ s.attr :title, default: 'My Space'
68
69
  end
69
70
  end
70
71
  ```
71
72
 
72
- ## Nested/Multiple Keys
73
-
73
+ You can get these defaults by:
74
+ ```ruby
75
+ @blog.settings(:theme).background_color # 'red'
76
+ @blog.settings(:theme).text_size # 50
77
+ @blog.settings.title # 'My Space'
78
+ ```
74
79
 
80
+ ## Nested Keys
81
+ Settings on Rails supports nested keys
75
82
  ```ruby
76
- @blog.settings(:theme, :homepage).background_color = 'white'
77
- #OR
83
+ # Set
78
84
  @blog.settings(:theme).settings(:homepage).background_color = 'white'
85
+ # Get
86
+ @blog.settings(:theme).settings(:homepage).background_color # 'white'
87
+ ```
79
88
 
89
+ # Multiple Keys
90
+ You can also define you nested keys in following ways, it's equal to nested keys
91
+ ```ruby
92
+ # Set
93
+ @blog.settings(:theme, :homepage).background_color = 'white'
94
+ # Get
80
95
  @blog.settings(:theme, :homepage).background_color # 'white'
81
- #OR
82
- @blog.settings(:theme).settings(:homepage).background_color # 'white'
83
96
  ```
84
97
 
98
+
85
99
  ## Method Name Customization
86
100
  You can customize the name of the settings method
87
101
  ```ruby
@@ -3,20 +3,24 @@ module SettingsOnRails
3
3
  extend ActiveSupport::Concern
4
4
 
5
5
  module ClassMethods
6
- def has_settings_on(column, options = {})
7
- SettingsColumn.setup(self, column)
6
+ def has_settings_on(column, options = {}, &block)
7
+ Configuration.init(self, column)
8
8
 
9
9
  method_name = options[:method] || :settings
10
10
  define_method method_name do |*keys|
11
- column = SettingsColumn.check!(self)
11
+ column = Configuration.check!(self)
12
12
 
13
- SettingsHandler.new(keys, self, column, method_name)
13
+ Settings.new(keys, self, column, method_name)
14
14
  end
15
+
16
+ has_settings(&block)
15
17
  end
16
18
 
17
- def has_settings
18
- raise NoSettingsColumnError unless SettingsColumn::NAME
19
- yield if block_given?
19
+ def has_settings(*keys)
20
+ settings = HasSettings.new(keys, self, Configuration::DEFAULTS_COLUMN)
21
+ yield settings if block_given?
22
+
23
+ settings
20
24
  end
21
25
  end
22
26
  end
@@ -0,0 +1,54 @@
1
+ module SettingsOnRails
2
+ module Configuration
3
+ NAME_COLUMN = :settings_column_name
4
+ DEFAULTS_COLUMN = :default_settings
5
+
6
+ # Initialize needed variables for given class
7
+ # @param [ActiveRecord] klass, the Model who has settings
8
+ # @param [Symbol] column, the column to store settings
9
+ def self.init(klass, column)
10
+ raise ArgumentError.new("has_settings_on: symbol expected, but got a #{column.class}") unless column.is_a?(Symbol)
11
+
12
+ klass.class_eval do
13
+ class_attribute Configuration::NAME_COLUMN, Configuration::DEFAULTS_COLUMN
14
+
15
+ serialize column, Hash
16
+ Configuration::init_defaults_column(self)
17
+ Configuration::init_name_column(self, column)
18
+ end
19
+ end
20
+
21
+ # Returns the name of settings column for that instance
22
+ def self.column_name(instance)
23
+ instance.class.send(Configuration::NAME_COLUMN)
24
+ end
25
+
26
+ # Check for the validity of the settings column
27
+ # Returns the column name if valid
28
+ def self.check!(instance)
29
+ settings_column_name = column_name(instance)
30
+ raise NoSettingsColumnError unless settings_column_name
31
+ raise ColumnNotExistError unless instance.has_attribute?(settings_column_name)
32
+ raise InvalidColumnTypeError if column_type_not_text?(instance, settings_column_name)
33
+
34
+ settings_column_name
35
+ end
36
+
37
+ # init to Hash {} for data attribute in klass if nil
38
+ def self.init_defaults_column(klass)
39
+ defaults = klass.send(Configuration::DEFAULTS_COLUMN)
40
+ klass.send(Configuration::DEFAULTS_COLUMN.to_s + '=', {}) unless defaults
41
+ end
42
+
43
+ def self.init_name_column(klass, column_name)
44
+ klass.send(Configuration::NAME_COLUMN.to_s + '=', column_name)
45
+ end
46
+
47
+
48
+ private
49
+
50
+ def self.column_type_not_text?(instance, settings_column)
51
+ return true if instance.column_for_attribute(settings_column).try(:sql_type) != 'text'
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,68 @@
1
+ require 'settings_on_rails/key_tree_builder'
2
+
3
+ module SettingsOnRails
4
+ # Creates a object to save default settings
5
+ #
6
+ # @param [Array] keys, the symbol keys in a array
7
+ # @param [Class] target_model, default values will be saved in an class attribute in target model
8
+ # @param [Symbol] column_name, the column name in target_model to save default values
9
+ # @param [HasSettings] parent, parent has_settings object
10
+ class HasSettings < KeyTreeBuilder
11
+ def initialize(keys, target_model, column_name, parent = nil)
12
+ super(keys, target_model, column_name, parent)
13
+
14
+ @target_model = target_model
15
+ @column_name = column_name
16
+ end
17
+
18
+ REGEX_ATTR = /\A([a-z]\w*)\Z/i
19
+
20
+ # Declare a key, with default values
21
+ #
22
+ # @param [Symbol] keys
23
+ # @param [Hash] options, the last param must be an option with a defaults hash
24
+ def key(*keys)
25
+ options = keys.extract_options!
26
+ raise ArgumentError.new("has_settings: Option :defaults expected, but got #{options.keys.join(', ')}") unless options.blank? || (options.keys == [:defaults])
27
+ keys.each do |key_name|
28
+ raise ArgumentError.new("has_settings: symbol expected, but got a #{key_name.class}") unless key_name.is_a?(Symbol)
29
+ end
30
+
31
+ options[:defaults].each do |k, v|
32
+ has_settings(*keys).attr(k, default: v)
33
+ end
34
+ end
35
+
36
+ # Declare an attribute with default value
37
+ #
38
+ # @param [Symbol] value
39
+ # @param [Hash] options, options with a default Hash
40
+ def attr(value, options = {})
41
+ raise ArgumentError.new("has_settings: symbol expected, but got a #{value.class}") unless value.is_a?(Symbol)
42
+ raise ArgumentError.new("has_settings: Option :default expected, but got #{options.keys.join(', ')}") unless options.blank? || (options.keys == [:default])
43
+
44
+ default_value = options[:default]
45
+ raise 'Error' unless value.to_s =~ REGEX_ATTR
46
+
47
+ _set_value(value.to_s, default_value)
48
+ end
49
+
50
+ def has_settings(*keys)
51
+ settings = HasSettings.new(keys, @target_model, @column_name, self)
52
+ yield settings if block_given?
53
+ settings
54
+ end
55
+
56
+ private
57
+
58
+ def _set_value(name, v)
59
+ build_nodes
60
+
61
+ if v.nil?
62
+ current_node.delete(name)
63
+ else
64
+ current_node[name] = v
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,68 @@
1
+ module SettingsOnRails
2
+ class KeyTreeBuilder
3
+ attr_accessor :keys, :parent
4
+
5
+ # All keys must be symbols, and attributes are strings
6
+ # Thus we can differentiate settings(:key).attr and settings(:key, :attr)
7
+ def initialize(keys, target_obj, target_column_name, parent)
8
+ keys.each do |key_name|
9
+ raise ArgumentError.new("has_settings: symbol expected, but got a #{key_name.class}") unless key_name.is_a?(Symbol)
10
+ end
11
+ @keys = keys
12
+ @target_obj = target_obj
13
+ @column_name = target_column_name
14
+ @parent = parent
15
+ end
16
+
17
+ # Returns column[key_chain[0]][key_chain[1]][...]
18
+ def current_node
19
+ return nil unless _key_node_exist?
20
+
21
+ _key_chain.inject(_target_column) { |h, key| h[key] }
22
+ end
23
+
24
+ # Call this method before set any values
25
+ def build_nodes
26
+ value = _target_column
27
+
28
+ for key in _key_chain
29
+ value[key] = {} unless value[key]
30
+ value = value[key]
31
+ end
32
+ end
33
+
34
+ private
35
+
36
+ def _key_node_exist?
37
+ value = _target_column
38
+
39
+ for key in _key_chain
40
+ value = value[key]
41
+ return false unless value
42
+ end
43
+
44
+ true
45
+ end
46
+
47
+ # Returns a key chain which includes all parent's keys and self keys
48
+ def _key_chain
49
+ handler = self
50
+ key_chain = []
51
+
52
+ begin
53
+ key_chain = handler.keys + key_chain
54
+ handler = handler.parent
55
+ end while handler
56
+
57
+ key_chain
58
+ end
59
+
60
+ def _target_column
61
+ if @target_obj.respond_to?(:read_attribute)
62
+ @target_obj.read_attribute(@column_name.to_sym)
63
+ else
64
+ @target_obj.send(@column_name.to_sym)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,65 @@
1
+ require 'settings_on_rails/key_tree_builder'
2
+
3
+ module SettingsOnRails
4
+ class Settings < KeyTreeBuilder
5
+ def initialize(keys, target_object, settings_column_name, method_name, parent = nil)
6
+ super(keys, target_object, settings_column_name, parent)
7
+ @target_object = target_object
8
+ @column_name = settings_column_name
9
+ @method_name = method_name
10
+
11
+ self.class_eval do
12
+ define_method(method_name, instance_method(:_settings))
13
+ end
14
+ end
15
+
16
+ REGEX_SETTER = /\A([a-z]\w*)=\Z/i
17
+ REGEX_GETTER = /\A([a-z]\w*)\Z/i
18
+
19
+ def respond_to?(method_name, include_priv=false)
20
+ super || method_name.to_s =~ REGEX_SETTER
21
+ end
22
+
23
+ def method_missing(method_name, *args, &block)
24
+ if method_name.to_s =~ REGEX_SETTER && args.size == 1
25
+ _set_value($1, args.first)
26
+ elsif method_name.to_s =~ REGEX_GETTER && args.size == 0
27
+ _get_value($1) || _default_settings($1)
28
+ else
29
+ super
30
+ end
31
+ end
32
+
33
+ private
34
+
35
+ def _settings(*keys)
36
+ raise ArgumentError, 'wrong number of arguments (0 for 1..n)' if keys.size == 0
37
+
38
+ Settings.new(keys, @target_object, @column_name, @method_name, self)
39
+ end
40
+
41
+ def _get_value(name)
42
+ node = current_node
43
+
44
+ node[name] if node
45
+ end
46
+
47
+ def _set_value(name, v)
48
+ return if _get_value(name) == v
49
+
50
+ @target_object.send("#{@column_name}_will_change!")
51
+ build_nodes
52
+
53
+ if v.nil?
54
+ current_node.delete(name)
55
+ else
56
+ current_node[name] = v
57
+ end
58
+ end
59
+
60
+ def _default_settings(name)
61
+ default_node = KeyTreeBuilder.new(keys, @target_object.class, Configuration::DEFAULTS_COLUMN, parent).current_node
62
+ default_node[name] if default_node
63
+ end
64
+ end
65
+ end
@@ -1,3 +1,3 @@
1
1
  module SettingsOnRails
2
- VERSION = '0.1.30'
2
+ VERSION = '0.2.0'
3
3
  end
@@ -1,6 +1,7 @@
1
1
  require 'settings_on_rails/base'
2
- require 'settings_on_rails/settings_column'
3
- require 'settings_on_rails/settings_handler'
2
+ require 'settings_on_rails/configuration'
3
+ require 'settings_on_rails/settings'
4
+ require 'settings_on_rails/has_settings'
4
5
  require 'settings_on_rails/exceptions'
5
6
  require 'settings_on_rails/version'
6
7
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: settings_on_rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.30
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - ALLEN WANG QIANG
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-04-09 00:00:00.000000000 Z
11
+ date: 2015-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -98,9 +98,11 @@ files:
98
98
  - Rakefile
99
99
  - lib/settings_on_rails.rb
100
100
  - lib/settings_on_rails/base.rb
101
+ - lib/settings_on_rails/configuration.rb
101
102
  - lib/settings_on_rails/exceptions.rb
102
- - lib/settings_on_rails/settings_column.rb
103
- - lib/settings_on_rails/settings_handler.rb
103
+ - lib/settings_on_rails/has_settings.rb
104
+ - lib/settings_on_rails/key_tree_builder.rb
105
+ - lib/settings_on_rails/settings.rb
104
106
  - lib/settings_on_rails/version.rb
105
107
  - settings_on_rails.gemspec
106
108
  homepage: https://github.com/allenwq/settings_on_rails
@@ -1,32 +0,0 @@
1
- module SettingsOnRails
2
- module SettingsColumn
3
- NAME = :__settings_column
4
-
5
- def self.setup(klass, column)
6
- klass.class_eval do
7
- class << self; attr_accessor SettingsColumn::NAME; end
8
-
9
- serialize column, Hash
10
- self.send(SettingsColumn::NAME.to_s + '=', column.to_sym)
11
- end
12
- end
13
-
14
- def self.column(instance)
15
- instance.class.send(SettingsColumn::NAME)
16
- end
17
-
18
- def self.check!(instance)
19
- settings_column = column(instance)
20
- raise NoSettingsColumnError unless settings_column
21
- raise ColumnNotExistError unless instance.has_attribute?(settings_column)
22
- raise InvalidColumnTypeError if column_type_not_text?(instance, settings_column)
23
-
24
- settings_column
25
- end
26
-
27
- private
28
- def self.column_type_not_text?(instance, settings_column)
29
- return true if instance.column_for_attribute(settings_column).try(:sql_type) != 'text'
30
- end
31
- end
32
- end
@@ -1,118 +0,0 @@
1
- module SettingsOnRails
2
- class SettingsHandler
3
- PREFIX = '_'
4
-
5
- attr_accessor :keys, :parent
6
- def initialize(keys, target_object, column, method_name, parent = nil)
7
- @keys = _prefix(keys.dup)
8
- @target_object = target_object
9
- @column = column
10
- @method_name = method_name
11
- @parent = parent
12
-
13
- self.class_eval do
14
- define_method(method_name, instance_method(:_settings))
15
- end
16
- end
17
-
18
- REGEX_SETTER = /\A([a-z]\w*)=\Z/i
19
- REGEX_GETTER = /\A([a-z]\w*)\Z/i
20
-
21
- def respond_to?(method_name, include_priv=false)
22
- super || method_name.to_s =~ REGEX_SETTER
23
- end
24
-
25
- def method_missing(method_name, *args, &block)
26
- if method_name.to_s =~ REGEX_SETTER && args.size == 1
27
- _set_value($1, args.first)
28
- elsif method_name.to_s =~ REGEX_GETTER && args.size == 0
29
- _get_value($1)
30
- else
31
- super
32
- end
33
- end
34
-
35
- private
36
- def _settings(*keys)
37
- raise ArgumentError, 'wrong number of arguments (0 for 1..n)' if keys.size == 0
38
-
39
- SettingsHandler.new(keys, @target_object, @column, @method_name, self)
40
- end
41
-
42
- def _get_value(name)
43
- node = _get_key_node
44
-
45
- if node
46
- node[name]
47
- else
48
- nil
49
- end
50
- end
51
-
52
- def _set_value(name, v)
53
- return if _get_value(name) == v
54
-
55
- @target_object.send("#{@column}_will_change!")
56
- _build_key_tree
57
- node = _get_key_node
58
- if v.nil?
59
- node.delete(name)
60
- else
61
- node[name] = v
62
- end
63
- end
64
-
65
- def _key_node_exist?
66
- value = _target_column
67
-
68
- for key in _key_chain
69
- value = value[key]
70
- return false unless value
71
- end
72
-
73
- true
74
- end
75
-
76
- def _get_key_node
77
- ret = _key_node_exist?
78
- return nil unless ret
79
-
80
- _key_chain.inject(_target_column) { |h, key| h[key] }
81
- end
82
-
83
- def _build_key_tree
84
- value = _target_column
85
-
86
- for key in _key_chain
87
- value[key] = {} unless value[key]
88
- value = value[key]
89
- end
90
- end
91
-
92
- def _target_column
93
- @target_object.read_attribute(@column.to_sym)
94
- end
95
-
96
- # prefix keys with _, to differentiate `settings(:key_a, :key_b)` and settings(:key_a).key_b
97
- # thus _ becomes an reserved field
98
- def _prefix(keys)
99
- for i in 0..(keys.length - 1)
100
- keys[i] = (PREFIX + keys[i].to_s).to_sym
101
- end
102
- keys
103
- end
104
-
105
- # Returns a key chain which includes all parent's keys and self keys
106
- def _key_chain
107
- handler = self
108
- key_chain = []
109
-
110
- begin
111
- key_chain = handler.keys + key_chain
112
- handler = handler.parent
113
- end while handler
114
-
115
- key_chain
116
- end
117
- end
118
- end