makwa 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1efddd0368efae3bf02af1759d3923729f39414c2d728c3567aa911ec2bd5892
4
- data.tar.gz: ff81e0decb31353995008f0a4c3d69c14f741d45a30a72fa7015f8149b377ca3
3
+ metadata.gz: 2da52aacefd38c1922e5e8b48277fca8106997a068b682d93698bd9e444da41f
4
+ data.tar.gz: a506c6b8ae75f3d093afdf455b84733f0d0984843561dde099bbbb9ffddc7e91
5
5
  SHA512:
6
- metadata.gz: ca622d29e65ddd191fcaa8dad747e4a7ed7ca1fb3fba11cf2749276f61ab38706e88d36a4ed6d531fcadebc7a28fb9c767932039684bd40b706df081ac37201f
7
- data.tar.gz: f84ceb0ed0e479c2aa18ab58a9ee3504591c3b3df0e444eb4c111aa09c204fdef198f3b49073d297127622e9027b6d8a719a055ddf9aafbc6c81075a59430375
6
+ metadata.gz: 8784e825031765550dce4b56c82aae5a872d403cf14f444cdb0a2afb00fd2603e14f28910a92f3415b60df351820a17f738264eb6bf3bacd050f3ac21ee79c43
7
+ data.tar.gz: ea5dde94eca0a95abda600e4bd8f129dc63413a855cdf3ffb9ee5f15176ee7f1edcd368216265aff272def00ca4795b647f7667bd6bbe58ec3a742ab361274bd
data/.gitignore CHANGED
@@ -2,7 +2,9 @@
2
2
  /.yardoc
3
3
  /_yardoc/
4
4
  /coverage/
5
- /doc/
6
5
  /pkg/
7
6
  /spec/reports/
8
7
  /tmp/
8
+
9
+ Gemfile.lock
10
+ .byebug_history
data/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.0] - 2021-10-27
4
+
5
+ - Adds implementation
6
+ - Updates ReturningInteraction to copy errors and values to returned object on invalid inputs
7
+
3
8
  ## [0.1.0] - 2021-09-23
4
9
 
5
10
  - Initial release
data/LICENSE.txt CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2021 Jo Hund
3
+ Copyright (c) 2021 Animikii
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -1,4 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "bundler/gem_tasks"
4
- task default: %i[]
4
+
5
+ require "rake/testtask"
6
+
7
+ Rake::TestTask.new(:test) do |t|
8
+ t.libs << "test"
9
+ t.pattern = "test/**/*_test.rb"
10
+ t.verbose = false
11
+ end
12
+
13
+ task default: :test
@@ -0,0 +1,14 @@
1
+ # How to release a new version
2
+
3
+ * Make changes to the code
4
+ * Update tests as needed
5
+ * Update CHANGELOG.md
6
+ * Commit changes
7
+ * Prepare new release
8
+ * Bump version via one of
9
+ * `gem bump --tag --version major`
10
+ * `gem bump --tag --version minor`
11
+ * `gem bump --tag --version patch`
12
+ * The bump command will commit the new version and tag the commit.
13
+ * Distribute new release
14
+ * `gem release` - This will push the new release to rubygems.org.
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Makwa
4
+ class Interaction < ::ActiveInteraction::Base
5
+ class Interrupt < Object.const_get("::ActiveInteraction::Interrupt")
6
+ end
7
+
8
+ #
9
+ # Safely checking for errors
10
+ #
11
+
12
+ # Use this instead of `outcome.invalid?` (which is destructive and clears errors)
13
+ delegate :any?, to: :errors, prefix: true # def errors_any?
14
+ # Use this instead of `outcome.valid?` (which is destructive and clears errors)
15
+ delegate :empty?, to: :errors, prefix: true # def errors_empty?
16
+
17
+ #
18
+ # Halting of execution
19
+ #
20
+
21
+ # Exits early if there are any errors.
22
+ def return_if_errors!
23
+ raise(Interrupt, errors) if errors_any?
24
+ end
25
+
26
+ #
27
+ # Logging interaction execution
28
+ #
29
+
30
+ # Log execution of interaction, caller, and inputs
31
+ set_callback :type_check, :before, ->(interaction) {
32
+ debug("Executing interaction #{interaction.class.name} #{interaction.id_marker}")
33
+ calling_interaction = interaction.calling_interaction
34
+ debug(" ↳ called from #{calling_interaction} #{interaction.id_marker}") if calling_interaction.present?
35
+ # The next two lines offer two ways of printing inputs: Either truncated, or full. Adjust as needed.
36
+ # truncated_inputs = interaction.inputs.inspect.truncate_in_the_middle(2000, omission: "\n... [inputs truncated] ...\n")
37
+ truncated_inputs = interaction.inputs.inspect
38
+ debug(" ↳ inputs: #{truncated_inputs} #{interaction.id_marker}")
39
+ }
40
+
41
+ # Log interaction's outcome and errors if any.
42
+ set_callback :execute, :after, ->(interaction) {
43
+ if interaction.errors_empty?
44
+ debug(" ↳ outcome: succeeded (id##{interaction.object_id})")
45
+ else
46
+ debug(" ↳ outcome: failed (id##{interaction.object_id})")
47
+ debug(" ↳ errors: #{interaction.errors.details} (id##{interaction.object_id})")
48
+ end
49
+ }
50
+
51
+ # @return [Array<String>] the callstack containing interactions only, starting with the immediate caller.
52
+ def calling_interactions
53
+ @calling_interactions ||= caller.find_all { |e|
54
+ e.index("/app/interactions/") && !e.index(__FILE__) && !e.index("/returning_interaction.rb")
55
+ }
56
+ end
57
+
58
+ # @return [String] the backtrace entry for the immediately calling interaction (first item in calling_interactions).
59
+ def calling_interaction
60
+ @calling_interaction ||= calling_interactions.first&.split("/interactions/")&.last || ""
61
+ end
62
+
63
+ # The standard method for all logging output. Turn this on for detailed interaction logging.
64
+ def debug(txt)
65
+ # puts indent + txt
66
+ end
67
+
68
+ # @return [String] a marker that identifies an interaction instance by its Ruby object_id. This is helpful
69
+ # when following an execution log with nested or interleaved interaction log lines.
70
+ def id_marker
71
+ "(id##{object_id})"
72
+ end
73
+
74
+ # @return [String] a prefix that indents each debug line according to the level of interactions nesting.
75
+ def indent
76
+ lvl = [0, calling_interactions.count].max
77
+ " " * lvl
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Makwa
4
+ # @abstract Override {#execute_returning} and call {#returning} in the class body, passing in the symbol
5
+ # of the input you want returned. Guaranteed to return the returning input, with interaction errors
6
+ # merged into it.
7
+ class ReturningInteraction < ::Makwa::Interaction
8
+ class ReturnFilterInexistent < StandardError; end
9
+
10
+ class ReturningFilterNotSpecified < StandardError; end
11
+
12
+ class NotActiveModelErrorable < StandardError; end
13
+
14
+ define_callbacks(:execute_returning)
15
+
16
+ class << self
17
+ # @param (see ActiveInteraction::Runnable#initialize)
18
+ #
19
+ # @return (see ReturningInteraction#run_returning!)
20
+ def run_returning!(*args)
21
+ new(*args).send(:run_returning!)
22
+ end
23
+
24
+ # @param return_filter [Symbol] Name of the input filter to be returned
25
+ def returning(return_filter)
26
+ @return_filter = return_filter
27
+ end
28
+ end
29
+
30
+ # @abstract
31
+ #
32
+ # @raise [NotImplementedError]
33
+ def execute_returning
34
+ raise NotImplementedError, "You need to implemented the method #execute_returning in your interaction."
35
+ end
36
+
37
+ private
38
+
39
+ # @return [Object]
40
+ def run_returning!
41
+ @_interaction_result = return_input # {#result=} has side-effects
42
+ raise ReturningFilterNotSpecified unless self.class.instance_variable_defined?(:@return_filter)
43
+ raise ReturnFilterInexistent unless result
44
+ raise NotActiveModelErrorable unless result.respond_to?(:errors) && result.errors.respond_to?(:merge!)
45
+
46
+ # Run validations (explicitly, don't rely on #valid?)
47
+ validate
48
+ if errors_any?
49
+ # Add errors and values to the result object (so that the form can render them) and return the result object
50
+ return result
51
+ .tap { |r| r.errors.merge!(errors) }
52
+ .tap { |r| r.assign_attributes(inputs.except(@return_filter)) }
53
+ end
54
+
55
+ # Otherwise run the body of the interaction (along with any callbacks) ...
56
+ run_callbacks(:execute_returning) do
57
+ execute_returning
58
+ rescue ::Makwa::Interaction::Interrupt
59
+ # Do nothing
60
+ end
61
+
62
+ # ... and return the result, merging in any errors added in the body of the interaction that are not duplicates.
63
+ # Duplicates would occur if, for example, the body of the interaction calls
64
+ # `errors.merge!(<returning_filter>.errors)` as is often done in non-returning interactions.
65
+ result.tap do |r|
66
+ errors.each do |e|
67
+ r.errors.add(e.attribute, e.message) unless r.errors.added?(e.attribute, e.message)
68
+ end
69
+ end
70
+ end
71
+
72
+ # @return [Object]
73
+ def return_input
74
+ @return_input ||= inputs[self.class.instance_variable_get(:@return_filter)]
75
+ end
76
+
77
+ # @param other [Class] The other interaction.
78
+ #
79
+ # @return (see #result)
80
+ def compose(other, *args)
81
+ @_interaction_result = other.run_returning!(*args)
82
+
83
+ if block_given?
84
+ errors.merge!(@_interaction_result.errors)
85
+ yield @_interaction_result
86
+ end
87
+
88
+ @_interaction_result
89
+ rescue NotImplementedError
90
+ super(other, *args)
91
+ end
92
+ end
93
+ end
data/lib/makwa/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Makwa
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
data/lib/makwa.rb CHANGED
@@ -1,8 +1,12 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative "makwa/version"
3
+ require "active_interaction"
4
4
 
5
5
  module Makwa
6
- class Error < StandardError; end
7
- # Your code goes here...
8
6
  end
7
+
8
+ # Dependency boundary
9
+
10
+ require "makwa/interaction"
11
+ require "makwa/returning_interaction"
12
+ require "makwa/version"
data/makwa.gemspec CHANGED
@@ -3,14 +3,18 @@
3
3
  require_relative "lib/makwa/version"
4
4
 
5
5
  Gem::Specification.new do |spec|
6
- spec.name = "makwa"
7
- spec.version = Makwa::VERSION
8
- spec.authors = ["Jo Hund"]
9
- spec.email = ["jhund@clearcove.ca"]
10
-
11
- spec.summary = "Interactions for Ruby on Rails apps."
12
- spec.homepage = "https://github.com/animikii/makwa"
13
- spec.license = "MIT"
6
+ spec.name = "makwa"
7
+ spec.version = Makwa::VERSION
8
+ {
9
+ "Jo Hund" => "jo@animikii.com",
10
+ "Fabio Papa" => "fabio.papa@animikii.com"
11
+ }.tap do |hash|
12
+ spec.authors = hash.keys
13
+ spec.email = hash.values
14
+ end
15
+ spec.summary = "Interactions for Ruby on Rails apps."
16
+ spec.homepage = "https://github.com/animikii/makwa"
17
+ spec.license = "MIT"
14
18
  spec.required_ruby_version = ">= 2.4.0"
15
19
 
16
20
  spec.metadata["homepage_uri"] = spec.homepage
@@ -22,13 +26,10 @@ Gem::Specification.new do |spec|
22
26
  spec.files = Dir.chdir(File.expand_path(__dir__)) do
23
27
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A(?:test|spec|features)/}) }
24
28
  end
25
- spec.bindir = "exe"
26
- spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
29
  spec.require_paths = ["lib"]
28
30
 
29
- # Uncomment to register a new dependency of your gem
30
- # spec.add_dependency "example-gem", "~> 1.0"
31
+ spec.add_dependency "active_interaction", "~> 4.0"
31
32
 
32
- # For more information and examples about making a new gem, checkout our
33
- # guide at: https://bundler.io/guides/creating_gem.html
33
+ spec.add_development_dependency "standard"
34
+ spec.add_development_dependency "byebug"
34
35
  end
metadata CHANGED
@@ -1,18 +1,62 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: makwa
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jo Hund
8
+ - Fabio Papa
8
9
  autorequire:
9
- bindir: exe
10
+ bindir: bin
10
11
  cert_chain: []
11
- date: 2021-09-23 00:00:00.000000000 Z
12
- dependencies: []
12
+ date: 2021-10-27 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: active_interaction
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '4.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '4.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: standard
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: byebug
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
13
56
  description:
14
57
  email:
15
- - jhund@clearcove.ca
58
+ - jo@animikii.com
59
+ - fabio.papa@animikii.com
16
60
  executables: []
17
61
  extensions: []
18
62
  extra_rdoc_files: []
@@ -25,7 +69,10 @@ files:
25
69
  - Rakefile
26
70
  - bin/console
27
71
  - bin/setup
72
+ - doc/how_to_release_new_version.md
28
73
  - lib/makwa.rb
74
+ - lib/makwa/interaction.rb
75
+ - lib/makwa/returning_interaction.rb
29
76
  - lib/makwa/version.rb
30
77
  - makwa.gemspec
31
78
  homepage: https://github.com/animikii/makwa