dux 0.3.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,9 +1,11 @@
1
1
  module Dux
2
+ # A string value that is programmatically equivalent to its symbol representation.
2
3
  class IndifferentString < DelegateClass(String)
4
+ # @param [String, Symbol, Dux::IndifferentString, #to_str, #acts_like_string?] stringish
3
5
  def initialize(stringish)
4
6
  stringified =
5
7
  case stringish
6
- when String then stringish
8
+ when String, Dux::IndifferentString then stringish
7
9
  when Symbol then stringish.to_s
8
10
  when Dux[:to_str] then stringish.to_str
9
11
  else
@@ -17,6 +19,9 @@ module Dux
17
19
  super(stringified)
18
20
  end
19
21
 
22
+ # Test basic equality.
23
+ #
24
+ # @param [String, Symbol] other
20
25
  def ==(other)
21
26
  if other.kind_of?(Symbol)
22
27
  self == other.to_s
@@ -27,6 +32,9 @@ module Dux
27
32
 
28
33
  alias_method :eql?, :==
29
34
 
35
+ # Test case equality
36
+ #
37
+ # @param [String, Symbol, Regexp] other
30
38
  def ===(other)
31
39
  if other.kind_of?(Symbol)
32
40
  self == other.to_s
@@ -1,11 +1,16 @@
1
1
  module Dux
2
+ # Calculate the object's inspection id
2
3
  module InspectID
3
4
  # Calculates the id shown in the default
4
5
  # version of `#inspect` methods.
5
6
  #
7
+ # @note This is currently limited to the implementation
8
+ # used in MRI. Rubinius and JRuby have their own
9
+ # implementations that are not currently served
10
+ # by this.
6
11
  # @param [Object] object
7
12
  # @return [String]
8
- def inspect_id(object)
13
+ def inspect_id(object = self)
9
14
  "0x%0.14x" % ( object.object_id << 1 )
10
15
  end
11
16
 
@@ -0,0 +1,97 @@
1
+ module Dux
2
+ class << self
3
+ # @!group Core extensions
4
+
5
+ # Enhance `Array` with {Dux::FlockMethods#duckify}
6
+ #
7
+ # @return [void]
8
+ def add_flock_methods!
9
+ Array.__send__ :prepend, Dux::FlockMethods
10
+
11
+ return nil
12
+ end
13
+
14
+ # Load all `Dux` core extensions.
15
+ #
16
+ # @param [Boolean] experimental whether to load experimental shorthand methods
17
+ # @return [void]
18
+ def extend_all!(experimental: false)
19
+ add_flock_methods!
20
+
21
+ extend_strings_and_symbols!
22
+
23
+ return unless experimental
24
+
25
+ array_shorthand!
26
+ string_shorthand!
27
+ symbol_shorthand!
28
+ end
29
+
30
+ # Enhance `String` with {Dux::Duckify}
31
+ #
32
+ # @return [void]
33
+ def extend_strings!
34
+ String.__send__ :prepend, Dux::Duckify
35
+
36
+ return nil
37
+ end
38
+
39
+ # Enhance `Symbol` with {Dux::Duckify}
40
+ #
41
+ # @return [void]
42
+ def extend_symbols!
43
+ Symbol.__send__ :prepend, Dux::Duckify
44
+
45
+ return nil
46
+ end
47
+
48
+ # Enhance `String` and `Symbol` classes with {Dux::Duckify#duckify}
49
+ #
50
+ # @return [void]
51
+ def extend_strings_and_symbols!
52
+ extend_strings!
53
+ extend_symbols!
54
+
55
+ return nil
56
+ end
57
+
58
+ # Experimental feature to add unary `~` to `Array`s
59
+ # for quick usage in case statements.
60
+ #
61
+ # @return [void]
62
+ def array_shorthand!
63
+ add_flock_methods!
64
+
65
+ Array.__send__ :prepend, Dux::HacksLikeADuck
66
+
67
+ nil
68
+ end
69
+
70
+ # Experimental feature to add unary `~` to `String`s
71
+ # for quick usage in case statements.
72
+ #
73
+ # @return [void]
74
+ def string_shorthand!
75
+ extend_strings!
76
+
77
+ String.__send__ :prepend, Dux::HacksLikeADuck
78
+
79
+ nil
80
+ end
81
+
82
+ # Experimental feature to add unary `~` to `Symbol`s
83
+ # for quick usage in case statements.
84
+ #
85
+ # @return [void]
86
+ def symbol_shorthand!
87
+ extend_symbols!
88
+
89
+ Symbol.__send__ :prepend, Dux::HacksLikeADuck
90
+
91
+ return nil
92
+ end
93
+
94
+ # @!endgroup
95
+
96
+ end
97
+ end
@@ -1,30 +1,77 @@
1
1
  module Dux
2
+ # Null objects that can be used for smarter
3
+ # argument building.
4
+ #
5
+ # @example Usage
6
+ # module Foo
7
+ # class Bar
8
+ # NULL = Dux.null 'Foo::Bar::NULL'
9
+ #
10
+ # # Making it private is not required,
11
+ # # but recommended if it is something
12
+ # # that should only be available
13
+ # # internally.
14
+ # private_constant :NULL
15
+ #
16
+ # def initialize(some_option: NULL)
17
+ # if some_option == NULL
18
+ # # Do some default logic
19
+ # else
20
+ # @some_option = some_option
21
+ # end
22
+ # end
23
+ # end
24
+ # end
2
25
  class NullObject
3
26
  include Dux::InspectID
4
27
 
28
+ # @!attribute [r] name
29
+ # The name of this null object,
30
+ # for self-documenting / introspection.
31
+ #
32
+ # In practice, this should be the object path,
33
+ # e.g. `Foo::Bar::NULL`
34
+ # @return [String]
5
35
  attr_reader :name
36
+
37
+ # @!attribute [r] purpose
38
+ # A purpose or description of this object,
39
+ # for self-documenting / introspection.
40
+ # @return [String]
6
41
  attr_reader :purpose
7
42
 
8
43
  # @param [String] name
9
44
  # @param [String] purpose
10
- def initialize(name, purpose: 'a null object')
11
- @name = name
45
+ def initialize(name = nil, purpose: 'a null object')
46
+ @name = name || default_name
12
47
  @purpose = purpose
48
+
49
+ freeze
13
50
  end
14
51
 
15
52
  private
53
+
54
+ # Generates a default name for this object.
55
+ #
56
+ # @return [String]
16
57
  def default_name
17
-
58
+ "Dux::NullObject(#{inspect_id(self)})"
18
59
  end
19
60
  end
20
61
 
21
62
  class << self
22
- # @param (@see Dux::NullObject#initialize)
63
+ # @!group DSL
64
+
65
+ # Create {Dux::NullObject a null object} with the provided options.
66
+ #
67
+ # @see Dux::NullObject#initialize
23
68
  # @return [Dux::NullObject]
24
- def null_object(name, **options)
69
+ def null_object(name = nil, **options)
25
70
  Dux::NullObject.new(name, **options)
26
71
  end
27
72
 
28
73
  alias_method :null, :null_object
74
+
75
+ # @!endgroup DSL
29
76
  end
30
77
  end
@@ -0,0 +1,139 @@
1
+ module Dux
2
+ # Methods for generating "interface" checks against an array of symbols
3
+ FLOCK_TYPES = Dux.enum :all, :any, :none
4
+
5
+ class << self
6
+ # @!group Predicates
7
+
8
+ # Create a predicate that checks if the provided object
9
+ # responds to the `symbol`.
10
+ #
11
+ # @param [Symbol] symbol
12
+ # @param [Boolean] include_all
13
+ # @return [Proc]
14
+ def [](symbol, include_all: false)
15
+ ->(obj) { obj.respond_to? symbol, include_all }
16
+ end
17
+
18
+ alias_method :predicate, :[]
19
+ alias_method :dux, :[]
20
+
21
+ # Create a predicate that checks if the provided `Class` or `Module`
22
+ # inherits from a given class or module.
23
+ #
24
+ # @param [Class, Module] parent
25
+ # @param [Boolean] include_self
26
+ # @raise [TypeError] requires a class or module to serve as parent
27
+ # @return [Proc]
28
+ def inherits(parent, include_self: false)
29
+ raise TypeError, "Must be a class or module to check inheritance" unless inheritable?(parent)
30
+
31
+ if include_self
32
+ ->(klass) { Dux.inheritable?(klass) && klass <= parent }
33
+ else
34
+ ->(klass) { Dux.inheritable?(klass) && klass < parent }
35
+ end
36
+ end
37
+
38
+ alias_method :includes, :inherits
39
+ alias_method :prepends, :inherits
40
+
41
+ # Create a predicate that checks if the provided `Class` or `Module`
42
+ # extends a given `parent` module.
43
+ #
44
+ # @param [Module] parent
45
+ # @raise [TypeError] requires a module to check extension
46
+ # @return [Proc]
47
+ def extends(parent)
48
+ raise TypeError, "Must be a module to check if something extends it" unless parent.kind_of?(Module)
49
+
50
+ ->(klass) { Dux.inheritable?(klass) && klass.singleton_class < parent }
51
+ end
52
+
53
+ # Create a predicate that checks against a specific YARD type description.
54
+ #
55
+ # @note Some limitations apply because of the underlying gem, e.g.
56
+ # symbol / string literals are not matched and will raise an error
57
+ # @see https://github.com/pd/yard_types the gem used for parsing types
58
+ # @raise [SyntaxError] when `YardTypes` fails to parse.
59
+ # @param [String] pattern
60
+ # @return [Proc]
61
+ def yard(pattern)
62
+ type = YardTypes.parse pattern
63
+
64
+ ->(value) { !type.check(value).nil? }
65
+ end
66
+
67
+ # Check if the provided thing is a class or a module.
68
+ #
69
+ # @param [Class, Module] klass_or_module
70
+ # @api private
71
+ def inheritable?(klass_or_module)
72
+ klass_or_module.kind_of?(Class) || klass_or_module.kind_of?(Module)
73
+ end
74
+
75
+ # Creates a lambda that describes a restrictive interface,
76
+ # wherein the provided object must `respond_to?` *all* of
77
+ # the methods.
78
+ #
79
+ # @param [<Symbol, String>] methods
80
+ # @param [Boolean] include_all
81
+ # @return [Proc]
82
+ def all(*methods, include_all: false)
83
+ ducks = flockify methods, include_all: include_all
84
+
85
+ ->(obj) { ducks.all? { |duck| duck.call obj } }
86
+ end
87
+
88
+ # Creates a lambda that describes a permissive interface,
89
+ # wherein the provided object must `respond_to?` at least
90
+ # one of the methods.
91
+ #
92
+ # @param [<Symbol>] methods
93
+ # @param [Boolean] include_all
94
+ # @return [Proc]
95
+ def any(*methods, include_all: false)
96
+ ducks = flockify methods, include_all: include_all
97
+
98
+ ->(obj) { ducks.any? { |duck| duck.call obj } }
99
+ end
100
+
101
+ # Creates a lambda that describes a restrictive interface,
102
+ # wherein the provided object must `respond_to?` *none* of
103
+ # the methods.
104
+ #
105
+ # @param [<Symbol>] methods
106
+ # @param [Boolean] include_all
107
+ # @return [Proc]
108
+ def none(*methods, include_all: false)
109
+ ducks = flockify methods, include_all: include_all
110
+
111
+ ->(obj) { ducks.none? { |duck| duck.call obj } }
112
+ end
113
+
114
+ # @param [:all, :any, :none] type
115
+ # @param [<Symbol, String>] methods
116
+ # @param [Boolean] include_all
117
+ # @raise [ArgumentError] on invalid type
118
+ # @api private
119
+ # @return [<Proc>]
120
+ def flock(type, *methods, include_all: false)
121
+ type = FLOCK_TYPES.fetch(type) { raise ArgumentError, "Invalid flock type: #{type}" }
122
+
123
+ __send__ type, methods, include_all: include_all
124
+ end
125
+
126
+ protected
127
+
128
+ # @param [<Symbol>] methods
129
+ # @param [Boolean] include_all
130
+ # @return [<Proc>]
131
+ def flockify(methods, include_all: false)
132
+ methods.flatten.map do |sym|
133
+ dux sym, include_all: include_all
134
+ end
135
+ end
136
+
137
+ # @!endgroup
138
+ end
139
+ end
@@ -1,4 +1,4 @@
1
1
  module Dux
2
2
  # Gem version.
3
- VERSION = "0.3.0"
3
+ VERSION = "0.8.0"
4
4
  end
metadata CHANGED
@@ -1,17 +1,17 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dux
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexa Grey
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-04-06 00:00:00.000000000 Z
11
+ date: 2017-04-09 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: lru_redux
14
+ name: yard_types
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
17
  - - ">="
@@ -132,14 +132,16 @@ files:
132
132
  - lib/dux.rb
133
133
  - lib/dux/attempt.rb
134
134
  - lib/dux/blankness.rb
135
- - lib/dux/duck_type.rb
135
+ - lib/dux/comparable.rb
136
136
  - lib/dux/duckify.rb
137
137
  - lib/dux/enum.rb
138
138
  - lib/dux/flock_methods.rb
139
139
  - lib/dux/hacks_like_a_duck.rb
140
140
  - lib/dux/indifferent_string.rb
141
141
  - lib/dux/inspect_id.rb
142
+ - lib/dux/monkey_patch.rb
142
143
  - lib/dux/null_object.rb
144
+ - lib/dux/predicate.rb
143
145
  - lib/dux/version.rb
144
146
  homepage: https://github.com/scryptmouse/dux
145
147
  licenses:
@@ -165,5 +167,5 @@ rubyforge_project:
165
167
  rubygems_version: 2.6.8
166
168
  signing_key:
167
169
  specification_version: 4
168
- summary: Lazy duck-type matching
170
+ summary: Swiss-army knife gem for duck-type matching and utility methods
169
171
  test_files: []
File without changes