trailblazer-compat 0.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 (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +7 -0
  5. data/README.md +76 -0
  6. data/Rakefile +10 -0
  7. data/lib/trailblazer/1.1/autoloading.rb +15 -0
  8. data/lib/trailblazer/1.1/endpoint.rb +31 -0
  9. data/lib/trailblazer/1.1/operation.rb +175 -0
  10. data/lib/trailblazer/1.1/operation/builder.rb +26 -0
  11. data/lib/trailblazer/1.1/operation/callback.rb +53 -0
  12. data/lib/trailblazer/1.1/operation/collection.rb +6 -0
  13. data/lib/trailblazer/1.1/operation/controller.rb +87 -0
  14. data/lib/trailblazer/1.1/operation/controller/active_record.rb +21 -0
  15. data/lib/trailblazer/1.1/operation/dispatch.rb +3 -0
  16. data/lib/trailblazer/1.1/operation/model.rb +50 -0
  17. data/lib/trailblazer/1.1/operation/model/active_model.rb +11 -0
  18. data/lib/trailblazer/1.1/operation/model/dsl.rb +29 -0
  19. data/lib/trailblazer/1.1/operation/model/external.rb +34 -0
  20. data/lib/trailblazer/1.1/operation/module.rb +29 -0
  21. data/lib/trailblazer/1.1/operation/policy.rb +85 -0
  22. data/lib/trailblazer/1.1/operation/policy/guard.rb +35 -0
  23. data/lib/trailblazer/1.1/operation/representer.rb +98 -0
  24. data/lib/trailblazer/1.1/operation/resolver.rb +30 -0
  25. data/lib/trailblazer/1.1/operation/responder.rb +18 -0
  26. data/lib/trailblazer/1.1/operation/uploaded_file.rb +77 -0
  27. data/lib/trailblazer/1.1/operation/worker.rb +112 -0
  28. data/lib/trailblazer/1.1/rails.rb +21 -0
  29. data/lib/trailblazer/1.1/rails/autoloading.rb +3 -0
  30. data/lib/trailblazer/1.1/rails/railtie.rb +24 -0
  31. data/lib/trailblazer/1.1/rails/test/integration.rb +6 -0
  32. data/lib/trailblazer/compat.rb +50 -0
  33. data/lib/trailblazer/compat/version.rb +5 -0
  34. data/trailblazer-compat.gemspec +25 -0
  35. metadata +118 -0
@@ -0,0 +1,21 @@
1
+ # Assigns an additional instance variable for +@model+ named after the model's table name (e.g. @comment).
2
+ #
3
+ # THIS MODULE IS DEPRECATED!
4
+ #
5
+ # Please don't use this module. Instead, use @model in your controller or pass the
6
+ # operation instance to a cell to present it.
7
+ module Trailblazer::V1_1::Operation::Controller::ActiveRecord
8
+ private
9
+ def setup_operation_instance_variables!(operation, options)
10
+ super
11
+ instance_variable_set(:"@#{operation_model_name}", @model)
12
+ end
13
+
14
+ def operation_model_name
15
+ # set the right variable name if collection
16
+ if @operation.is_a?(Trailblazer::Operation::Collection)
17
+ return @model.model.table_name.split(".").last
18
+ end
19
+ @model.class.table_name.split(".").last.singularize
20
+ end
21
+ end
@@ -0,0 +1,3 @@
1
+ require "trailblazer/1.1/operation/callback"
2
+ # TODO: deprecate this module.
3
+ Trailblazer::V1_1::Operation::Dispatch = Trailblazer::V1_1::Operation::Callback
@@ -0,0 +1,50 @@
1
+ require "trailblazer/1.1/operation/model/dsl"
2
+
3
+ module Trailblazer::V1_1
4
+ class Operation
5
+ # The Model module will automatically create/find models for the configured +action+.
6
+ # It adds a public +Operation#model+ reader to access the model (after performing).
7
+ module Model
8
+ def self.included(base)
9
+ base.extend DSL
10
+ end
11
+
12
+ # Methods to create the model according to class configuration and params.
13
+ module BuildModel
14
+ def model!(params)
15
+ instantiate_model(params)
16
+ end
17
+
18
+ def instantiate_model(params)
19
+ send("#{action_name}_model", params)
20
+ end
21
+
22
+ def create_model(params)
23
+ model_class.new
24
+ end
25
+
26
+ def update_model(params)
27
+ model_class.find(params[:id])
28
+ end
29
+
30
+ alias_method :find_model, :update_model
31
+ end
32
+
33
+
34
+ # #validate no longer accepts a model since this module instantiates it for you.
35
+ def validate(params, model=self.model, *args)
36
+ super(params, model, *args)
37
+ end
38
+
39
+ private
40
+ include BuildModel
41
+
42
+ def model_class
43
+ self.class.model_class
44
+ end
45
+ def action_name
46
+ self.class.action_name
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ module Trailblazer::V1_1
2
+ module Operation::Model
3
+ # Automatically set model_name on operation's contract.
4
+ module ActiveModel
5
+ def contract(*, &block)
6
+ super
7
+ contract_class.model(model_class) # this assumes that Form::ActiveModel is mixed in.
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,29 @@
1
+ class Trailblazer::V1_1::Operation
2
+ module Model
3
+ # Imports ::model and ::action into an operation.
4
+ module DSL
5
+ def self.extended(extender)
6
+ extender.extend Uber::InheritableAttr
7
+ extender.inheritable_attr :config
8
+ extender.config = {}
9
+ end
10
+
11
+ def model(name, action=nil)
12
+ self.config[:model] = name
13
+ action(action) if action # coolest line ever.
14
+ end
15
+
16
+ def action(name)
17
+ self.config[:action] = name
18
+ end
19
+
20
+ def action_name # considered private.
21
+ self.config[:action] or :create
22
+ end
23
+
24
+ def model_class # considered private.
25
+ self.config[:model] or raise "[Trailblazer] You didn't call Operation::model."
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require "trailblazer/1.1/operation/model"
2
+
3
+ class Trailblazer::V1_1::Operation
4
+ module Model
5
+ # Builds (finds or creates) the model _before_ the operation is instantiated.
6
+ # Passes the model instance into the builder with the following signature.
7
+ #
8
+ # builds ->(model, params)
9
+ #
10
+ # The initializer will now expect you to pass the model in via options[:model]. This
11
+ # happens automatically when coming from a builder.
12
+ module External
13
+ def self.included(includer)
14
+ includer.extend Model::DSL
15
+ includer.extend Model::BuildModel
16
+ includer.extend ClassMethods
17
+ end
18
+
19
+ def assign_model!(*) # i don't like to "disable" the `@model =` like this but it's the simplest for now.
20
+ @model = @options[:model]
21
+ end
22
+
23
+
24
+ module ClassMethods
25
+ private
26
+ def build_operation(params, options={}) # TODO: merge with Resolver::build_operation.
27
+ model = model!(params)
28
+ build_operation_class(model, params). # calls builds->(model, params).
29
+ new(params, options.merge(model: model))
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,29 @@
1
+ module Trailblazer::V1_1::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
@@ -0,0 +1,85 @@
1
+ require "trailblazer/1.1/operation/policy/guard"
2
+
3
+ module Trailblazer::V1_1
4
+ class NotAuthorizedError < RuntimeError
5
+ end
6
+
7
+ # Adds #evaluate_policy to #setup!, and ::policy.
8
+ module Operation::Policy
9
+ def self.included(includer)
10
+ includer.extend DSL
11
+ end
12
+
13
+ module DSL
14
+ def self.extended(extender)
15
+ extender.inheritable_attr :policy_config
16
+ extender.policy_config = lambda { |*| true } # return true per default.
17
+ end
18
+
19
+ def policy(*args, &block)
20
+ self.policy_config = permission_class.new(*args, &block)
21
+ end
22
+
23
+ def permission_class
24
+ Permission
25
+ end
26
+ end
27
+
28
+ attr_reader :policy
29
+
30
+ private
31
+ module Setup
32
+ def setup!(params)
33
+ evaluate_policy(super)
34
+ end
35
+ end
36
+ include Setup
37
+
38
+ def evaluate_policy(params)
39
+ user = params[:current_user]
40
+
41
+ @policy = self.class.policy_config.(user, model, @policy) do |policy, action|
42
+ raise policy_exception(policy, action, model)
43
+ end
44
+ end
45
+
46
+ def policy_exception(policy, action, model)
47
+ NotAuthorizedError.new(query: action, record: model, policy: policy)
48
+ end
49
+
50
+ # Encapsulate building the Policy object and calling the defined query action.
51
+ # This assumes the policy class is "pundit-style", as in Policy.new(user, model).edit?.
52
+ class Permission
53
+ def initialize(policy_class, action)
54
+ @policy_class, @action = policy_class, action
55
+ end
56
+
57
+ # Without a block, return the policy object (which is usually a Pundit-style class).
58
+ # When block is passed evaluate the default rule and run block when false.
59
+ def call(user, model, external_policy=nil)
60
+ policy = build_policy(user, model, external_policy)
61
+
62
+ policy.send(@action) || yield(policy, @action) if block_given?
63
+ policy
64
+ end
65
+
66
+ private
67
+ def build_policy(user, model, policy)
68
+ policy or @policy_class.new(user, model)
69
+ end
70
+ end
71
+ end
72
+
73
+
74
+ module Operation::Deny
75
+ def self.included(includer)
76
+ includer.extend ClassMethods
77
+ end
78
+
79
+ module ClassMethods
80
+ def deny!
81
+ raise NotAuthorizedError
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,35 @@
1
+ module Trailblazer::V1_1
2
+ # Policy::Guard is a very simple policy implementation.
3
+ # It adds #evaluate_policy to Operation#setup! and calls whatever
4
+ # you provided to ::policy.
5
+ #
6
+ # http://trailblazer.to/gems/operation/policy.html#guard
7
+ module Operation::Policy
8
+ module Guard
9
+ def self.included(includer)
10
+ includer.extend(DSL) # Provides ::policy(CallableObject)
11
+ includer.extend(ClassMethods)
12
+ includer.send(:include, Setup)
13
+ end
14
+
15
+ module ClassMethods
16
+ def policy(callable=nil, &block)
17
+ self.policy_config = Uber::Options::Value.new(callable || block)
18
+ end
19
+ end
20
+
21
+ def evaluate_policy(params)
22
+ call_policy(params) or raise policy_exception
23
+ end
24
+
25
+ # Override if you want your own policy invocation, e.g. with more args.
26
+ def call_policy(params)
27
+ self.class.policy_config.(self, params)
28
+ end
29
+
30
+ def policy_exception
31
+ NotAuthorizedError.new
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,98 @@
1
+ # Including this will change the way deserialization in #validate works.
2
+ #
3
+ # Instead of treating params as a hash and letting the form object deserialize it,
4
+ # a representer will be infered from the contract. This representer is then passed as
5
+ # deserializer into Form#validate.
6
+ #
7
+ # TODO: so far, we only support JSON, but it's two lines to change to support any kind of format.
8
+ module Trailblazer::V1_1::Operation::Representer
9
+ def self.included(base)
10
+ base.extend DSL
11
+ end
12
+
13
+ module DSL
14
+ def self.extended(extender)
15
+ extender.inheritable_attr :_representer_class
16
+ end
17
+
18
+ def representer(constant=nil, &block)
19
+ return representer_class unless constant or block_given?
20
+
21
+ self.representer_class= Class.new(constant) if constant
22
+ representer_class.class_eval(&block) if block_given?
23
+ end
24
+
25
+ def representer_class
26
+ self._representer_class ||= infer_representer_class
27
+ end
28
+
29
+ def representer_class=(constant)
30
+ self._representer_class = constant
31
+ end
32
+
33
+ require "disposable/version"
34
+ def infer_representer_class
35
+ if Disposable::VERSION =~ /^0.1/
36
+ warn "[Trailblazer] Reform 2.0 won't be supported in Trailblazer 1.2. Don't be lazy and upgrade to Reform 2.1."
37
+
38
+ Disposable::Twin::Schema.from(contract_class,
39
+ include: [Representable::JSON],
40
+ options_from: :deserializer, # use :instance etc. in deserializer.
41
+ superclass: Representable::Decorator,
42
+ representer_from: lambda { |inline| inline.representer_class },
43
+ )
44
+ else
45
+ Disposable::Rescheme.from(contract_class,
46
+ include: [Representable::JSON],
47
+ options_from: :deserializer, # use :instance etc. in deserializer.
48
+ superclass: Representable::Decorator,
49
+ definitions_from: lambda { |inline| inline.definitions },
50
+ exclude_options: [:default, :populator], # TODO: test with populator: in an operation.
51
+ exclude_properties: [:persisted?]
52
+ )
53
+ end
54
+ end
55
+ end
56
+
57
+ private
58
+ module Rendering
59
+ # Override this if you need to pass options to the rendering.
60
+ #
61
+ # def to_json(*)
62
+ # super(include: @params[:include])
63
+ # end
64
+ def to_json(options={})
65
+ self.class.representer_class.new(represented).to_json(options)
66
+ end
67
+
68
+ # Override this if you want to render something else, e.g. the contract.
69
+ def represented
70
+ model
71
+ end
72
+ end
73
+ include Rendering
74
+
75
+
76
+ module Deserializer
77
+ module Hash
78
+ def validate_contract(params)
79
+ # use the inferred representer from the contract for deserialization in #validate.
80
+ contract.validate(params) do |document|
81
+ self.class.representer_class.new(contract).from_hash(document)
82
+ end
83
+ end
84
+ end
85
+
86
+ # This looks crazy, but all it does is using a Reform hook in #validate where we can use
87
+ # our own representer for deserialization. After the object graph is set up, Reform will
88
+ # run its validation without even knowing this came from JSON.
89
+ module JSON
90
+ def validate_contract(params)
91
+ contract.validate(params) do |document|
92
+ self.class.representer_class.new(contract).from_json(document)
93
+ end
94
+ end
95
+ end
96
+ end
97
+ include Deserializer::JSON
98
+ end
@@ -0,0 +1,30 @@
1
+ require "trailblazer/1.1/operation/model/external"
2
+ require "trailblazer/1.1/operation/policy"
3
+
4
+ class Trailblazer::V1_1::Operation
5
+ # Provides builds-> (model, policy, params).
6
+ module Resolver
7
+ def self.included(includer)
8
+ includer.class_eval do
9
+ include Policy # ::build_policy
10
+ include Model::External # ::build_operation_class
11
+
12
+ extend BuildOperation # ::build_operation
13
+ end
14
+ end
15
+
16
+ module BuildOperation
17
+ def build_operation(params, options={})
18
+ model = model!(params)
19
+ policy = policy_config.call(params[:current_user], model)
20
+ build_operation_class(model, policy, params).
21
+ new(params, options.merge(model: model, policy: policy))
22
+ end
23
+ end
24
+
25
+ def initialize(params, options)
26
+ @policy = options[:policy]
27
+ super
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,18 @@
1
+ module Trailblazer::V1_1::Operation::Responder
2
+ def self.included(base)
3
+ base.extend ClassMethods
4
+ end
5
+
6
+ module ClassMethods
7
+ def model_name
8
+ model_class.model_name
9
+ end
10
+ end
11
+
12
+ extend Forwardable
13
+ def_delegators :@model, :to_param, :destroyed?, :persisted?
14
+
15
+ def to_model
16
+ @model
17
+ end
18
+ end