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,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,3 @@
1
+ Trailblazer::V1_1::Operation.class_eval do
2
+ autoload :Responder, "trailblazer/1.1/operation/responder"
3
+ end
@@ -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,6 @@
1
+ require "minitest/rails/capybara" # loads Capybara, etc.
2
+
3
+ module Trailblazer::V1_1::Test
4
+ class Integration < Capybara::Rails::TestCase
5
+ end
6
+ 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,5 @@
1
+ module Trailblazer
2
+ module Compat
3
+ VERSION = "0.1.0"
4
+ end
5
+ 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: []