trailblazer-compat 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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