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.
- checksums.yaml +7 -0
- data/.gitignore +9 -0
- data/.travis.yml +5 -0
- data/Gemfile +7 -0
- data/README.md +76 -0
- data/Rakefile +10 -0
- data/lib/trailblazer/1.1/autoloading.rb +15 -0
- data/lib/trailblazer/1.1/endpoint.rb +31 -0
- data/lib/trailblazer/1.1/operation.rb +175 -0
- data/lib/trailblazer/1.1/operation/builder.rb +26 -0
- data/lib/trailblazer/1.1/operation/callback.rb +53 -0
- data/lib/trailblazer/1.1/operation/collection.rb +6 -0
- data/lib/trailblazer/1.1/operation/controller.rb +87 -0
- data/lib/trailblazer/1.1/operation/controller/active_record.rb +21 -0
- data/lib/trailblazer/1.1/operation/dispatch.rb +3 -0
- data/lib/trailblazer/1.1/operation/model.rb +50 -0
- data/lib/trailblazer/1.1/operation/model/active_model.rb +11 -0
- data/lib/trailblazer/1.1/operation/model/dsl.rb +29 -0
- data/lib/trailblazer/1.1/operation/model/external.rb +34 -0
- data/lib/trailblazer/1.1/operation/module.rb +29 -0
- data/lib/trailblazer/1.1/operation/policy.rb +85 -0
- data/lib/trailblazer/1.1/operation/policy/guard.rb +35 -0
- data/lib/trailblazer/1.1/operation/representer.rb +98 -0
- data/lib/trailblazer/1.1/operation/resolver.rb +30 -0
- data/lib/trailblazer/1.1/operation/responder.rb +18 -0
- data/lib/trailblazer/1.1/operation/uploaded_file.rb +77 -0
- data/lib/trailblazer/1.1/operation/worker.rb +112 -0
- data/lib/trailblazer/1.1/rails.rb +21 -0
- data/lib/trailblazer/1.1/rails/autoloading.rb +3 -0
- data/lib/trailblazer/1.1/rails/railtie.rb +24 -0
- data/lib/trailblazer/1.1/rails/test/integration.rb +6 -0
- data/lib/trailblazer/compat.rb +50 -0
- data/lib/trailblazer/compat/version.rb +5 -0
- data/trailblazer-compat.gemspec +25 -0
- 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,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
|