cuprum 0.1.0

This diff has not been reviewed by any users.
Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: bea85b3d71780b913660e164e106f5d996c36218
4
+ data.tar.gz: 256bcdf2ac7b383c060c812d004f8b45d491e907
5
+ SHA512:
6
+ metadata.gz: a0dbaddff3b7672ee4dc4cfd4ce06f27d958b2f816643aa2db13ee3704b8717e28f93c446130a104476bcda2991ac9963b2fc768b983a2d468cdc7b883545841
7
+ data.tar.gz: 8a465d71bcb45eb32b7cfb60a26e09e91be3736b0346c7b60f786d99371c09d8fa913c6ae2c9ad817a62cb837b15fae4c24d1545a00f05cd0e9dfc6a697de4ff
@@ -0,0 +1,13 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ Initial version.
6
+
7
+ ### Functions
8
+
9
+ Implemented `Cuprum::Function`. A functional object that encapsulates a business logic operation with a consistent interface and tracking of result value and status.
10
+
11
+ ### Results
12
+
13
+ Implemented `Cuprum::Result`. A data object that encapsulates the result of calling a Cuprum function.
@@ -0,0 +1,17 @@
1
+ # Development
2
+
3
+ ## Errors
4
+
5
+ - Dedicated errors object.
6
+
7
+ ## Function
8
+
9
+ - Chaining with #then, #else, etc.
10
+
11
+ ## Operation
12
+
13
+ - Wraps a function and tracks the last result object.
14
+
15
+ ## Result
16
+
17
+ - Force success or failure status with #success!, #failure! methods.
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ (The MIT License)
2
+
3
+ Copyright (c) 2017 Rob Smith
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
20
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
21
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
22
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,125 @@
1
+ # Cuprum
2
+
3
+ A lightweight, functional-lite toolkit for making business logic a first-class
4
+ citizen of your application.
5
+
6
+ ## Support
7
+
8
+ Cuprum is tested against Ruby 2.4.
9
+
10
+ ## Contribute
11
+
12
+ ### GitHub
13
+
14
+ The canonical repository for this gem is located at https://github.com/sleepingkingstudios/cuprum.
15
+
16
+ ### A Note From The Developer
17
+
18
+ Hi, I'm Rob Smith, a Ruby Engineer and the developer of this library. I use these tools every day, but they're not just written for me. If you find this project helpful in your own work, or if you have any questions, suggestions or critiques, please feel free to get in touch! I can be reached on GitHub (see above, and feel encouraged to submit bug reports or merge requests there) or via email at merlin@sleepingkingstudios.com. I look forward to hearing from you!
19
+
20
+ ## Features
21
+
22
+ ### Functions
23
+
24
+ Functions are the core feature of Cuprum. In a nutshell, each Cuprum::Function is a functional object that encapsulates a business logic operation. A Function provides a consistent interface and tracking of result value and status. This minimizes boilerplate and allows for interchangeability between different implementations or strategies for managing your data and processes.
25
+
26
+ Each Function implements a `#call` method that wraps your defined business logic and returns an instance of Cuprum::Result. The result wraps the returned data (with the `#value` method), any `#errors` generated when running the Function, and the overall status with the `#success?` and `#failure` methods. For more details about Cuprum::Result, see below.
27
+
28
+ #### Defining With a Block
29
+
30
+ Functions can be used right out of the box by passing a block to the Cuprum::Function constructor, as follows:
31
+
32
+ # A Function with a block
33
+ double_function = Function.new { |int| 2 * int }
34
+ result = double_function.call(5)
35
+
36
+ result.value #=> 10
37
+
38
+ The constructor block will be called each time `Function#call` is executed, and will be passed all of the arguments given to `#call`. You can even define a block parameter, which will be passed along to the constructor block when `#call` is called with a block argument.
39
+
40
+ #### Defining With a Subclass
41
+
42
+ Larger applications will want to create Function subclasses that encapsulate their business logic in a reusable, composable fashion. The implementation for each subclass is handled by the `#process` private method. If a subclass or its ancestors does not implement `#process`, a `Cuprum::Function::NotImplementedError` will be raised.
43
+
44
+ # A Function subclass
45
+ class MultiplyFunction
46
+ def initialize multiplier
47
+ @multiplier = multiplier
48
+ end # constructor
49
+
50
+ private
51
+
52
+ def process int
53
+ int * @multiplier
54
+ end # method process
55
+ end # class
56
+
57
+ triple_function = MultiplyFunction.new(3)
58
+ result = triple_function.call(5)
59
+
60
+ result.value #=> 15
61
+
62
+ As with the block syntax, a Function whose implementation is defined via the `#process` method will call `#process` each time that `#call` is executed, and will pass all arguments from `#call` on to `#process`. The value returned by `#process` will be assigned to the result `#value`.
63
+
64
+ #### Success, Failure, and Errors
65
+
66
+ Whether defined with a block or in the `#process` method, the Function implementation can access an `#errors` object while in the `#call` method. Any errors added to the errors object will be exposed by the `#errors` method on the result object.
67
+
68
+ # A Function with errors
69
+ class DivideFunction
70
+ def initialize divisor
71
+ @divisor = divisor
72
+ end # constructor
73
+
74
+ private
75
+
76
+ def process int
77
+ if @divisor.zero?
78
+ errors << 'errors.messages.divide_by_zero'
79
+
80
+ return
81
+ end # if
82
+
83
+ int / @divisor
84
+ end # method process
85
+ end # class
86
+
87
+ In addition, the result object defines `#success?` and `#failure?` predicates. If the result has no errors, then `#success?` will return true and `#failure?` will return false.
88
+
89
+ halve_function = DivideFunction.new(2)
90
+ result = halve_function.call(10)
91
+
92
+ result.errors #=> []
93
+ result.success? #=> true
94
+ result.failure? #=> false
95
+ result.value #=> 5
96
+
97
+ If the result does have errors, `#success?` will return false and `#failure?` will return true.
98
+
99
+ function_with_errors = DivideFunction.new(0)
100
+ result = function_with_errors.call(10)
101
+
102
+ result.errors #=> ['errors.messages.divide_by_zero']
103
+ result.success? #=> false
104
+ result.failure? #=> true
105
+ result.value #=> nil
106
+
107
+ ### Results
108
+
109
+ A Cuprum::Result is a data object that encapsulates the result of calling a Cuprum function - the returned value, the success or failure status, and any errors generated by the function. It defines the following methods:
110
+
111
+ #### `#value`
112
+
113
+ The value returned by the function. For example, for an increment function that added 1 to a given integer, the `#value` of the result object would be the incremented integer.
114
+
115
+ #### `#errors`
116
+
117
+ The errors generated by the function, or an empty array if no errors were generated.
118
+
119
+ #### `#success?`
120
+
121
+ True if the function did not generate any errors, otherwise false.
122
+
123
+ #### `#failure?`
124
+
125
+ True if the function generated one or more errors, otherwise false.
@@ -0,0 +1,8 @@
1
+ # A lightweight, functional-lite toolkit for making business logic a first-class
2
+ # citizen of your application.
3
+ module Cuprum
4
+ # @return [String] The current version of the gem.
5
+ def self.version
6
+ VERSION
7
+ end # class method version
8
+ end # module
@@ -0,0 +1,122 @@
1
+ # lib/cuprum/function.rb
2
+
3
+ require 'cuprum/result'
4
+
5
+ module Cuprum
6
+ # Functional object that encapsulates a business logic operation with a
7
+ # consistent interface and tracking of result value and status.
8
+ #
9
+ # A Function can be defined either by passing a block to the constructor, or
10
+ # by defining a subclass of Function and implementing the #process method.
11
+ #
12
+ # @example A Function with a block
13
+ # double_function = Function.new { |int| 2 * int }
14
+ # result = double_function.call(5)
15
+ #
16
+ # result.value #=> 10
17
+ #
18
+ # @example A Function subclass
19
+ # class MultiplyFunction
20
+ # def initialize multiplier
21
+ # @multiplier = multiplier
22
+ # end # constructor
23
+ #
24
+ # private
25
+ #
26
+ # def process int
27
+ # int * @multiplier
28
+ # end # method process
29
+ # end # class
30
+ #
31
+ # triple_function = MultiplyFunction.new(3)
32
+ # result = triple_function.call(5)
33
+ #
34
+ # result.value #=> 15
35
+ #
36
+ # @example A Function with errors
37
+ # class DivideFunction
38
+ # def initialize divisor
39
+ # @divisor = divisor
40
+ # end # constructor
41
+ #
42
+ # private
43
+ #
44
+ # def process int
45
+ # if @divisor.zero?
46
+ # errors << 'errors.messages.divide_by_zero'
47
+ #
48
+ # return
49
+ # end # if
50
+ #
51
+ # int / @divisor
52
+ # end # method process
53
+ # end # class
54
+ #
55
+ # halve_function = DivideFunction.new(2)
56
+ # result = halve_function.call(10)
57
+ #
58
+ # result.errors #=> []
59
+ # result.value #=> 5
60
+ #
61
+ # function_with_errors = DivideFunction.new(0)
62
+ # result = function_with_errors.call(10)
63
+ #
64
+ # result.errors #=> ['errors.messages.divide_by_zero']
65
+ # result.value #=> nil
66
+ class Function
67
+ # Error class for calling a Function that was not given a definition block
68
+ # or have a #process method defined.
69
+ class NotImplementedError < StandardError
70
+ # Error message for a NotImplementedError.
71
+ DEFAULT_MESSAGE = 'no implementation defined for function'.freeze
72
+
73
+ def initialize message = nil
74
+ super(message || DEFAULT_MESSAGE)
75
+ end # constructor
76
+ end # class
77
+
78
+ # Returns a new instance of Cuprum::Function.
79
+ #
80
+ # @yield [*arguments, **keywords, &block] If a block is given, the
81
+ # #call method will wrap the block and set the result #value to the return
82
+ # value of the block. This overrides the implementation in #process, if
83
+ # any.
84
+ def initialize &implementation
85
+ define_singleton_method :process, &implementation if implementation
86
+ end # method initialize
87
+
88
+ # @overload call(*arguments, *keywords, &block)
89
+ # Executes the logic encoded in the constructor block, or the #process
90
+ # method if no block was passed to the constructor.
91
+ #
92
+ # @param arguments [Array] Arguments to be passed to the implementation.
93
+ #
94
+ # @param keywords [Hash] Keywords to be passed to the implementation.
95
+ #
96
+ # @return [Cuprum::Result] The result object for the function.
97
+ #
98
+ # @yield If a block argument is given, it will be passed to the
99
+ # implementation.
100
+ #
101
+ # @raise [NotImplementedError] unless a block was passed to the
102
+ # constructor or the #process method was overriden by a Function
103
+ # subclass.
104
+ def call *args, &block
105
+ Cuprum::Result.new.tap do |result|
106
+ @errors = result.errors
107
+
108
+ result.value = process(*args, &block)
109
+
110
+ @errors = nil
111
+ end # tap
112
+ end # method call
113
+
114
+ private
115
+
116
+ attr_reader :errors
117
+
118
+ def process *_args
119
+ raise NotImplementedError, nil, caller(1..-1)
120
+ end # method process
121
+ end # class
122
+ end # module
@@ -0,0 +1,30 @@
1
+ require 'cuprum'
2
+
3
+ module Cuprum
4
+ # Data object that encapsulates the result of calling a Cuprum function or
5
+ # operation.
6
+ class Result
7
+ # @return [Object] the value returned by calling the function.
8
+ attr_accessor :value
9
+
10
+ attr_writer :errors
11
+
12
+ # @return [Array] the errors (if any) generated when the function was
13
+ # called.
14
+ def errors
15
+ @errors ||= []
16
+ end # method errors
17
+
18
+ # @return [Boolean] false if the function did not generate any errors,
19
+ # otherwise true.
20
+ def failure?
21
+ !errors.empty?
22
+ end # method failure?
23
+
24
+ # @return [Boolean] true if the function did not generate any errors,
25
+ # otherwise false.
26
+ def success?
27
+ errors.empty?
28
+ end # method success?
29
+ end # class
30
+ end # module
@@ -0,0 +1,54 @@
1
+ module Cuprum
2
+ # @api private
3
+ #
4
+ # The current version of the gem.
5
+ #
6
+ # @see http://semver.org/
7
+ module Version
8
+ # Major version.
9
+ MAJOR = 0
10
+ # Minor version.
11
+ MINOR = 1
12
+ # Patch version.
13
+ PATCH = 0
14
+ # Prerelease version.
15
+ PRERELEASE = nil
16
+ # Build metadata.
17
+ BUILD = nil
18
+
19
+ class << self
20
+ # Generates the gem version string from the Version constants.
21
+ #
22
+ # Inlined here because dependencies may not be loaded when processing a
23
+ # gemspec, which results in the user being unable to install the gem for
24
+ # the first time.
25
+ #
26
+ # @see SleepingKingStudios::Tools::SemanticVersion#to_gem_version
27
+ def to_gem_version
28
+ str = "#{MAJOR}.#{MINOR}.#{PATCH}"
29
+
30
+ prerelease = value_of(:PRERELEASE)
31
+ str << ".#{prerelease}" if prerelease
32
+
33
+ build = value_of(:BUILD)
34
+ str << ".#{build}" if build
35
+
36
+ str
37
+ end # class method to_version
38
+
39
+ private
40
+
41
+ def value_of constant
42
+ return nil unless const_defined?(constant)
43
+
44
+ value = const_get(constant)
45
+
46
+ return nil if value.respond_to?(:empty?) && value.empty?
47
+
48
+ value
49
+ end # method value_of
50
+ end # eigenclass
51
+ end # module
52
+
53
+ VERSION = Version.to_gem_version
54
+ end # module
metadata ADDED
@@ -0,0 +1,123 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cuprum
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob "Merlin" Smith
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-08-17 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.6'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec-sleeping_king_studios
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 2.3.0
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: 2.3.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: rubocop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.49.1
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.49.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rubocop-rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 1.15.1
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 1.15.1
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.15'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.15'
83
+ description: A lightweight, functional-lite toolkit for making business logic a first-class
84
+ citizen of your application.
85
+ email:
86
+ - merlin@sleepingkingstudios.com
87
+ executables: []
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - CHANGELOG.md
92
+ - DEVELOPMENT.md
93
+ - LICENSE
94
+ - README.md
95
+ - lib/cuprum.rb
96
+ - lib/cuprum/function.rb
97
+ - lib/cuprum/result.rb
98
+ - lib/cuprum/version.rb
99
+ homepage: http://sleepingkingstudios.com
100
+ licenses:
101
+ - MIT
102
+ metadata: {}
103
+ post_install_message:
104
+ rdoc_options: []
105
+ require_paths:
106
+ - lib
107
+ required_ruby_version: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - ">="
115
+ - !ruby/object:Gem::Version
116
+ version: '0'
117
+ requirements: []
118
+ rubyforge_project:
119
+ rubygems_version: 2.6.11
120
+ signing_key:
121
+ specification_version: 4
122
+ summary: A lightweight, functional-lite toolkit.
123
+ test_files: []