j8-functional 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ lib = File.expand_path('lib', __dir__)
4
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
5
+ require 'j8/functional/version'
6
+
7
+ Gem::Specification.new do |spec|
8
+ spec.name = 'j8-functional'
9
+ spec.version = J8::Functional::VERSION
10
+ spec.authors = ['John Shields']
11
+ spec.email = ['john@shields.wtf']
12
+
13
+ spec.summary = 'Java 8 inspired functional utilities'
14
+ spec.homepage = 'https://john.shields.wtf'
15
+ spec.license = 'MIT'
16
+
17
+ spec.required_ruby_version = Gem::Requirement.new('>= 2.3.0')
18
+
19
+ spec.metadata['allowed_push_host'] = 'https://rubygems.org'
20
+
21
+ # Specify which files should be added to the gem when it is released.
22
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
23
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
24
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
25
+ end
26
+ spec.bindir = 'exe'
27
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+ spec.require_paths = ['lib']
29
+
30
+ spec.add_development_dependency 'bundler'
31
+ spec.add_development_dependency 'gem-release'
32
+ spec.add_development_dependency 'pry'
33
+ spec.add_development_dependency 'pry-doc'
34
+ spec.add_development_dependency 'rake', '~> 10.0'
35
+ spec.add_development_dependency 'rb-inotify'
36
+ spec.add_development_dependency 'rspec', '~> 3.0'
37
+ spec.add_development_dependency 'rubocop'
38
+ end
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'j8/functional'
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class BiConsumer
5
+ include J8::Common
6
+
7
+ def accept(o1, o2)
8
+ @callable.call(o1, o2)
9
+ end
10
+
11
+ def then(after = nil, &block)
12
+ callable = from_callable(after, block)
13
+
14
+ J8::BiConsumer.new(
15
+ lambda do |o1, o2|
16
+ accept(o1, o2)
17
+ callable.accept(o1, o2)
18
+ end
19
+ )
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class BiFunction
5
+ include J8::Common
6
+
7
+ def apply(o1, o2)
8
+ @callable.call(o1, o2)
9
+ end
10
+
11
+ def then(after = nil, &block)
12
+ callable = from_callable(after, block)
13
+
14
+ J8::BiFunction.new(->(o1, o2) { callable.apply(*apply(o1, o2)) })
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class BiPredicate
5
+ include J8::Common
6
+
7
+ def test(o1, o2)
8
+ @callable.call(o1, o2)
9
+ end
10
+
11
+ def negate
12
+ J8::BiPredicate.new(->(o1, o2) { !test(o1, o2) })
13
+ end
14
+
15
+ def and(other = nil, &block)
16
+ callable = from_callable(other, block)
17
+
18
+ J8::BiPredicate.new(->(o1, o2) { test(o1, o2) && callable.test(o1, o2) })
19
+ end
20
+
21
+ def or(other = nil, &block)
22
+ callable = from_callable(other, block)
23
+
24
+ J8::BiPredicate.new(->(o1, o2) { test(o1, o2) || callable.test(o1, o2) })
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class BinaryOperator < J8::BiFunction
5
+ def self.max_by(comparator = nil, &block)
6
+ callable = from_callable_class(comparator, block, J8::Comparator)
7
+
8
+ J8::BinaryOperator.new(->(a, b) { callable.compare(a, b) <= 0 ? a : b })
9
+ end
10
+
11
+ def self.min_by(comparator = nil, &block)
12
+ callable = from_callable_class(comparator, block, J8::Comparator)
13
+
14
+ J8::BinaryOperator.new(->(a, b) { callable.compare(a, b) >= 0 ? a : b })
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Collector
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Collectors
5
+ end
6
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ module Common
5
+ module_function
6
+
7
+ def initialize(callable = nil, &block)
8
+ @callable = callable_from_proc(callable, block)
9
+ end
10
+
11
+ def lambda?(o)
12
+ o.is_a?(Proc) && o.lambda?
13
+ end
14
+
15
+ def raise_unless_lambda(o)
16
+ return if lambda?(o)
17
+
18
+ raise ArgumentError, "(#{o.class}) '#{o}' is not a lambda"
19
+ end
20
+
21
+ def from_callable(callable, block)
22
+ callable = callable_from_proc(callable, block)
23
+
24
+ if callable.is_a?(self.class)
25
+ callable
26
+ else
27
+ raise_unless_lambda(callable)
28
+
29
+ self.class.new(callable)
30
+ end
31
+ end
32
+
33
+ def from_callable_class(callable, block, klass)
34
+ if callable.is_a?(klass)
35
+ callable
36
+ else
37
+ callable = callable_from_proc(callable, block)
38
+ raise_unless_lambda(callable)
39
+
40
+ klass.new(callable)
41
+ end
42
+ end
43
+
44
+ def make_lambda(block)
45
+ Object.new.tap { |n| n.define_singleton_method(:_, block) }.method(:_).to_proc
46
+ end
47
+
48
+ def callable_from_proc(callable, block)
49
+ if block.nil?
50
+ raise J8::NilException if callable.nil?
51
+
52
+ raise_unless_lambda(callable)
53
+
54
+ callable
55
+ else
56
+ make_lambda(block)
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Comparator
5
+ include J8::Common
6
+
7
+ def self.comparing(extractor, comparator)
8
+ extractor = from_callable_class(extractor, nil, J8::Function)
9
+ comparator = from_callable_class(comparator, nil, J8::Comparator)
10
+
11
+ J8::Comparataor.new(
12
+ lambda do |o1, o2|
13
+ comparator.compare(extractor.apply(o1), extractor.apply(o2))
14
+ end
15
+ )
16
+ end
17
+
18
+ def compare(o1, o2)
19
+ raise J8::NilException if o1.nil?
20
+ raise J8::NilException if o2.nil?
21
+
22
+ @callable.call(o1, o2)
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Consumer
5
+ include J8::Common
6
+
7
+ def accept(o)
8
+ @callable.call(o)
9
+ end
10
+
11
+ def then(after = nil, &block)
12
+ callable = from_callable(after, block)
13
+
14
+ J8::Consumer.new(
15
+ lambda do |o|
16
+ accept(o)
17
+ callable.accept(o)
18
+ end
19
+ )
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Function
5
+ include J8::Common
6
+
7
+ def apply(o)
8
+ @callable.call(o)
9
+ end
10
+
11
+ def compose(before = nil, &block)
12
+ callable = from_callable(before, block)
13
+
14
+ J8::Function.new(->(o) { apply(callable.apply(o)) })
15
+ end
16
+
17
+ def then(after = nil, &block)
18
+ callable = from_callable(after, block)
19
+
20
+ J8::Function.new(->(o) { callable.apply(apply(o)) })
21
+ end
22
+
23
+ def self.identity
24
+ J8::Function.new { |o| o }
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'j8/functional/version'
4
+ require 'j8/functional/exceptions'
5
+ require 'j8/common'
6
+
7
+ require 'j8/bi_consumer'
8
+ require 'j8/bi_function'
9
+ require 'j8/predicate'
10
+ require 'j8/collector'
11
+ require 'j8/supplier'
12
+ require 'j8/optional'
13
+ require 'j8/function'
14
+ require 'j8/consumer'
15
+ require 'j8/comparator'
16
+ require 'j8/stream'
17
+
18
+ class Object
19
+ def j8_stream
20
+ if self.class.ancestors.include?(Enumerable)
21
+ J8::Stream.new(each)
22
+ else
23
+ J8::Stream.new([self].each)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class NilException < StandardError; end
5
+
6
+ class NoSuchElementException < StandardError; end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ module Functional
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module J8
4
+ class Optional
5
+ include J8::Common
6
+
7
+ def initialize(value = nil)
8
+ @value = value
9
+ end
10
+
11
+ def self.empty
12
+ J8::Optional.new
13
+ end
14
+
15
+ def self.of(value)
16
+ raise J8::NilException if value.nil?
17
+
18
+ J8::Optional.new(value)
19
+ end
20
+
21
+ def self.of_nilable(value)
22
+ J8::Optional.new(value)
23
+ end
24
+
25
+ def empty?
26
+ @value.nil?
27
+ end
28
+
29
+ def equals?(object)
30
+ @value == object
31
+ end
32
+ alias == equals?
33
+
34
+ def filter(predicate = nil, &block)
35
+ callable = from_callable_class(predicate, block, J8::Predicate)
36
+
37
+ if present? && callable.test(@value)
38
+ J8::Optional.new(@value)
39
+ else
40
+ J8::Optional.empty
41
+ end
42
+ end
43
+
44
+ def flat_map(function = nil, &block)
45
+ callable = from_callable_class(function, block, J8::Function)
46
+ return J8::Optional.empty unless present?
47
+
48
+ callable.apply(@value).tap do |result|
49
+ raise J8::NilException if result.nil?
50
+ end
51
+ end
52
+
53
+ def get
54
+ raise J8::NoSuchElementException unless present?
55
+
56
+ @value
57
+ end
58
+ alias value get
59
+
60
+ def if_present(consumer = nil, &block)
61
+ callable = from_callable_class(consumer, block, J8::Consumer)
62
+ return unless present?
63
+
64
+ callable.accept(@value)
65
+ end
66
+
67
+ def present?
68
+ !@value.nil?
69
+ end
70
+
71
+ def map(function = nil, &block)
72
+ callable = from_callable_class(function, block, J8::Function)
73
+ return J8::Optional.empty unless present?
74
+
75
+ J8::Optional.new(callable.apply(@value))
76
+ end
77
+
78
+ def or_else(value)
79
+ if present?
80
+ @value
81
+ else
82
+ value
83
+ end
84
+ end
85
+
86
+ def or_else_get(supplier = nil, &block)
87
+ callable = from_callable_class(supplier, block, J8::Supplier)
88
+
89
+ if present?
90
+ @value
91
+ else
92
+ callable.get.tap do |result|
93
+ raise J8::NilException if result.nil?
94
+ end
95
+ end
96
+ end
97
+
98
+ def or_else_raise(supplier = nil, &block)
99
+ callable = from_callable_class(supplier, block, J8::Supplier)
100
+ raise callable.get unless present?
101
+
102
+ @value
103
+ end
104
+ end
105
+ end