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,77 @@
|
|
1
|
+
require 'trailblazer/operation'
|
2
|
+
require 'action_dispatch/http/upload'
|
3
|
+
require 'tempfile'
|
4
|
+
|
5
|
+
module Trailblazer
|
6
|
+
# TODO: document:
|
7
|
+
# to_hash
|
8
|
+
# from_hash
|
9
|
+
# initialize/tmp_dir
|
10
|
+
class Operation::UploadedFile
|
11
|
+
def initialize(uploaded, options={})
|
12
|
+
@uploaded = uploaded
|
13
|
+
@options = options
|
14
|
+
@tmp_dir = options[:tmp_dir]
|
15
|
+
end
|
16
|
+
|
17
|
+
def to_hash
|
18
|
+
path = persist!
|
19
|
+
|
20
|
+
hash = {
|
21
|
+
filename: @uploaded.original_filename,
|
22
|
+
type: @uploaded.content_type,
|
23
|
+
tempfile_path: path
|
24
|
+
}
|
25
|
+
|
26
|
+
cleanup!
|
27
|
+
|
28
|
+
hash
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns a ActionDispatch::Http::UploadedFile as if the upload was in the same request.
|
32
|
+
def self.from_hash(hash)
|
33
|
+
suffix = File.extname(hash[:filename])
|
34
|
+
|
35
|
+
# we need to create a Tempfile to make Http::UploadedFile work.
|
36
|
+
tmp = Tempfile.new(["bla", suffix]) # always force file suffix to avoid problems with imagemagick etc.
|
37
|
+
file = File.open(hash[:tempfile_path])# doesn't close automatically :( # fixme: introduce strategy (Tempfile:=>slow, File:=> hopefully less memory footprint)
|
38
|
+
tmp.write(file.read) # DISCUSS: We need Tempfile.new(<File>) to avoid this slow and memory-consuming mechanics.
|
39
|
+
|
40
|
+
file.close # TODO: can we test that?
|
41
|
+
File.unlink(file)
|
42
|
+
|
43
|
+
ActionDispatch::Http::UploadedFile.new(hash.merge(tempfile: tmp))
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
attr_reader :tmp_dir
|
48
|
+
|
49
|
+
# convert Tempfile from Rails upload into persistent "temp" file so it is available in workers.
|
50
|
+
def persist!
|
51
|
+
path = @uploaded.path # original Tempfile path (from Rails).
|
52
|
+
path = path_with_tmp_dir(path)
|
53
|
+
|
54
|
+
path = path + "_trailblazer_upload"
|
55
|
+
|
56
|
+
FileUtils.mv(@uploaded.path, path) # move Rails upload file into persistent `path`.
|
57
|
+
path
|
58
|
+
end
|
59
|
+
|
60
|
+
def path_with_tmp_dir(path)
|
61
|
+
return path unless tmp_dir # if tmp_dir set, create path in it.
|
62
|
+
|
63
|
+
@with_tmp_dir = Tempfile.new(File.basename(path), tmp_dir)
|
64
|
+
@with_tmp_dir.path # use Tempfile to create nested dirs (os-dependent.)
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete!(file)
|
68
|
+
file.close
|
69
|
+
file.unlink # the Rails uploaded file is already unlinked since moved.
|
70
|
+
end
|
71
|
+
|
72
|
+
def cleanup!
|
73
|
+
delete!(@uploaded.tempfile) if @uploaded.respond_to?(:tempfile) # this is Rails' uploaded file, not sure if we need to do that. in 3.2, we don't have UploadedFile#close, yet.
|
74
|
+
delete!(@with_tmp_dir) if @with_tmp_dir # we used that file to create a tmp file path below tmp_dir.
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'sidekiq/worker'
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
|
4
|
+
#setup in initialize: when Op.run() with Worker, the policy will be run "delayed" and not with the actual permission set. this will result in many crashing sidekiq workers.
|
5
|
+
|
6
|
+
class Trailblazer::Operation
|
7
|
+
# only kicks in when Operation::run, #run will still do it real-time
|
8
|
+
# Works with Reform 2, only.
|
9
|
+
module Worker
|
10
|
+
def self.included(base)
|
11
|
+
base.send(:include, Sidekiq::Worker)
|
12
|
+
base.extend(ClassMethods)
|
13
|
+
end
|
14
|
+
|
15
|
+
module ClassMethods
|
16
|
+
def run(params)
|
17
|
+
if background?
|
18
|
+
return perform_async(serializable(params))
|
19
|
+
end
|
20
|
+
|
21
|
+
super(params)
|
22
|
+
end
|
23
|
+
|
24
|
+
def new(*args)
|
25
|
+
return super if args.any?
|
26
|
+
# sidekiq behavior: (not a big fan of this)
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def perform(params) # called by Sidekiq.
|
31
|
+
build_operation(params).perform
|
32
|
+
end
|
33
|
+
|
34
|
+
def jid=(jid)
|
35
|
+
puts "@@@@@ #{jid.inspect}"
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
def perform_async(*args)
|
40
|
+
client_push('class' => self, 'args' => args) # calls class.new.perform(params)
|
41
|
+
end
|
42
|
+
|
43
|
+
def background? # TODO: make configurable.
|
44
|
+
true
|
45
|
+
# if Rails.env == "production" or Rails.env == "staging"
|
46
|
+
end
|
47
|
+
|
48
|
+
def serializable(params)
|
49
|
+
params # this is where we convert file uloads into Trailblazer::UploadedFile, etc. soon.
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
|
54
|
+
def perform#(params)
|
55
|
+
# the serialized params hash from Sidekiq contains a Op::UploadedFile hash.
|
56
|
+
|
57
|
+
# the following code is basically what happens in a controller.
|
58
|
+
# this is a bug in Rails, it doesn't work without requiring as/hash/ina
|
59
|
+
# params = ActiveSupport::HashWithIndifferentAccess.new_from_hash_copying_default(params) # TODO: this might make it ultra-slow as Reform converts it back to strings.
|
60
|
+
params = @params.with_indifferent_access
|
61
|
+
@params = deserializable(params)
|
62
|
+
run
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
def deserializable(params)
|
67
|
+
params # this is where we convert file uloads into Trailblazer::UploadedFile, etc. soon.
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
# Overrides ::serializable and #deserializable and handles file properties from the Contract schema.
|
72
|
+
module FileMarshaller
|
73
|
+
# NOTE: this is WIP and the implementation will be more understandable and performant soon.
|
74
|
+
def self.included(base)
|
75
|
+
base.extend ClassMethods
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
private
|
80
|
+
module ClassMethods
|
81
|
+
def file_marshaller_representer
|
82
|
+
@file_marshaller_representer ||= contract_class.schema(include: [Representable::Hash]).apply do |dfn|
|
83
|
+
dfn.merge!(
|
84
|
+
getter: lambda { |*| self[dfn.name.to_sym] },
|
85
|
+
setter: lambda { |fragment, *| self[dfn.name.to_s] = fragment }
|
86
|
+
) # FIXME: allow both sym and str.
|
87
|
+
|
88
|
+
dfn.merge!(class: Hash) and next if dfn[:form] or dfn[:twin] # nested properties need a class for deserialization.
|
89
|
+
next unless dfn[:file]
|
90
|
+
|
91
|
+
# TODO: where do we set /tmp/uploads?
|
92
|
+
dfn.merge!(
|
93
|
+
serialize: lambda { |file, *| Trailblazer::Operation::UploadedFile.new(file, tmp_dir: "/tmp/uploads").to_hash },
|
94
|
+
deserialize: lambda { |object, hash, *| Trailblazer::Operation::UploadedFile.from_hash(hash) },
|
95
|
+
class: Hash
|
96
|
+
)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def serializable(params)
|
101
|
+
file_marshaller_representer.new(params).to_hash
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# todo: do with_indifferent_access in #deserialize and call super here.
|
106
|
+
def deserializable(hash)
|
107
|
+
# self.class.file_marshaller_representer.new({}).extend(Representable::Debug).from_hash(hash)
|
108
|
+
self.class.file_marshaller_representer.new({}.with_indifferent_access).from_hash(hash)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "reform/rails"
|
2
|
+
require "trailblazer/1.1/rails/railtie"
|
3
|
+
|
4
|
+
require "trailblazer/1.1/operation"
|
5
|
+
# TODO: remove that once i18n, validations etc in Reform/AM are sorted.
|
6
|
+
Trailblazer::V1_1::Operation.contract_class.class_eval do
|
7
|
+
def self.name
|
8
|
+
# for whatever reason, validations climb up the inheritance tree and require _every_ class to have a name (4.1).
|
9
|
+
"Reform::Form"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
# Automatically set model_name on operation's contract when `Op::Model` is included.
|
14
|
+
require "trailblazer/1.1/operation/model"
|
15
|
+
require "trailblazer/1.1/operation/model/active_model"
|
16
|
+
Trailblazer::V1_1::Operation::Model::DSL.module_eval do
|
17
|
+
include Trailblazer::V1_1::Operation::Model::ActiveModel # ::contract.
|
18
|
+
end
|
19
|
+
|
20
|
+
require "trailblazer/1.1/autoloading"
|
21
|
+
require "trailblazer/1.1/rails/autoloading"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require "rails/railtie"
|
2
|
+
|
3
|
+
module Trailblazer::V1_1
|
4
|
+
class Railtie < ::Rails::Railtie
|
5
|
+
# This is to autoload Operation::Dispatch, etc. I'm simply assuming people find this helpful in Rails.
|
6
|
+
initializer "trailblazer.library_autoloading" do
|
7
|
+
require "trailblazer/1.1/autoloading"
|
8
|
+
end
|
9
|
+
|
10
|
+
initializer "trailblazer.application_controller" do
|
11
|
+
ActiveSupport.on_load(:action_controller) do
|
12
|
+
require "trailblazer/rails/railtie"
|
13
|
+
# this requires trailblazer-rails-1.0.0
|
14
|
+
V2_Controller = Trailblazer::Rails::Controller
|
15
|
+
|
16
|
+
ActionController::Base.class_eval do
|
17
|
+
include V2_Controller # if Trailblazer::Operation.const_defined?(:Controller) # from V2.
|
18
|
+
alias run_v2 run
|
19
|
+
include Trailblazer::V1_1::Operation::Controller
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require "trailblazer/compat/version"
|
2
|
+
|
3
|
+
module Trailblazer
|
4
|
+
module V1_1
|
5
|
+
# Your code goes here...
|
6
|
+
end
|
7
|
+
|
8
|
+
module V2
|
9
|
+
end
|
10
|
+
|
11
|
+
module Compat
|
12
|
+
module Version
|
13
|
+
def version(v)
|
14
|
+
V2::Operation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
require "trailblazer/operation" # 2.x
|
22
|
+
require "trailblazer/operation/model" # 2.x
|
23
|
+
require "trailblazer/operation/policy" # 2.x
|
24
|
+
require "trailblazer/operation/pundit" # 2.x
|
25
|
+
require "trailblazer/operation/guard" # 2.x
|
26
|
+
require "trailblazer/operation/contract" # 2.x
|
27
|
+
require "trailblazer/operation/validate" # 2.x
|
28
|
+
require "trailblazer/operation/persist" # 2.x
|
29
|
+
require "trailblazer/operation/rescue" # 2.x
|
30
|
+
require "trailblazer/operation/wrap" # 2.x
|
31
|
+
require "trailblazer/operation/nested" # 2.x
|
32
|
+
|
33
|
+
Trailblazer::V2::Operation = ::Trailblazer::Operation # copy TRB2 Operation constant to a safe place.
|
34
|
+
|
35
|
+
require "trailblazer/1.1/rails"
|
36
|
+
|
37
|
+
Trailblazer.send(:remove_const, :Operation)
|
38
|
+
Trailblazer.send(:const_set, :Operation, Trailblazer::V1_1::Operation) # TRB::Op is now TRB 1.1
|
39
|
+
|
40
|
+
# Trailblazer::Operation = Trailblazer::V1_1::Operation
|
41
|
+
Trailblazer::NotAuthorizedError = Trailblazer::V1_1::NotAuthorizedError
|
42
|
+
|
43
|
+
Trailblazer::Operation.extend(Trailblazer::Compat::Version)
|
44
|
+
|
45
|
+
Trailblazer::V2::Operation::Nested.module_eval do
|
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::V2::Operation
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
lib = File.expand_path('../lib', __FILE__)
|
2
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
+
require 'trailblazer/compat/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = "trailblazer-compat"
|
7
|
+
spec.version = Trailblazer::Compat::VERSION
|
8
|
+
spec.authors = ["Nick Sutterer"]
|
9
|
+
spec.email = ["apotonick@gmail.com"]
|
10
|
+
|
11
|
+
spec.summary = %q{Use Trailblazer 1.1 and 2.x.}
|
12
|
+
spec.description = %q{Use Trailblazer 1.1 and 2.x in one application.}
|
13
|
+
spec.homepage = "http://trailblazer.to"
|
14
|
+
|
15
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
16
|
+
spec.bindir = "exe"
|
17
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
18
|
+
spec.require_paths = ["lib"]
|
19
|
+
|
20
|
+
# spec.add_dependency "trailblazer", ">= 1.2.0", "< 1.3.0"
|
21
|
+
|
22
|
+
spec.add_development_dependency "bundler", "~> 1.12"
|
23
|
+
spec.add_development_dependency "rake", "~> 10.0"
|
24
|
+
spec.add_development_dependency "minitest", "~> 5.0"
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: trailblazer-compat
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Nick Sutterer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-04-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.12'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.12'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '10.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '10.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
description: Use Trailblazer 1.1 and 2.x in one application.
|
56
|
+
email:
|
57
|
+
- apotonick@gmail.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".gitignore"
|
63
|
+
- ".travis.yml"
|
64
|
+
- Gemfile
|
65
|
+
- README.md
|
66
|
+
- Rakefile
|
67
|
+
- lib/trailblazer/1.1/autoloading.rb
|
68
|
+
- lib/trailblazer/1.1/endpoint.rb
|
69
|
+
- lib/trailblazer/1.1/operation.rb
|
70
|
+
- lib/trailblazer/1.1/operation/builder.rb
|
71
|
+
- lib/trailblazer/1.1/operation/callback.rb
|
72
|
+
- lib/trailblazer/1.1/operation/collection.rb
|
73
|
+
- lib/trailblazer/1.1/operation/controller.rb
|
74
|
+
- lib/trailblazer/1.1/operation/controller/active_record.rb
|
75
|
+
- lib/trailblazer/1.1/operation/dispatch.rb
|
76
|
+
- lib/trailblazer/1.1/operation/model.rb
|
77
|
+
- lib/trailblazer/1.1/operation/model/active_model.rb
|
78
|
+
- lib/trailblazer/1.1/operation/model/dsl.rb
|
79
|
+
- lib/trailblazer/1.1/operation/model/external.rb
|
80
|
+
- lib/trailblazer/1.1/operation/module.rb
|
81
|
+
- lib/trailblazer/1.1/operation/policy.rb
|
82
|
+
- lib/trailblazer/1.1/operation/policy/guard.rb
|
83
|
+
- lib/trailblazer/1.1/operation/representer.rb
|
84
|
+
- lib/trailblazer/1.1/operation/resolver.rb
|
85
|
+
- lib/trailblazer/1.1/operation/responder.rb
|
86
|
+
- lib/trailblazer/1.1/operation/uploaded_file.rb
|
87
|
+
- lib/trailblazer/1.1/operation/worker.rb
|
88
|
+
- lib/trailblazer/1.1/rails.rb
|
89
|
+
- lib/trailblazer/1.1/rails/autoloading.rb
|
90
|
+
- lib/trailblazer/1.1/rails/railtie.rb
|
91
|
+
- lib/trailblazer/1.1/rails/test/integration.rb
|
92
|
+
- lib/trailblazer/compat.rb
|
93
|
+
- lib/trailblazer/compat/version.rb
|
94
|
+
- trailblazer-compat.gemspec
|
95
|
+
homepage: http://trailblazer.to
|
96
|
+
licenses: []
|
97
|
+
metadata: {}
|
98
|
+
post_install_message:
|
99
|
+
rdoc_options: []
|
100
|
+
require_paths:
|
101
|
+
- lib
|
102
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
103
|
+
requirements:
|
104
|
+
- - ">="
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '0'
|
107
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 2.5.2
|
115
|
+
signing_key:
|
116
|
+
specification_version: 4
|
117
|
+
summary: Use Trailblazer 1.1 and 2.x.
|
118
|
+
test_files: []
|