ruby-mutant 1.0.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.
- checksums.yaml +7 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +23 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- data/.gitignore +13 -0
- data/.rakeTasks +7 -0
- data/.rspec +3 -0
- data/.travis.yml +7 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +25 -0
- data/Gemfile +6 -0
- data/Gemfile.lock +35 -0
- data/LICENSE +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +152 -0
- data/Rakefile +6 -0
- data/bin/console +14 -0
- data/bin/setup +8 -0
- data/lib/mutant.rb +5 -0
- data/lib/ruby-mutant/base.rb +156 -0
- data/lib/ruby-mutant/exceptions/mutation_missing_required_var_exception.rb +7 -0
- data/lib/ruby-mutant/exceptions/mutation_setup_exception.rb +5 -0
- data/lib/ruby-mutant/exceptions/mutation_validation_exception.rb +5 -0
- data/lib/ruby-mutant/output.rb +60 -0
- data/lib/ruby-mutant/version.rb +3 -0
- data/ruby-mutant.gemspec +30 -0
- metadata +112 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 51e7408e70faf6d2d947d56810cb097eb612234a92809ae92d1ba190cff3b976
|
4
|
+
data.tar.gz: d99ff9d4b8045ef89dc965081e872829cf08b8ceb313c04d3f48703ca54e1369
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5fdf4681e19da56d8e37edb0dc1289804eb98731193685777914831f7f94582491eab9f2ce788cd488c5495769806c5172a2da274da19bd1b0dbd10f17fdd74b
|
7
|
+
data.tar.gz: 19113031cd96afcc38351c4d6b8818a43ba712e100df8475bb11e9bdda21e32da0a247453bb35fef2dae2f63dc34809948a155a207323edd8bfbf6a14cb11d6e
|
@@ -0,0 +1,23 @@
|
|
1
|
+
---
|
2
|
+
name: Bug report
|
3
|
+
about: Create a report to help us improve
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Describe the bug**
|
11
|
+
A clear and concise description of what the bug is.
|
12
|
+
|
13
|
+
**To Reproduce**
|
14
|
+
Steps to reproduce the behavior:
|
15
|
+
|
16
|
+
|
17
|
+
**Expected behavior**
|
18
|
+
A clear and concise description of what you expected to happen.
|
19
|
+
|
20
|
+
|
21
|
+
|
22
|
+
**Additional context**
|
23
|
+
Add any other context about the problem here.
|
@@ -0,0 +1,20 @@
|
|
1
|
+
---
|
2
|
+
name: Feature request
|
3
|
+
about: Suggest an idea for this project
|
4
|
+
title: ''
|
5
|
+
labels: ''
|
6
|
+
assignees: ''
|
7
|
+
|
8
|
+
---
|
9
|
+
|
10
|
+
**Is your feature request related to a problem? Please describe.**
|
11
|
+
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
|
12
|
+
|
13
|
+
**Describe the solution you'd like**
|
14
|
+
A clear and concise description of what you want to happen.
|
15
|
+
|
16
|
+
**Describe alternatives you've considered**
|
17
|
+
A clear and concise description of any alternative solutions or features you've considered.
|
18
|
+
|
19
|
+
**Additional context**
|
20
|
+
Add any other context or screenshots about the feature request here.
|
data/.gitignore
ADDED
data/.rakeTasks
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<Settings><!--This file was automatically generated by Ruby plugin.
|
3
|
+
You are allowed to:
|
4
|
+
1. Remove rake task
|
5
|
+
2. Add existing rake tasks
|
6
|
+
To add existing rake tasks automatically delete this file and reload the project.
|
7
|
+
--><RakeGroup description="" fullCmd="" taksId="rake" /></Settings>
|
data/.rspec
ADDED
data/.travis.yml
ADDED
data/CODE_OF_CONDUCT.md
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
# Contributor Covenant Code of Conduct
|
2
|
+
|
3
|
+
## Our Pledge
|
4
|
+
|
5
|
+
In the interest of fostering an open and welcoming environment, we as
|
6
|
+
contributors and maintainers pledge to making participation in our project and
|
7
|
+
our community a harassment-free experience for everyone, regardless of age, body
|
8
|
+
size, disability, ethnicity, gender identity and expression, level of experience,
|
9
|
+
nationality, personal appearance, race, religion, or sexual identity and
|
10
|
+
orientation.
|
11
|
+
|
12
|
+
## Our Standards
|
13
|
+
|
14
|
+
Examples of behavior that contributes to creating a positive environment
|
15
|
+
include:
|
16
|
+
|
17
|
+
* Using welcoming and inclusive language
|
18
|
+
* Being respectful of differing viewpoints and experiences
|
19
|
+
* Gracefully accepting constructive criticism
|
20
|
+
* Focusing on what is best for the community
|
21
|
+
* Showing empathy towards other community members
|
22
|
+
|
23
|
+
Examples of unacceptable behavior by participants include:
|
24
|
+
|
25
|
+
* The use of sexualized language or imagery and unwelcome sexual attention or
|
26
|
+
advances
|
27
|
+
* Trolling, insulting/derogatory comments, and personal or political attacks
|
28
|
+
* Public or private harassment
|
29
|
+
* Publishing others' private information, such as a physical or electronic
|
30
|
+
address, without explicit permission
|
31
|
+
* Other conduct which could reasonably be considered inappropriate in a
|
32
|
+
professional setting
|
33
|
+
|
34
|
+
## Our Responsibilities
|
35
|
+
|
36
|
+
Project maintainers are responsible for clarifying the standards of acceptable
|
37
|
+
behavior and are expected to take appropriate and fair corrective action in
|
38
|
+
response to any instances of unacceptable behavior.
|
39
|
+
|
40
|
+
Project maintainers have the right and responsibility to remove, edit, or
|
41
|
+
reject comments, commits, code, wiki edits, issues, and other contributions
|
42
|
+
that are not aligned to this Code of Conduct, or to ban temporarily or
|
43
|
+
permanently any contributor for other behaviors that they deem inappropriate,
|
44
|
+
threatening, offensive, or harmful.
|
45
|
+
|
46
|
+
## Scope
|
47
|
+
|
48
|
+
This Code of Conduct applies both within project spaces and in public spaces
|
49
|
+
when an individual is representing the project or its community. Examples of
|
50
|
+
representing a project or community include using an official project e-mail
|
51
|
+
address, posting via an official social media account, or acting as an appointed
|
52
|
+
representative at an online or offline event. Representation of a project may be
|
53
|
+
further defined and clarified by project maintainers.
|
54
|
+
|
55
|
+
## Enforcement
|
56
|
+
|
57
|
+
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
58
|
+
reported by contacting the project team at TODO: Write your email address. All
|
59
|
+
complaints will be reviewed and investigated and will result in a response that
|
60
|
+
is deemed necessary and appropriate to the circumstances. The project team is
|
61
|
+
obligated to maintain confidentiality with regard to the reporter of an incident.
|
62
|
+
Further details of specific enforcement policies may be posted separately.
|
63
|
+
|
64
|
+
Project maintainers who do not follow or enforce the Code of Conduct in good
|
65
|
+
faith may face temporary or permanent repercussions as determined by other
|
66
|
+
members of the project's leadership.
|
67
|
+
|
68
|
+
## Attribution
|
69
|
+
|
70
|
+
This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
|
71
|
+
available at [http://contributor-covenant.org/version/1/4][version]
|
72
|
+
|
73
|
+
[homepage]: http://contributor-covenant.org
|
74
|
+
[version]: http://contributor-covenant.org/version/1/4/
|
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
|
2
|
+
## How to contribute to RubyMutant
|
3
|
+
---
|
4
|
+
|
5
|
+
Did you find a bug?
|
6
|
+
- Ensure the bug was not already reported by searching on Github under [issues](https://github.com/coreyjs/ruby-mutant/issues).
|
7
|
+
- If you are unable to find any issue that is the same or address your problem, feel free to open a new bug report issue. A clear and concise title and description of the bug and as much information possible on reproducible steps would greatly ensure the squashing of said bug.
|
8
|
+
|
9
|
+
|
10
|
+
**Did you create a patch that fixes a bug?**
|
11
|
+
- Open a new PR with the code fix
|
12
|
+
- Ensure your PR description is clear and concise and describes the problem and the solution. Include the issue # as well.
|
13
|
+
- Make sure your patch has the appropriate test coverage to confirm the fix.
|
14
|
+
- Once in review, a maintainer will review the PR and provide any needed feedback and guidance to help get the PR merged in.
|
15
|
+
|
16
|
+
|
17
|
+
**Did you want to add a new feature or enhancement?**
|
18
|
+
- Create a feature request using the "Feature Request" issue template, [here](https://github.com/coreyjs/ruby-mutant/issues/new?assignees=&labels=&template=feature_request.md&title=)
|
19
|
+
- Be clear about the feature you want to add, the problem it is looking to solve or the idea/intent behind the feature.
|
20
|
+
- If you plan to build the feature yourself, tag the issue with the label `work in progress` and assign yourself as an `Assignee` (or a maintainer can if permissions dont allow).
|
21
|
+
- For branch naming conventions when dealing with features, please prefix the branch name with`feature/` such as `feature/this-adds-async-support`
|
22
|
+
- Once ready for review, open a PR with the base branch being `master`. Once all builds are complete and passing, a maintainer will your with you to finalize any code changes that may be necessary.
|
23
|
+
|
24
|
+
|
25
|
+
Thanks everyone for their interest in this project! This is meant to be a learning expiernce for everyone, to learn how to contribute and work in open source. I encourage you to pitch in and let your creativity drive you. ❤️❤️❤️
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ruby-mutant (0.0.4)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.3)
|
10
|
+
rake (13.0.1)
|
11
|
+
rspec (3.8.0)
|
12
|
+
rspec-core (~> 3.8.0)
|
13
|
+
rspec-expectations (~> 3.8.0)
|
14
|
+
rspec-mocks (~> 3.8.0)
|
15
|
+
rspec-core (3.8.0)
|
16
|
+
rspec-support (~> 3.8.0)
|
17
|
+
rspec-expectations (3.8.2)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.8.0)
|
20
|
+
rspec-mocks (3.8.0)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.8.0)
|
23
|
+
rspec-support (3.8.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
ruby
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
bundler (~> 1.16)
|
30
|
+
rake (~> 13.0)
|
31
|
+
rspec (~> 3.0)
|
32
|
+
ruby-mutant!
|
33
|
+
|
34
|
+
BUNDLED WITH
|
35
|
+
1.17.3
|
data/LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2018 Corey Schaf
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2018 TODO: Write your name
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
# Ruby Mutant
|
2
|
+
[](https://travis-ci.org/coreyjs/ruby-mutant)
|
3
|
+
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
|
8
|
+
```ruby
|
9
|
+
gem 'ruby-mutant', git: 'https://github.com/coreyjs/ruby-mutant'
|
10
|
+
```
|
11
|
+
|
12
|
+
---
|
13
|
+
## Usage
|
14
|
+
|
15
|
+
Ruby Mutant is a lightweight mutations library to help you encapsulate your business logic into decoupled, testable mutations. With Ruby Mutant you can easily add executable code with validation, helping you decouple important logic from your application.
|
16
|
+
|
17
|
+
To create a mutation from a ruby object, include the ruby module `require "mutatant"`
|
18
|
+
|
19
|
+
|
20
|
+
|
21
|
+
```ruby
|
22
|
+
require "mutant"
|
23
|
+
|
24
|
+
class RecipeCreatedMutation
|
25
|
+
include Mutant
|
26
|
+
|
27
|
+
required_attr :recipe
|
28
|
+
|
29
|
+
#Define custom validators for our attributes
|
30
|
+
def validate_name?
|
31
|
+
true
|
32
|
+
end
|
33
|
+
|
34
|
+
# Required, this will execute our new mutation.
|
35
|
+
def execute(args)
|
36
|
+
# here, recipe is passe into out Class.run(recipe=Recipe.new) method
|
37
|
+
if recipe.difficulty == 'godlike'
|
38
|
+
recipe_service.send_alert_new_super_recipe_confirmation()
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
```
|
43
|
+
|
44
|
+
---
|
45
|
+
To run a mutation definition:
|
46
|
+
```ruby
|
47
|
+
# run() excepts any number of parameters that you want to pass into your mutation
|
48
|
+
output = RecipeCreatedMutation.run(recipe: Recipe.new, obj2: obj2, ....)
|
49
|
+
```
|
50
|
+
|
51
|
+
Every mutation execution will return an `output` object. This object contains information on the
|
52
|
+
mutation execution, and errors occurred or any metadata that's needed to be returned from the mutation,
|
53
|
+
in the form of a hash.
|
54
|
+
|
55
|
+
Any meta data that needs to be returned can be added to the output object using the
|
56
|
+
helper method inside your mutation:
|
57
|
+
|
58
|
+
```ruby
|
59
|
+
class RecipeCreatedMutation
|
60
|
+
include Mutant
|
61
|
+
...
|
62
|
+
def execute(args)
|
63
|
+
output.add_meta(:test, 'value')
|
64
|
+
end
|
65
|
+
|
66
|
+
...
|
67
|
+
|
68
|
+
output = RecipeCreatedMutation.run()
|
69
|
+
output.meta[:test] # >> 'value'
|
70
|
+
|
71
|
+
```
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
output = RecipeCreatedMutation.run(obj: obj1)
|
75
|
+
output.success? # >> true
|
76
|
+
output.errors # >> [err1, err1, ...]
|
77
|
+
output.meta # > {:my => 'value', :other => 'value1'}
|
78
|
+
```
|
79
|
+
|
80
|
+
---
|
81
|
+
## How to use this library
|
82
|
+
|
83
|
+
### Rails:
|
84
|
+
We could define a folder structure such as this, for our rails Recipe web app:
|
85
|
+
```
|
86
|
+
/lib/use_cases/recipes/recipe_created_mutation.rb
|
87
|
+
/lib/use_cases/user/new_user_signed_up_mutation.rb
|
88
|
+
```
|
89
|
+
|
90
|
+
`recipe_created_mutation.rb`
|
91
|
+
```ruby
|
92
|
+
require 'mutant'
|
93
|
+
|
94
|
+
module UseCases::Recipes
|
95
|
+
class RecipeCreatedMutation
|
96
|
+
include Mutant
|
97
|
+
|
98
|
+
required_attr :recipe
|
99
|
+
|
100
|
+
def execute(args)
|
101
|
+
if recipe.name.blank?
|
102
|
+
puts 'whoops this recipe is bad'
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
end
|
108
|
+
```
|
109
|
+
|
110
|
+
And in a controller, we can execute the mutation like so:
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
class RecipesController < ApplicationController
|
114
|
+
include UseCases::Recipes
|
115
|
+
|
116
|
+
|
117
|
+
def create
|
118
|
+
@recipe = Recipe.new(recipe_params)
|
119
|
+
|
120
|
+
output = RecipeCreatedMutation.run(recipe: @recipe)
|
121
|
+
|
122
|
+
if output.success?
|
123
|
+
puts 'everything went super duper good'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
...
|
128
|
+
end
|
129
|
+
```
|
130
|
+
|
131
|
+
|
132
|
+
---
|
133
|
+
|
134
|
+
|
135
|
+
## Contributing
|
136
|
+
|
137
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/coreyjs/ruby-mutant. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
|
138
|
+
|
139
|
+
For more information on contributing, here: [How To Contribute](https://github.com/coreyjs/ruby-mutant/blob/master/CONTRIBUTING.md)
|
140
|
+
|
141
|
+
## License
|
142
|
+
|
143
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
144
|
+
|
145
|
+
|
146
|
+
## Special thanks to the inspiration for this library!
|
147
|
+
|
148
|
+
https://github.com/cypriss/mutations
|
149
|
+
|
150
|
+
https://github.com/omarish/mutations
|
151
|
+
|
152
|
+
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require "bundler/setup"
|
4
|
+
require "ruby-mutant"
|
5
|
+
|
6
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
7
|
+
# with your gem easier. You can also use a different console, if you like.
|
8
|
+
|
9
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
10
|
+
# require "pry"
|
11
|
+
# Pry.start
|
12
|
+
|
13
|
+
require "irb"
|
14
|
+
IRB.start(__FILE__)
|
data/bin/setup
ADDED
data/lib/mutant.rb
ADDED
@@ -0,0 +1,156 @@
|
|
1
|
+
require 'ruby-mutant/exceptions/mutation_validation_exception'
|
2
|
+
require 'ruby-mutant/exceptions/mutation_setup_exception'
|
3
|
+
require 'ruby-mutant/exceptions/mutation_missing_required_var_exception'
|
4
|
+
require 'ruby-mutant/output'
|
5
|
+
require "bundler/setup"
|
6
|
+
|
7
|
+
|
8
|
+
module Mutant
|
9
|
+
attr_accessor :output
|
10
|
+
|
11
|
+
def self.included(klass)
|
12
|
+
klass.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def required_attr(*attrs)
|
17
|
+
puts 'Mutant::required_attr'
|
18
|
+
# This defines a method called required_attr that will
|
19
|
+
# return an array of symbols for all the properties of the
|
20
|
+
# mutation that we should have defined.
|
21
|
+
define_method(:required_attr) { attrs ||= [] }
|
22
|
+
end
|
23
|
+
|
24
|
+
# The entry point, main method that will be execute any mutation logic
|
25
|
+
#
|
26
|
+
# == Parameters:
|
27
|
+
# args::
|
28
|
+
# A hash of inputs suppplied when the user runs the mutation.
|
29
|
+
# i.e. MyMutation.run(name: 'jon', house: 'stark')
|
30
|
+
#
|
31
|
+
# == Returns:
|
32
|
+
# An instance of `Mutant::Output`. This object defines all
|
33
|
+
# errors, metadata and success definition of the mutation
|
34
|
+
def run(args = {})
|
35
|
+
unless args.has_key?(:raise_on_error)
|
36
|
+
args[:raise_on_error] = true
|
37
|
+
end
|
38
|
+
args[:raise_on_error].freeze
|
39
|
+
|
40
|
+
puts 'Mutant::self.run(*args) '
|
41
|
+
obj = new(args)
|
42
|
+
|
43
|
+
# Ensure the mutation has the correct method
|
44
|
+
unless obj.respond_to?(:execute)
|
45
|
+
raise MutationSetupException.new(msg='Missing execute method')
|
46
|
+
end
|
47
|
+
|
48
|
+
# 1. We want to run the validators first, then determine if we should continue
|
49
|
+
errs = obj.send(:validate)
|
50
|
+
obj.output.errors = obj.output.errors + errs
|
51
|
+
if errs.length > 0
|
52
|
+
if args[:raise_on_error]
|
53
|
+
raise MutationSetupException.new(msg='Validation failed')
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# 2. Check to see the mutation has the corresponding inst vars
|
58
|
+
args.each do |k, val|
|
59
|
+
puts "Mutant::var check '#{k}', responds? #{obj.respond_to? k.to_sym}"
|
60
|
+
|
61
|
+
# First make sure this mutation obj has the correct vars,
|
62
|
+
# if not, then proceeed to create them
|
63
|
+
unless obj.respond_to? k.to_sym
|
64
|
+
# create the attr_accessor for the missing vars
|
65
|
+
obj.class.send(:define_method, "#{k}=".to_sym) do |value|
|
66
|
+
instance_variable_set("@" + k.to_s, value)
|
67
|
+
end
|
68
|
+
obj.class.send(:define_method, k.to_sym) do
|
69
|
+
instance_variable_get("@" + k.to_s)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# 3. Propagate the values from the mutation props to the class
|
74
|
+
obj.send("#{k}=".to_sym, val)
|
75
|
+
end
|
76
|
+
|
77
|
+
# 3 If this instance defines :required_attr
|
78
|
+
if obj.respond_to? :required_attr
|
79
|
+
required_attr_errors = obj.send(:check_required_attrs)
|
80
|
+
unless required_attr_errors.length == 0
|
81
|
+
# We need to handle any errors we get back from our
|
82
|
+
# required_attr validator
|
83
|
+
obj.output.errors += required_attr_errors
|
84
|
+
if args[:raise_on_error]
|
85
|
+
raise required_attr_errors[0]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# 4. Run execute method to run mutation logic
|
91
|
+
obj.execute(args)
|
92
|
+
# Return out Output obj, with all meta data regarding the ran mutation
|
93
|
+
obj.output
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def initialize(*args)
|
98
|
+
@output = Output.new
|
99
|
+
end
|
100
|
+
|
101
|
+
private
|
102
|
+
|
103
|
+
# This will run all ou validation functions on our mutation class.
|
104
|
+
# This will return an array of MutationValidationException, to the class method, run()
|
105
|
+
#
|
106
|
+
# == Parameters:
|
107
|
+
#
|
108
|
+
# == Returns:
|
109
|
+
# errors. An array of errors representing all validation methods that have failed.
|
110
|
+
# (defaults to `[]`)
|
111
|
+
def validate
|
112
|
+
errors = []
|
113
|
+
self.public_methods.each do |m|
|
114
|
+
if m.to_s.start_with?('validate_') && m.to_s.end_with?('?')
|
115
|
+
# execute validation method
|
116
|
+
res = self.send(m)
|
117
|
+
|
118
|
+
# unless the response is truthy
|
119
|
+
unless res
|
120
|
+
errors << MutationValidationException.new(msg='Validator has returned false', validator=m)
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
errors
|
125
|
+
end
|
126
|
+
|
127
|
+
# Checks to see if any `required_attr` has been set, if so check to see if each one
|
128
|
+
# is defined in either the .run() definition or the mutation's `attr_accesor`.
|
129
|
+
# TODO need to also check that these required attributes have values
|
130
|
+
#
|
131
|
+
# == Parameters:
|
132
|
+
#
|
133
|
+
# == Returns:
|
134
|
+
# An array of errors, of type `MutationMissingRequiredVarException`, for each
|
135
|
+
# missing required attribute.
|
136
|
+
def check_required_attrs
|
137
|
+
# In this we need to compare what we define in
|
138
|
+
# required_attr(*attrs) against what we have defined in
|
139
|
+
# the mutation vs what we pass into the run() definition
|
140
|
+
errors = []
|
141
|
+
puts 'Mutant::check_required_attrs'
|
142
|
+
self.required_attr.each do |attr|
|
143
|
+
if !self.respond_to?(attr)
|
144
|
+
# Our attribute is not defined on our mutation class
|
145
|
+
# So we will build the error to return to the run()
|
146
|
+
# method, which can determine how we proceed
|
147
|
+
err = MutationMissingRequiredVarException.new(
|
148
|
+
msg="A property that is marked as required is not defined on the mutation: #{attr}",
|
149
|
+
prop=attr)
|
150
|
+
errors << err
|
151
|
+
end
|
152
|
+
end
|
153
|
+
errors
|
154
|
+
end
|
155
|
+
|
156
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Mutant
|
2
|
+
class Output
|
3
|
+
|
4
|
+
# Allows to reading the status of the executed mutation
|
5
|
+
attr_reader :success
|
6
|
+
|
7
|
+
# @return [Hash] the hash of metadata the user wants returned from the mutation execution
|
8
|
+
attr_accessor :meta
|
9
|
+
|
10
|
+
# @return [Array] The get/set for the mutations errors that have been thrown
|
11
|
+
attr_accessor :errors
|
12
|
+
|
13
|
+
# Output constructor
|
14
|
+
#
|
15
|
+
# == Parameters:
|
16
|
+
# success::
|
17
|
+
# The successful state of the mutation that owns this object. (defaults to `true`)
|
18
|
+
# errors::
|
19
|
+
# An array of errors that can be packaged and wrapped in this object to be
|
20
|
+
# `returned` from the mutation's `execute` method. (defaults to `[]`)
|
21
|
+
# meta::
|
22
|
+
# A hash map that can be used to set any information that needs to be returned
|
23
|
+
# from the mutation to the calling code. (defaults to `{}`)
|
24
|
+
#
|
25
|
+
def initialize(success=true, errors=[], meta={})
|
26
|
+
@success = success
|
27
|
+
@errors = errors
|
28
|
+
@meta = meta
|
29
|
+
end
|
30
|
+
|
31
|
+
# Adds meta data to the @meta hash
|
32
|
+
# (will overwrite existing value)
|
33
|
+
#
|
34
|
+
# == Parameters:
|
35
|
+
# key::
|
36
|
+
# A symbol that will be used to store the value in the hash
|
37
|
+
#
|
38
|
+
# value::
|
39
|
+
# Any value that you wish to store indexed by the `key` param.
|
40
|
+
# This can be used if you need to return data from the mutation's `execute` method
|
41
|
+
#
|
42
|
+
# == Returns:
|
43
|
+
# Nothing useful
|
44
|
+
def add_meta(key, value)
|
45
|
+
@meta[key] = value
|
46
|
+
end
|
47
|
+
|
48
|
+
# Helper method to determine successfulnes of the mutation that has ran. A successful
|
49
|
+
# mutation execution is defined by having an error count of 0.
|
50
|
+
# TODO: This needs to be handled better, more smart about what success means
|
51
|
+
#
|
52
|
+
# == Parameters:
|
53
|
+
#
|
54
|
+
# == Returns:
|
55
|
+
# A boolean that represents the status of the just execute mutation
|
56
|
+
def success?
|
57
|
+
@success
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/ruby-mutant.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
|
2
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "ruby-mutant/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "ruby-mutant"
|
8
|
+
spec.version = Mutant::VERSION
|
9
|
+
spec.authors = ["Corey Schaf"]
|
10
|
+
spec.email = ["cschaf@gmail.com"]
|
11
|
+
|
12
|
+
spec.summary = %q{Clean up your code by encapsulating business logic in mutations!}
|
13
|
+
spec.description = %q{Object mutations that encapsulate business logic.
|
14
|
+
RubyMutant makes it simple to add complex logic to objects, in with automatic validation and execution.}
|
15
|
+
spec.homepage = "https://github.com/coreyjs/ruby-mutant"
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
# Specify which files should be added to the gem when it is released.
|
19
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
20
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
21
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
22
|
+
end
|
23
|
+
spec.bindir = "exe"
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ["lib"]
|
26
|
+
|
27
|
+
spec.add_development_dependency "bundler", "~> 1.16"
|
28
|
+
spec.add_development_dependency "rake", "~> 13.0"
|
29
|
+
spec.add_development_dependency "rspec", "~> 3.0"
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ruby-mutant
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Corey Schaf
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-05-01 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.16'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.16'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '13.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '13.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.0'
|
55
|
+
description: |-
|
56
|
+
Object mutations that encapsulate business logic.
|
57
|
+
RubyMutant makes it simple to add complex logic to objects, in with automatic validation and execution.
|
58
|
+
email:
|
59
|
+
- cschaf@gmail.com
|
60
|
+
executables: []
|
61
|
+
extensions: []
|
62
|
+
extra_rdoc_files: []
|
63
|
+
files:
|
64
|
+
- ".github/ISSUE_TEMPLATE/bug_report.md"
|
65
|
+
- ".github/ISSUE_TEMPLATE/feature_request.md"
|
66
|
+
- ".gitignore"
|
67
|
+
- ".rakeTasks"
|
68
|
+
- ".rspec"
|
69
|
+
- ".travis.yml"
|
70
|
+
- CODE_OF_CONDUCT.md
|
71
|
+
- CONTRIBUTING.md
|
72
|
+
- Gemfile
|
73
|
+
- Gemfile.lock
|
74
|
+
- LICENSE
|
75
|
+
- LICENSE.txt
|
76
|
+
- README.md
|
77
|
+
- Rakefile
|
78
|
+
- bin/console
|
79
|
+
- bin/setup
|
80
|
+
- lib/mutant.rb
|
81
|
+
- lib/ruby-mutant/base.rb
|
82
|
+
- lib/ruby-mutant/exceptions/mutation_missing_required_var_exception.rb
|
83
|
+
- lib/ruby-mutant/exceptions/mutation_setup_exception.rb
|
84
|
+
- lib/ruby-mutant/exceptions/mutation_validation_exception.rb
|
85
|
+
- lib/ruby-mutant/output.rb
|
86
|
+
- lib/ruby-mutant/version.rb
|
87
|
+
- ruby-mutant.gemspec
|
88
|
+
homepage: https://github.com/coreyjs/ruby-mutant
|
89
|
+
licenses:
|
90
|
+
- MIT
|
91
|
+
metadata: {}
|
92
|
+
post_install_message:
|
93
|
+
rdoc_options: []
|
94
|
+
require_paths:
|
95
|
+
- lib
|
96
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
97
|
+
requirements:
|
98
|
+
- - ">="
|
99
|
+
- !ruby/object:Gem::Version
|
100
|
+
version: '0'
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
|
+
requirements:
|
103
|
+
- - ">="
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
requirements: []
|
107
|
+
rubyforge_project:
|
108
|
+
rubygems_version: 2.7.7
|
109
|
+
signing_key:
|
110
|
+
specification_version: 4
|
111
|
+
summary: Clean up your code by encapsulating business logic in mutations!
|
112
|
+
test_files: []
|