dux 0.3.0 → 0.8.0

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