trailblazer 1.0.0.rc2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,144 +0,0 @@
1
- require 'trailblazer/autoloading'
2
-
3
- class Song < ActiveRecord::Base
4
- class Create < Trailblazer::Operation
5
- include Model
6
- include Responder
7
- model Song, :create
8
-
9
-
10
- contract do
11
- property :title, validates: {presence: true}
12
- property :length
13
- end
14
-
15
- def process(params)
16
- validate(params[:song]) do
17
- contract.save
18
- end
19
- end
20
-
21
-
22
- class Json < Create
23
- def process(params)
24
- @model = Song.create(JSON.parse(params[:song]))
25
- end
26
- end
27
- end
28
-
29
-
30
- class Delete < Create
31
- action :find
32
-
33
- def process(params)
34
- model.destroy
35
- self
36
- end
37
- end
38
- end
39
-
40
- class Band < ActiveRecord::Base
41
- class Create < Trailblazer::Operation
42
- include Model, Responder#, Representer
43
- model Band, :create
44
-
45
- contract do
46
- include Reform::Form::ActiveModel
47
- model Band
48
-
49
- property :name, validates: {presence: true}
50
- property :locality, prepopulator: ->(*) { self.locality = "Sydney" }
51
-
52
- # class: Song #=> always create new song
53
- # instance: { Song.find(params[:id]) or Song.new } # same as find_or_create ?
54
- # this is what i want:
55
- # maybe make populate_if_empty a representable feature?
56
- collection :songs, populate_if_empty: Song do
57
- property :title
58
- end
59
- end
60
-
61
- def process(params)
62
- validate(params[:band]) do
63
- contract.save
64
- end
65
- end
66
-
67
- require "representable/json"
68
- class JSON < self
69
- include Representer
70
-
71
- representer do
72
- collection :songs, inherit: true, render_empty: false # tested in ControllerPresentTest.
73
- end
74
- end
75
-
76
- class Admin < self
77
- def process(params)
78
- res = super
79
- model.update_attribute :name, "#{model.name} [ADMIN]"
80
- res
81
- end
82
- end
83
-
84
- # TODO: wait for uber 0.0.10 and @dutow.
85
- # builds -> (params) do
86
- # return JSON if params[:format] == "json"
87
- # return Admin if params[:admin]
88
- # end
89
- builds do |params|
90
- if params[:format] == "json"
91
- JSON
92
- elsif params[:admin]
93
- Admin
94
- end
95
- end
96
- end
97
-
98
- class Update < Create
99
- action :update
100
-
101
- # TODO: infer stuff per default.
102
- class JSON < self
103
- include Representer
104
- self.contract_class = Create::JSON.contract_class
105
- self.representer_class = Create::JSON.representer_class
106
- end
107
-
108
- builds do |params|
109
- JSON if params[:format] == "json"
110
- end
111
- end
112
-
113
- class Index < Trailblazer::Operation
114
- include Collection
115
-
116
- def model!(params)
117
- Band.all
118
- end
119
-
120
- builds do |params|
121
- JSON if params[:format] == "json"
122
- end
123
-
124
- class JSON < self
125
- include Representer
126
-
127
- module BandRepresenter
128
- include Representable::JSON
129
- property :name
130
- property :locality
131
- end
132
-
133
- self.representer_class = BandRepresenter
134
- end
135
- end
136
- end
137
-
138
- class Tenant < ActiveRecord::Base
139
- class Show < Trailblazer::Operation
140
- include Model
141
- model Tenant, :update
142
- end
143
- end
144
-
@@ -1 +0,0 @@
1
- bands/index.html: <% @collection.each do |element| %><%= "#{element.name} " %><% end %>
@@ -1,2 +0,0 @@
1
- OTHER SONG
2
- <%= @form.errors.to_s %>
@@ -1 +0,0 @@
1
- <%= @form.errors.to_s %>
@@ -1,95 +0,0 @@
1
- require "test_helper"
2
-
3
- class ResponderRespondTest < ActionController::TestCase
4
- tests SongsController
5
-
6
- # HTML
7
- # #respond Create [valid]
8
- test "Create [html/valid]" do
9
- post :create, {song: {title: "You're Going Down"}}
10
- assert_redirected_to song_path(Song.last)
11
- end
12
-
13
- test "Create [html/valid/location]" do
14
- post :other_create, {song: {title: "You're Going Down"}}
15
- assert_redirected_to other_create_songs_path
16
- end
17
-
18
- test "Create [html/invalid]" do
19
- post :create, {song: {title: ""}}
20
- assert_response 200
21
- assert_equal @response.body, "{:title=&gt;[&quot;can&#39;t be blank&quot;]}"
22
- end
23
-
24
- test "Create [html/invalid/action]" do
25
- post :other_create, {song: {title: ""}}
26
- assert_response 200
27
- assert_equal @response.body, "OTHER SONG\n{:title=&gt;[&quot;can&#39;t be blank&quot;]}\n"
28
- assert_template "songs/another_view"
29
- end
30
-
31
- test "Delete [html/valid]" do
32
- song = Song::Create[song: {title: "You're Going Down"}].model
33
- delete :destroy, id: song.id
34
- assert_redirected_to songs_path
35
- # assert that model is deleted.
36
- end
37
-
38
- test "respond with block [html/valid]" do
39
- post :create_with_block, {song: {title: "You're Going Down"}}
40
- assert_response 200
41
- assert_equal "block run, valid: true", response.body
42
- end
43
-
44
- test "respond with block [html/invalid]" do
45
- post :create_with_block, {song: {title: ""}}
46
- assert_response 200
47
- assert_equal "block run, valid: false", response.body
48
- end
49
-
50
- # JSON
51
- test "Delete [json/valid]" do
52
- song = Song::Create[song: {title: "You're Going Down"}].model
53
- delete :destroy, id: song.id, format: :json
54
- assert_response 204 # no content.
55
- end
56
-
57
- # JS
58
- test "Delete [js/valid]" do
59
- song = Song::Create[song: {title: "You're Going Down"}].model
60
- assert_raises ActionView::MissingTemplate do
61
- # js wants to render destroy.js.erb
62
- delete :destroy, id: song.id, format: :js
63
- end
64
- end
65
-
66
- test "Delete with formats [js/valid]" do
67
- song = Song::Create[song: {title: "You're Going Down"}].model
68
-
69
- delete :destroy_with_formats, id: song.id, format: :js
70
- assert_response 200
71
- assert_equal "Song slayer!", response.body
72
- end
73
- end
74
-
75
- class ResponderRespondWithJSONTest < ActionController::TestCase
76
- tests BandsController
77
-
78
- # JSON
79
- test "Create [JSON/valid]" do
80
- post :create, {name: "SNFU"}.to_json, format: :json
81
- assert_response 201
82
- assert_equal "SNFU", Band.last.name
83
- end
84
- end
85
-
86
- # TODO: merge with above tests on SongsController.
87
- class ControllerRespondTest < ActionController::TestCase
88
- tests BandsController
89
-
90
- test "#respond with builds" do
91
- post :create, band: {name: "SNFU"}, admin: true
92
- assert_response 302
93
- assert_equal "SNFU [ADMIN]", Band.last.name
94
- end
95
- end
@@ -1,9 +0,0 @@
1
- require 'trailblazer'
2
- require 'minitest/autorun'
3
-
4
- require "reform/form/active_model/validations"
5
- Reform::Form.class_eval do
6
- include Reform::Form::ActiveModel::Validations
7
- end
8
-
9
- require 'fake_app/rails_app.rb'
@@ -1,75 +0,0 @@
1
- require 'test_helper'
2
- require 'trailblazer/operation/responder'
3
-
4
- class Song
5
- extend ActiveModel::Naming
6
-
7
- class Operation < Trailblazer::Operation
8
- include Model
9
- model Song
10
- include Responder
11
-
12
- def process(params)
13
- invalid! if params == false
14
- end
15
- end
16
- end
17
-
18
- module MyApp
19
- class Song
20
- extend ActiveModel::Naming
21
-
22
- class Operation < Trailblazer::Operation
23
- include Model
24
- include Responder
25
- model Song
26
-
27
- def process(params)
28
- invalid! if params == false
29
- end
30
- end
31
- end
32
- end
33
-
34
- class ResponderTestForModelWithoutNamespace < MiniTest::Spec
35
-
36
- # test ::model_name
37
- it { Song::Operation.model_name.name.must_equal "Song" }
38
- it { Song::Operation.model_name.singular.must_equal "song" }
39
- it { Song::Operation.model_name.plural.must_equal "songs" }
40
- it { Song::Operation.model_name.element.must_equal "song" }
41
- it { Song::Operation.model_name.human.must_equal "Song" }
42
- it { Song::Operation.model_name.collection.must_equal "songs" }
43
- it { Song::Operation.model_name.param_key.must_equal "song" }
44
- it { Song::Operation.model_name.i18n_key.must_equal :"song" }
45
- it { Song::Operation.model_name.route_key.must_equal "songs" }
46
- it { Song::Operation.model_name.singular_route_key.must_equal "song" }
47
-
48
- # #errors
49
- it { Song::Operation.(true).errors.must_equal [] }
50
- it { Song::Operation.(false).errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
51
-
52
- # TODO: integration test with Controller.
53
- end
54
-
55
-
56
- class ResponderTestForModelWitNamespace < MiniTest::Spec
57
-
58
- # test ::model_name
59
- it { MyApp::Song::Operation.model_name.name.must_equal "MyApp::Song" }
60
- it { MyApp::Song::Operation.model_name.singular.must_equal "my_app_song" }
61
- it { MyApp::Song::Operation.model_name.plural.must_equal "my_app_songs" }
62
- it { MyApp::Song::Operation.model_name.element.must_equal "song" }
63
- it { MyApp::Song::Operation.model_name.human.must_equal "Song" }
64
- it { MyApp::Song::Operation.model_name.collection.must_equal "my_app/songs" }
65
- it { MyApp::Song::Operation.model_name.param_key.must_equal "my_app_song" } # "song" for AR.
66
- it { MyApp::Song::Operation.model_name.i18n_key.must_equal :"my_app/song" }
67
- it { MyApp::Song::Operation.model_name.route_key.must_equal "my_app_songs" } # "songs" for AR.
68
- it { MyApp::Song::Operation.model_name.singular_route_key.must_equal "my_app_song" } # "song" for AR.
69
-
70
- # #errors
71
- it { MyApp::Song::Operation.(true).errors.must_equal [] }
72
- it { MyApp::Song::Operation.(false).errors.must_equal [1] } # TODO: since we don't want responder to render anything, just return _one_ error. :)
73
-
74
- # TODO: integration test with Controller.
75
- end
@@ -1,85 +0,0 @@
1
- require 'test_helper'
2
- require 'trailblazer/operation/uploaded_file'
3
-
4
- class TempfileTest < MiniTest::Spec
5
-
6
- end
7
-
8
- class UploadedFileTest < MiniTest::Spec
9
- let (:image) { File.open("test/fixtures/apotomo.png") }
10
- let (:tempfile) { tmp = Tempfile.new("bla")
11
- tmp.write image.read
12
- tmp
13
- }
14
-
15
- let (:upload) { ActionDispatch::Http::UploadedFile.new(
16
- tempfile: tempfile,
17
- filename: "apotomo.png",
18
- type: "image/png")
19
- }
20
-
21
- describe "#to_hash" do
22
- before {
23
- @uploaded_path = upload.tempfile.path
24
- @subject = Trailblazer::Operation::UploadedFile.new(upload).to_hash
25
- }
26
-
27
- it { @subject[:filename].must_equal "apotomo.png" }
28
- it { @subject[:type].must_equal "image/png" }
29
- it { @subject[:tempfile_path].must_match /\w+_trailblazer_upload$/ }
30
-
31
-
32
- # Rails upload file must be removed.
33
- it {
34
- File.exists?(@uploaded_path).must_equal false }
35
-
36
- it { File.exists?(@subject[:tempfile_path]).must_equal true }
37
- it { File.size(@subject[:tempfile_path]).must_equal image.size }
38
- end
39
-
40
- describe "::from_hash" do
41
- let (:data) { Trailblazer::Operation::UploadedFile.new(upload).to_hash }
42
- subject { Trailblazer::Operation::UploadedFile.from_hash(data) }
43
-
44
-
45
- it { subject.original_filename.must_equal "apotomo.png" }
46
- it { subject.content_type.must_equal "image/png" }
47
- it { subject.tempfile.must_be_kind_of File }
48
- it { subject.size.must_equal image.size }
49
-
50
- # params is not modified.
51
- it { params = data.clone and subject; data.must_equal params }
52
-
53
- # Tempfile must have proper extension for further processing (sidekiq/imagemagick, etc).
54
- it { subject.tempfile.path.must_match /\.png$/ }
55
-
56
- # Tempfile must be unlinked after process is finished.
57
- it do
58
- @subject = Trailblazer::Operation::UploadedFile.from_hash(data)
59
-
60
- processable_file = @subject.tempfile.path
61
- File.exists?(processable_file).must_equal true # this file must be GCed since it's a Tempfile, that's the whole point.
62
- # @subject = nil
63
- # GC.start
64
- # File.exists?(processable_file).must_equal false
65
- end
66
- end
67
-
68
-
69
- describe "with custom tmp directory" do
70
- describe "#to_hash" do
71
- before {
72
- @uploaded = Trailblazer::Operation::UploadedFile.new(upload, tmp_dir: tmp_dir)
73
- @subject = @uploaded.to_hash[:tempfile_path]
74
- }
75
-
76
- it { @subject.must_match /\w+_trailblazer_upload$/ }
77
- it { @subject.must_match /^\/tmp\/uploads\// }
78
-
79
- it { File.exists?(@subject).must_equal true }
80
- it { File.size(@subject).must_equal image.size }
81
-
82
- it { @uploaded.instance_variable_get(:@with_tmp_dir).path.must_equal nil }
83
- end
84
- end
85
- end
data/test/worker_test.rb DELETED
@@ -1,124 +0,0 @@
1
- # make sure #run always returns model
2
-
3
- # in test with sidekiq/testing
4
- # Operation.run #=> call perform_one and return [result, model] (model?)
5
-
6
- require 'test_helper'
7
- require 'trailblazer/operation'
8
- require 'trailblazer/operation/worker'
9
- require 'sidekiq/testing'
10
-
11
-
12
- class WorkerTest < MiniTest::Spec
13
- class Operation < Trailblazer::Operation
14
- include Worker
15
-
16
- def process(params)
17
- with_symbol = params[:title]
18
- with_string = params["title"]
19
- @model = "I was working hard on #{params.inspect}. title:#{with_symbol} \"title\"=>#{with_string}"
20
- end
21
- end
22
-
23
- class NoBackgroundOperation < Operation
24
- def self.background?
25
- false
26
- end
27
- end
28
-
29
- # test basic worker functionality.
30
- describe "with sidekiq ss" do
31
- it do
32
- res = Operation.run(title: "Dragonfly")
33
-
34
- res.kind_of?(String).must_equal true # for now, we return the job from sidekiq
35
- Operation.jobs[0]["args"].must_equal([{"title"=>"Dragonfly"}])
36
- Operation.perform_one.last.model.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly"
37
- end
38
- end
39
-
40
- # without sidekiq, we don't have indifferent_access automatically.
41
- it { NoBackgroundOperation.run(title: "Dragonfly").last.model.must_equal "I was working hard on {:title=>\"Dragonfly\"}. title:Dragonfly \"title\"=>" }
42
-
43
-
44
- # test manual serialisation (to be done with UploadedFile etc automatically).
45
- class SerializingOperation < Operation
46
- include Worker
47
-
48
- def self.serializable(params)
49
- {wrap: params}
50
- end
51
-
52
- def deserializable(params)
53
- params[:wrap]
54
- end
55
- end
56
-
57
- describe "with serialization in sidekiq" do
58
- before { @res = SerializingOperation.run(title: "Dragonfly") }
59
-
60
- it { @res.kind_of?(String).must_equal true } # for now, we return the job from sidekiq.
61
- it { SerializingOperation.jobs[0]["args"].must_equal([{"wrap"=>{"title"=>"Dragonfly"}}]) }
62
- it { SerializingOperation.perform_one.last.model.must_equal "I was working hard on {\"title\"=>\"Dragonfly\"}. title:Dragonfly \"title\"=>Dragonfly" }
63
- end
64
- end
65
-
66
-
67
- require "trailblazer/operation/uploaded_file"
68
- require "action_dispatch/http/upload"
69
- class WorkerFileMarshallerTest < MiniTest::Spec
70
- def uploaded_file(name)
71
- tmp = Tempfile.new("bla")
72
- tmp.write File.open("test/fixtures/#{name}").read
73
-
74
- ActionDispatch::Http::UploadedFile.new(
75
- tempfile: tmp,
76
- filename: name,
77
- type: "image/png")
78
- end
79
-
80
- class Operation < Trailblazer::Operation
81
- contract do
82
- property :title
83
- property :image, file: true
84
-
85
- property :album do
86
- property :image, file: true
87
- end
88
- end
89
-
90
- include Worker
91
- include Worker::FileMarshaller # should be ContractFileMarshaller
92
-
93
- def process(params)
94
- @params = params
95
- end
96
-
97
- attr_reader :params
98
- end
99
-
100
- # TODO: no image
101
-
102
- # with image serializes the file for later retrieval.
103
- it do
104
- Operation.run(title: "Dragonfly", image: uploaded_file("apotomo.png"), album: {image: uploaded_file("cells.png")})
105
-
106
- args = Operation.jobs[0]["args"].first
107
- args["title"].must_equal("Dragonfly")
108
- args["image"]["filename"].must_equal "apotomo.png"
109
- args["image"]["tempfile_path"].must_match /trailblazer_upload/
110
-
111
- args["album"]["image"]["filename"].must_equal "cells.png"
112
-
113
- _, op = Operation.perform_one # deserialize.
114
-
115
- params = op.params
116
-
117
- params["title"].must_equal("Dragonfly")
118
- params[:title].must_equal("Dragonfly") # must allow indifferent_access.
119
- params["image"].must_be_kind_of ActionDispatch::Http::UploadedFile
120
- params["image"].original_filename.must_equal "apotomo.png"
121
- params["album"]["image"].must_be_kind_of ActionDispatch::Http::UploadedFile
122
- params["album"]["image"].original_filename.must_equal "cells.png"
123
- end
124
- end