trailblazer 2.0.7 → 2.1.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.
Files changed (72) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
  4. data/.rubocop.yml +20 -0
  5. data/.rubocop_todo.yml +556 -0
  6. data/.travis.yml +6 -10
  7. data/CHANGES.md +83 -1
  8. data/COMM-LICENSE +46 -75
  9. data/CONTRIBUTING.md +179 -0
  10. data/Gemfile +0 -27
  11. data/{LICENSE.txt → LICENSE} +4 -4
  12. data/README.md +39 -138
  13. data/Rakefile +2 -19
  14. data/lib/trailblazer.rb +3 -17
  15. data/lib/trailblazer/version.rb +3 -1
  16. data/test/test_helper.rb +12 -3
  17. data/trailblazer.gemspec +10 -14
  18. metadata +22 -147
  19. data/doc/Trb-The-Stack.png +0 -0
  20. data/doc/operation-2017.png +0 -0
  21. data/doc/trb.jpg +0 -0
  22. data/lib/trailblazer/dsl.rb +0 -47
  23. data/lib/trailblazer/operation/auto_inject.rb +0 -47
  24. data/lib/trailblazer/operation/callback.rb +0 -35
  25. data/lib/trailblazer/operation/contract.rb +0 -46
  26. data/lib/trailblazer/operation/guard.rb +0 -18
  27. data/lib/trailblazer/operation/model.rb +0 -60
  28. data/lib/trailblazer/operation/module.rb +0 -29
  29. data/lib/trailblazer/operation/nested.rb +0 -113
  30. data/lib/trailblazer/operation/persist.rb +0 -10
  31. data/lib/trailblazer/operation/policy.rb +0 -35
  32. data/lib/trailblazer/operation/procedural/contract.rb +0 -15
  33. data/lib/trailblazer/operation/procedural/validate.rb +0 -22
  34. data/lib/trailblazer/operation/pundit.rb +0 -38
  35. data/lib/trailblazer/operation/representer.rb +0 -31
  36. data/lib/trailblazer/operation/rescue.rb +0 -21
  37. data/lib/trailblazer/operation/test.rb +0 -17
  38. data/lib/trailblazer/operation/validate.rb +0 -68
  39. data/lib/trailblazer/operation/wrap.rb +0 -25
  40. data/test/docs/auto_inject_test.rb +0 -30
  41. data/test/docs/contract_test.rb +0 -525
  42. data/test/docs/dry_test.rb +0 -31
  43. data/test/docs/fast_test.rb +0 -164
  44. data/test/docs/guard_test.rb +0 -169
  45. data/test/docs/macro_test.rb +0 -36
  46. data/test/docs/model_test.rb +0 -75
  47. data/test/docs/nested_test.rb +0 -334
  48. data/test/docs/operation_test.rb +0 -408
  49. data/test/docs/policy_test.rb +0 -2
  50. data/test/docs/pundit_test.rb +0 -133
  51. data/test/docs/representer_test.rb +0 -268
  52. data/test/docs/rescue_test.rb +0 -154
  53. data/test/docs/wrap_test.rb +0 -183
  54. data/test/gemfiles/Gemfile.ruby-1.9 +0 -3
  55. data/test/gemfiles/Gemfile.ruby-2.0 +0 -12
  56. data/test/gemfiles/Gemfile.ruby-2.3 +0 -12
  57. data/test/module_test.rb +0 -100
  58. data/test/operation/callback_test.rb +0 -70
  59. data/test/operation/contract_test.rb +0 -420
  60. data/test/operation/dsl/callback_test.rb +0 -106
  61. data/test/operation/dsl/contract_test.rb +0 -294
  62. data/test/operation/dsl/representer_test.rb +0 -169
  63. data/test/operation/model_test.rb +0 -60
  64. data/test/operation/params_test.rb +0 -36
  65. data/test/operation/persist_test.rb +0 -44
  66. data/test/operation/pipedream_test.rb +0 -59
  67. data/test/operation/pipetree_test.rb +0 -104
  68. data/test/operation/present_test.rb +0 -24
  69. data/test/operation/pundit_test.rb +0 -104
  70. data/test/operation/representer_test.rb +0 -254
  71. data/test/operation/resolver_test.rb +0 -47
  72. data/test/operation_test.rb +0 -143
Binary file
Binary file
Binary file
@@ -1,47 +0,0 @@
1
- module Trailblazer
2
- module DSL
3
- # Boring DSL code that allows to set a skill class, or define it ad-hoc using a block.
4
- # passing a constant always wipes out the existing class.
5
- #
6
- # Used in Contract, Representer, Callback, ..
7
- class Build
8
- # options[:prefix]
9
- # options[:class]
10
- # options[:container]
11
-
12
- # Currently, adds .class only to classes. this could break builder instances?
13
-
14
- def call(options, name=nil, constant=nil, dsl_block, &block)
15
- # contract MyForm
16
- if name.is_a?(Class)
17
- constant = name
18
- name = :default
19
- end
20
-
21
- is_instance = !(constant.kind_of?(Class) || dsl_block) # i don't like this magic too much, but since it's the only DSL method in TRB, it should be ok. # DISCUSS: options[:is_instance]
22
-
23
- path = path_name(options[:prefix], name, is_instance ? nil : "class") # "contract.default.class"
24
-
25
- if is_instance
26
- skill = constant
27
- else
28
- extended = options[:container][path] # Operation["contract.default.class"]
29
- extended = yield extended if extended && block_given?
30
-
31
- # only extend an existing skill class when NO constant was passed.
32
- constant = (extended || options[:class]) if constant.nil?# && block_given?
33
-
34
- skill = Class.new(constant)
35
- skill.class_eval(&dsl_block) if dsl_block
36
- end
37
-
38
- [path, skill]
39
- end
40
-
41
- private
42
- def path_name(prefix, name, suffix)
43
- [prefix, name, suffix].compact.join(".") # "contract.class" for default, otherwise "contract.params.class" etc.
44
- end
45
- end
46
- end
47
- end
@@ -1,47 +0,0 @@
1
- require "dry/auto_inject"
2
-
3
- class Trailblazer::Operation
4
- # Thanks, @timriley! <3
5
- # https://gist.github.com/timriley/d314a58da9784912159006e208ba8ea9
6
- module AutoInject
7
- class InjectStrategy < Module
8
- ClassMethods = Class.new(Module)
9
-
10
- attr_reader :container
11
- attr_reader :dependency_map
12
- attr_reader :class_mod
13
-
14
- def initialize(container, *dependency_names)
15
- @container = container
16
- @dependency_map = Dry::AutoInject::DependencyMap.new(*dependency_names)
17
- @class_mod = ClassMethods.new
18
- end
19
-
20
- def included(klass)
21
- define_call
22
-
23
- klass.singleton_class.prepend @class_mod
24
-
25
- super
26
- end
27
-
28
- private
29
-
30
- def define_call
31
- class_mod.class_exec(container, dependency_map) do |container, dependency_map|
32
- define_method :call do |params={}, options={}, *dependencies|
33
- options_with_deps = dependency_map.to_h.each_with_object({}) { |(name, identifier), obj|
34
- obj[name] = options[name] || container[identifier]
35
- }.merge(options)
36
-
37
- super(params, options_with_deps, *dependencies)
38
- end
39
- end
40
- end
41
- end
42
- end
43
-
44
- def self.AutoInject(container)
45
- Dry::AutoInject(container, strategies: {default: AutoInject::InjectStrategy})
46
- end
47
- end
@@ -1,35 +0,0 @@
1
- require "disposable/callback"
2
-
3
- class Trailblazer::Operation
4
- def self.Callback(group)
5
- step = ->(input, options) { Callback.(group, input, options) }
6
-
7
- [ step, name: "callback.#{group}" ]
8
- end
9
-
10
- module Callback
11
- def self.call(name=:default, operation, options)
12
- config = options["callback.#{name}.class"] || raise #.fetch(name) # TODO: test exception
13
- group = config[:group].new(options["contract.default"])
14
-
15
- options[:context] ||= (config[:context] == :operation ? operation : group)
16
- group.(options)
17
-
18
- options["result.callback.#{name}"] = group
19
- end
20
-
21
- module DSL
22
- def callback(name=:default, constant=nil, &block)
23
- heritage.record(:callback, name, constant, &block)
24
-
25
- # FIXME: make this nicer. we want to extend same-named callback groups.
26
- # TODO: allow the same with contract, or better, test it!
27
- extended = self["callback.#{name}.class"] && self["callback.#{name}.class"]
28
-
29
- path, group_class = Trailblazer::DSL::Build.new.({ prefix: :callback, class: Disposable::Callback::Group, container: self }, name, constant, block) { |extended| extended[:group] }
30
-
31
- self[path] = { group: group_class, context: constant ? nil : :operation }
32
- end
33
- end
34
- end
35
- end
@@ -1,46 +0,0 @@
1
- # Best practices for using contract.
2
- #
3
- # * inject contract instance via constructor to #contract
4
- # * allow contract setup and memo via #contract(model, options)
5
- # * allow implicit automatic setup via #contract and class.contract_class
6
- #
7
- # Needs Operation#model.
8
- # Needs #[], #[]= skill dependency.
9
- class Trailblazer::Operation
10
- module Contract
11
- def self.Build(name:"default", constant:nil, builder: nil)
12
- step = ->(input, options) { Build.for(input, options, name: name, constant: constant, builder: builder) }
13
-
14
- [ step, name: "contract.build" ]
15
- end
16
-
17
- module Build
18
- # bla build contract at runtime.
19
- def self.for(operation, options, name:"default", constant:nil, builder: nil)
20
- # TODO: we could probably clean this up a bit at some point.
21
- contract_class = constant || options["contract.#{name}.class"]
22
- model = options["model"] # FIXME: model.default
23
- name = "contract.#{name}"
24
-
25
- return options[name] = Option::KW.(builder).(operation, options, constant: contract_class, name: name) if builder
26
-
27
- options[name] = contract_class.new(model)
28
- end
29
- end
30
-
31
- module DSL
32
- # This is the class level DSL method.
33
- # Op.contract #=> returns contract class
34
- # Op.contract do .. end # defines contract
35
- # Op.contract CommentForm # copies (and subclasses) external contract.
36
- # Op.contract CommentForm do .. end # copies and extends contract.
37
- def contract(name=:default, constant=nil, base: Reform::Form, &block)
38
- heritage.record(:contract, name, constant, &block)
39
-
40
- path, form_class = Trailblazer::DSL::Build.new.({ prefix: :contract, class: base, container: self }, name, constant, block)
41
-
42
- self[path] = form_class
43
- end
44
- end # Contract
45
- end
46
- end
@@ -1,18 +0,0 @@
1
- require "trailblazer/operation/policy"
2
-
3
- class Trailblazer::Operation
4
- module Policy
5
- def self.Guard(proc, name: :default, &block)
6
- Policy.step(Guard.build(proc), name: name)
7
- end
8
-
9
- module Guard
10
- def self.build(callable)
11
- value = Option::KW.(callable) # Operation::Option
12
-
13
- # this gets wrapped in a Operation::Result object.
14
- ->(input, options) { Result.new( !!value.(input, options), {} ) }
15
- end
16
- end # Guard
17
- end
18
- end
@@ -1,60 +0,0 @@
1
- class Trailblazer::Operation
2
- def self.Model(model_class, action=nil)
3
- step = Model.for(model_class, action)
4
-
5
- step = Pipetree::Step.new(step, "model.class" => model_class, "model.action" => action)
6
-
7
- [ step, name: "model.build" ]
8
- end
9
-
10
- module Model
11
- def self.for(model_class, action)
12
- builder = Model::Builder.new
13
-
14
- ->(input, options) do
15
- options["model"] = model = builder.(options, options["params"])
16
-
17
- options["result.model"] = result = Result.new(!model.nil?, {})
18
-
19
- result.success?
20
- end
21
- end
22
-
23
- class Builder
24
- def call(options, params)
25
- deprecate_update!(options)
26
- action = options["model.action"] || :new
27
- model_class = options["model.class"]
28
-
29
- action = :pass_through unless [:new, :find_by, :find].include?(action)
30
-
31
- send("#{action}!", model_class, params, options["model.action"])
32
- end
33
-
34
- def new!(model_class, params, *)
35
- model_class.new
36
- end
37
-
38
- def find!(model_class, params, *)
39
- model_class.find(params[:id])
40
- end
41
-
42
- # Doesn't throw an exception and will return false to divert to Left.
43
- def find_by!(model_class, params, *)
44
- model_class.find_by(id: params[:id])
45
- end
46
-
47
- # Call any method on the model class and pass :id.
48
- def pass_through!(model_class, params, action)
49
- model_class.send(action, params[:id])
50
- end
51
-
52
- private
53
- def deprecate_update!(options) # TODO: remove in 2.1.
54
- return unless options["model.action"] == :update
55
- options["model.action"] = :find
56
- warn "[Trailblazer] Model( .., :update ) is deprecated, please use :find or :find_by."
57
- end
58
- end
59
- end
60
- end
@@ -1,29 +0,0 @@
1
- module Trailblazer::Operation::Module
2
- def self.included(base)
3
- base.extend ClassMethods
4
- base.extend Included
5
- end
6
-
7
- module Included # TODO: use representable's inheritance mechanism.
8
- def included(base)
9
- super
10
- instructions.each { |cfg|
11
- method = cfg[0]
12
- args = cfg[1].dup
13
- block = cfg[2]
14
- # options = args.extract_options!.dup # we need to duplicate options has as AM::Validations messes it up later.
15
-
16
- base.send(method, *args, &block) } # property :name, {} do .. end
17
- end
18
- end
19
-
20
- module ClassMethods
21
- def method_missing(method, *args, &block)
22
- instructions << [method, args, block]
23
- end
24
-
25
- def instructions
26
- @instructions ||= []
27
- end
28
- end
29
- end
@@ -1,113 +0,0 @@
1
- class Trailblazer::Operation
2
- def self.Nested(callable, input:nil, output:nil)
3
- step = Nested.for(callable, input, output)
4
-
5
- [ step, { name: "Nested(#{callable})" } ]
6
- end
7
-
8
- # WARNING: this is experimental API, but it will end up with something like that.
9
- module Element
10
- # DISCUSS: add builders here.
11
- def initialize(wrapped=nil)
12
- @wrapped = wrapped
13
- end
14
-
15
- module Dynamic
16
- def initialize(wrapped)
17
- @wrapped = Option::KW.(wrapped)
18
- end
19
- end
20
- end
21
-
22
- module Nested
23
- # Please note that the instance_variable_get are here on purpose since the
24
- # superinternal API is not entirely decided, yet.
25
- # @api private
26
- def self.for(step, input, output, is_nestable_object=method(:nestable_object?)) # DISCUSS: use builders here?
27
- invoker = Caller::Dynamic.new(step)
28
- invoker = Caller.new(step) if is_nestable_object.(step)
29
-
30
- options_for_nested = Options.new
31
- options_for_nested = Options::Dynamic.new(input) if input
32
-
33
- options_for_composer = Options::Output.new
34
- options_for_composer = Options::Output::Dynamic.new(output) if output
35
-
36
- # This lambda is the strut added on the track, executed at runtime.
37
- ->(operation, options) do
38
- result = invoker.(operation, options, options_for_nested.(operation, options)) # TODO: what about containers?
39
-
40
- options_for_composer.(operation, options, result).each { |k,v| options[k] = v }
41
-
42
- result.success? # DISCUSS: what if we could simply return the result object here?
43
- end
44
- end
45
-
46
- def self.nestable_object?(object)
47
- # interestingly, with < we get a weird nil exception. bug in Ruby?
48
- object.is_a?(Class) && object <= Trailblazer::Operation
49
- end
50
-
51
- # Is executed at runtime and calls the nested operation.
52
- class Caller
53
- include Element
54
-
55
- def call(input, options, options_for_nested)
56
- call_nested(nested(input, options), options_for_nested)
57
- end
58
-
59
- private
60
- def call_nested(operation, options)
61
- operation._call(options)
62
- end
63
-
64
- def nested(*); @wrapped end
65
-
66
- class Dynamic < Caller
67
- include Element::Dynamic
68
-
69
- def nested(input, options)
70
- @wrapped.(input, options)
71
- end
72
- end
73
- end
74
-
75
- class Options
76
- include Element
77
-
78
- # Per default, only runtime data for nested operation.
79
- def call(input, options)
80
- options.to_runtime_data[0]
81
- end
82
-
83
- class Dynamic
84
- include Element::Dynamic
85
-
86
- def call(operation, options)
87
- @wrapped.(operation, options, runtime_data: options.to_runtime_data[0], mutable_data: options.to_mutable_data )
88
- end
89
- end
90
-
91
- class Output
92
- include Element
93
-
94
- def call(input, options, result)
95
- mutable_data_for(result).each { |k,v| options[k] = v }
96
- end
97
-
98
- def mutable_data_for(result)
99
- result.instance_variable_get(:@data).to_mutable_data
100
- end
101
-
102
- class Dynamic < Output
103
- include Element::Dynamic
104
-
105
- def call(input, options, result)
106
- @wrapped.(input, options, mutable_data: mutable_data_for(result))
107
- end
108
- end
109
- end
110
- end
111
- end
112
- end
113
-
@@ -1,10 +0,0 @@
1
- class Trailblazer::Operation
2
- module Contract
3
- def self.Persist(method: :save, name: "default")
4
- path = "contract.#{name}"
5
- step = ->(input, options) { options[path].send(method) }
6
-
7
- [ step, name: "persist.save" ]
8
- end
9
- end
10
- end
@@ -1,35 +0,0 @@
1
- class Trailblazer::Operation
2
- module Policy
3
- # Step: This generically `call`s a policy and then pushes its result to `options`.
4
- # You can use any callable object as a policy with this step.
5
- class Eval
6
- def initialize(name:nil, path:nil)
7
- @name = name
8
- @path = path
9
- end
10
-
11
- def call(input, options)
12
- condition = options[@path] # this allows dependency injection.
13
- result = condition.(input, options)
14
-
15
- options["policy.#{@name}"] = result["policy"] # assign the policy as a skill.
16
- options["result.policy.#{@name}"] = result
17
-
18
- # flow control
19
- result.success? # since we & this, it's only executed OnRight and the return boolean decides the direction, input is passed straight through.
20
- end
21
- end
22
-
23
- # Adds the `yield` result to the pipe and treats it like a
24
- # policy-compatible object at runtime.
25
- def self.step(condition, options, &block)
26
- name = options[:name]
27
- path = "policy.#{name}.eval"
28
-
29
- step = Eval.new( name: name, path: path )
30
- step = Pipetree::Step.new(step, path => condition)
31
-
32
- [ step, name: path ]
33
- end
34
- end
35
- end