fusu 0.2.0 → 0.2.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5b0df9b35cbcd397ff4eaa1702330e0e0255f84a
4
- data.tar.gz: 40b28b9e12efa5cf79f00171311c62e3616e0a37
2
+ SHA256:
3
+ metadata.gz: abe9860bb714eb06dff8a1df416ee4002883d1f74a66166eb9ca61331f10cf35
4
+ data.tar.gz: 0472026d1cc75b37592ad0d61618c4e7dc3c1bc2711a8a73c1761fa3f95ac213
5
5
  SHA512:
6
- metadata.gz: 028aaafaf603c46939c503d0f4b422fc4649503968d620b950e647f1b0ae4f33dce8d0dd5b7839e58d48bbc5cc6df1e79038c6b9d8478619a5ad5b4b81e34ad1
7
- data.tar.gz: 28e4994d51bcdb01fd825e0382f5e4c312006a649872443a5ccb31f8ba3a5ff2bf222d86d0e53f40bf846926a644d1b35cb2558cd8f074a24c6d3eb809cec4b2
6
+ metadata.gz: 4a6b3ff3d19907727179ec8fe2646b3e88eb1047f4e90423bad354766e07c464e4b909807f9d75b05c9ad1359e1af4f5ee1e62359ea2a5ed35560f9f8122e9e5
7
+ data.tar.gz: db21445a5c0d8d4aa3f351f9ba42bcde5d293fd9a37bddac66f350ace8678fac0ab0bd54de3e3763fc07e296e0ba77beebd898887db6037c6facaa55b9f076eb
@@ -10,3 +10,5 @@ require "fusu/hash"
10
10
  require "fusu/string"
11
11
  require "fusu/regexp"
12
12
  require "fusu/hash_with_indifferent_access"
13
+ require "fusu/configurable"
14
+ require "fusu/concern"
@@ -9,5 +9,22 @@ module Fusu
9
9
  [object]
10
10
  end
11
11
  end
12
+
13
+ # Extracts options from a set of arguments. Removes and returns the last
14
+ # element in the array if it's a hash, otherwise returns a blank hash.
15
+ #
16
+ # def options(*args)
17
+ # args.extract_options!
18
+ # end
19
+ #
20
+ # options(1, 2) # => {}
21
+ # options(1, 2, a: :b) # => {:a=>:b}
22
+ def self.extract_options!(ary)
23
+ if ary.last.is_a?(::Hash) && Fusu::Hash.extractable_options?(ary.last)
24
+ ary.pop
25
+ else
26
+ {}
27
+ end
28
+ end
12
29
  end
13
30
  end
@@ -11,5 +11,9 @@ module Fusu
11
11
  false
12
12
  end
13
13
  end
14
+
15
+ def presence(elem)
16
+ blank?(elem) ? nil : elem
17
+ end
14
18
  end
15
19
  end
@@ -0,0 +1,146 @@
1
+ require "fusu/concern"
2
+ require "fusu/ordered_options"
3
+
4
+ module Fusu
5
+ # Configurable provides a <tt>config</tt> method to store and retrieve
6
+ # configuration options as an <tt>OrderedHash</tt>.
7
+ module Configurable
8
+ extend Fusu::Concern
9
+
10
+ class Configuration < Fusu::InheritableOptions
11
+ def compile_methods!
12
+ self.class.compile_methods!(keys)
13
+ end
14
+
15
+ # Compiles reader methods so we don't have to go through method_missing.
16
+ def self.compile_methods!(keys)
17
+ keys.reject { |m| method_defined?(m) }.each do |key|
18
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
19
+ def #{key}; _get(#{key.inspect}); end
20
+ RUBY
21
+ end
22
+ end
23
+ end
24
+
25
+ module ClassMethods
26
+ def config
27
+ @_config ||= if respond_to?(:superclass) && superclass.respond_to?(:config)
28
+ superclass.config.inheritable_copy
29
+ else
30
+ # create a new "anonymous" class that will host the compiled reader methods
31
+ Class.new(Configuration).new
32
+ end
33
+ end
34
+
35
+ def configure
36
+ yield config
37
+ end
38
+
39
+ # Allows you to add shortcut so that you don't have to refer to attribute
40
+ # through config. Also look at the example for config to contrast.
41
+ #
42
+ # Defines both class and instance config accessors.
43
+ #
44
+ # class User
45
+ # include Fusu::Configurable
46
+ # config_accessor :allowed_access
47
+ # end
48
+ #
49
+ # User.allowed_access # => nil
50
+ # User.allowed_access = false
51
+ # User.allowed_access # => false
52
+ #
53
+ # user = User.new
54
+ # user.allowed_access # => false
55
+ # user.allowed_access = true
56
+ # user.allowed_access # => true
57
+ #
58
+ # User.allowed_access # => false
59
+ #
60
+ # The attribute name must be a valid method name in Ruby.
61
+ #
62
+ # class User
63
+ # include Fusu::Configurable
64
+ # config_accessor :"1_Badname"
65
+ # end
66
+ # # => NameError: invalid config attribute name
67
+ #
68
+ # To opt out of the instance writer method, pass <tt>instance_writer: false</tt>.
69
+ # To opt out of the instance reader method, pass <tt>instance_reader: false</tt>.
70
+ #
71
+ # class User
72
+ # include Fusu::Configurable
73
+ # config_accessor :allowed_access, instance_reader: false, instance_writer: false
74
+ # end
75
+ #
76
+ # User.allowed_access = false
77
+ # User.allowed_access # => false
78
+ #
79
+ # User.new.allowed_access = true # => NoMethodError
80
+ # User.new.allowed_access # => NoMethodError
81
+ #
82
+ # Or pass <tt>instance_accessor: false</tt>, to opt out both instance methods.
83
+ #
84
+ # class User
85
+ # include Fusu::Configurable
86
+ # config_accessor :allowed_access, instance_accessor: false
87
+ # end
88
+ #
89
+ # User.allowed_access = false
90
+ # User.allowed_access # => false
91
+ #
92
+ # User.new.allowed_access = true # => NoMethodError
93
+ # User.new.allowed_access # => NoMethodError
94
+ #
95
+ # Also you can pass a block to set up the attribute with a default value.
96
+ #
97
+ # class User
98
+ # include Fusu::Configurable
99
+ # config_accessor :hair_colors do
100
+ # [:brown, :black, :blonde, :red]
101
+ # end
102
+ # end
103
+ #
104
+ # User.hair_colors # => [:brown, :black, :blonde, :red]
105
+ def config_accessor(*names)
106
+ options = Fusu::Array.extract_options!(names)
107
+
108
+ names.each do |name|
109
+ raise NameError.new("invalid config attribute name") unless /\A[_A-Za-z]\w*\z/.match?(name)
110
+
111
+ reader, reader_line = "def #{name}; config.#{name}; end", __LINE__
112
+ writer, writer_line = "def #{name}=(value); config.#{name} = value; end", __LINE__
113
+
114
+ singleton_class.class_eval reader, __FILE__, reader_line
115
+ singleton_class.class_eval writer, __FILE__, writer_line
116
+
117
+ unless options[:instance_accessor] == false
118
+ class_eval reader, __FILE__, reader_line unless options[:instance_reader] == false
119
+ class_eval writer, __FILE__, writer_line unless options[:instance_writer] == false
120
+ end
121
+ send("#{name}=", yield) if block_given?
122
+ end
123
+ end
124
+ private :config_accessor
125
+ end
126
+
127
+ # Reads and writes attributes from a configuration <tt>OrderedHash</tt>.
128
+ #
129
+ # require 'fusu/configurable'
130
+ #
131
+ # class User
132
+ # include Fusu::Configurable
133
+ # end
134
+ #
135
+ # user = User.new
136
+ #
137
+ # user.config.allowed_access = true
138
+ # user.config.level = 1
139
+ #
140
+ # user.config.allowed_access # => true
141
+ # user.config.level # => 1
142
+ def config
143
+ @_config ||= self.class.config.inheritable_copy
144
+ end
145
+ end
146
+ end
@@ -8,5 +8,13 @@ module Fusu
8
8
  extend Keys
9
9
  extend ReverseMerge
10
10
  extend DeepMerge
11
+
12
+ def extractable_options?(elem)
13
+ elem.instance_of?(::Hash)
14
+ end
15
+
16
+ def reject_key(hash, *keys)
17
+ hash.reject{ |k, v| keys.include?(k) }
18
+ end
11
19
  end
12
20
  end
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Fusu
4
+ # Usually key value pairs are handled something like this:
5
+ #
6
+ # h = {}
7
+ # h[:boy] = 'John'
8
+ # h[:girl] = 'Mary'
9
+ # h[:boy] # => 'John'
10
+ # h[:girl] # => 'Mary'
11
+ # h[:dog] # => nil
12
+ #
13
+ # Using +OrderedOptions+, the above code could be reduced to:
14
+ #
15
+ # h = ActiveSupport::OrderedOptions.new
16
+ # h.boy = 'John'
17
+ # h.girl = 'Mary'
18
+ # h.boy # => 'John'
19
+ # h.girl # => 'Mary'
20
+ # h.dog # => nil
21
+ #
22
+ # To raise an exception when the value is blank, append a
23
+ # bang to the key name, like:
24
+ #
25
+ # h.dog! # => raises KeyError: :dog is blank
26
+ #
27
+ class OrderedOptions < ::Hash
28
+ alias_method :_get, :[] # preserve the original #[] method
29
+ protected :_get # make it protected
30
+
31
+ def []=(key, value)
32
+ super(key.to_sym, value)
33
+ end
34
+
35
+ def [](key)
36
+ super(key.to_sym)
37
+ end
38
+
39
+ def method_missing(name, *args)
40
+ name_string = name.to_s
41
+ if name_string.chomp!("=")
42
+ self[name_string] = args.first
43
+ else
44
+ bangs = name_string.chomp!("!")
45
+
46
+ if bangs
47
+ Fusu.presence(self[name_string]) || raise(KeyError.new(":#{name_string} is blank"))
48
+ else
49
+ self[name_string]
50
+ end
51
+ end
52
+ end
53
+
54
+ def respond_to_missing?(name, include_private)
55
+ true
56
+ end
57
+ end
58
+
59
+ # +InheritableOptions+ provides a constructor to build an +OrderedOptions+
60
+ # hash inherited from another hash.
61
+ #
62
+ # Use this if you already have some hash and you want to create a new one based on it.
63
+ #
64
+ # h = ActiveSupport::InheritableOptions.new({ girl: 'Mary', boy: 'John' })
65
+ # h.girl # => 'Mary'
66
+ # h.boy # => 'John'
67
+ class InheritableOptions < OrderedOptions
68
+ def initialize(parent = nil)
69
+ if parent.kind_of?(OrderedOptions)
70
+ # use the faster _get when dealing with OrderedOptions
71
+ super() { |h, k| parent._get(k) }
72
+ elsif parent
73
+ super() { |h, k| parent[k] }
74
+ else
75
+ super()
76
+ end
77
+ end
78
+
79
+ def inheritable_copy
80
+ self.class.new(self)
81
+ end
82
+ end
83
+ end
@@ -1,3 +1,3 @@
1
1
  module Fusu
2
- VERSION = "0.2.0"
2
+ VERSION = "0.2.1"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fusu
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Artur Panyach
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-11-02 00:00:00.000000000 Z
11
+ date: 2018-12-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -74,12 +74,14 @@ files:
74
74
  - lib/fusu/array.rb
75
75
  - lib/fusu/blank.rb
76
76
  - lib/fusu/concern.rb
77
+ - lib/fusu/configurable.rb
77
78
  - lib/fusu/hash.rb
78
79
  - lib/fusu/hash/deep_merge.rb
79
80
  - lib/fusu/hash/except.rb
80
81
  - lib/fusu/hash/keys.rb
81
82
  - lib/fusu/hash/reverse_merge.rb
82
83
  - lib/fusu/hash_with_indifferent_access.rb
84
+ - lib/fusu/ordered_options.rb
83
85
  - lib/fusu/regexp.rb
84
86
  - lib/fusu/string.rb
85
87
  - lib/fusu/string/methods.rb
@@ -105,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
105
107
  version: '0'
106
108
  requirements: []
107
109
  rubyforge_project:
108
- rubygems_version: 2.2.3
110
+ rubygems_version: 2.7.6
109
111
  signing_key:
110
112
  specification_version: 4
111
113
  summary: Functional support