rails_ops 1.1.21 → 1.1.25

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +27 -0
  3. data/.gitignore +1 -0
  4. data/.rubocop.yml +1 -0
  5. data/CHANGELOG.md +17 -0
  6. data/LICENSE +1 -1
  7. data/README.md +105 -15
  8. data/Rakefile +4 -0
  9. data/VERSION +1 -1
  10. data/lib/generators/operation/USAGE +21 -0
  11. data/lib/generators/operation/operation_generator.rb +46 -0
  12. data/lib/generators/operation/templates/controller.erb +39 -0
  13. data/lib/generators/operation/templates/create.erb +11 -0
  14. data/lib/generators/operation/templates/destroy.erb +9 -0
  15. data/lib/generators/operation/templates/load.erb +5 -0
  16. data/lib/generators/operation/templates/update.erb +12 -0
  17. data/lib/generators/operation/templates/view.erb +0 -0
  18. data/lib/rails_ops/mixins/model/authorization.rb +3 -1
  19. data/lib/rails_ops/model_mixins/sti_fixes.rb +21 -0
  20. data/lib/rails_ops/model_mixins.rb +1 -0
  21. data/lib/rails_ops/operation/model/update.rb +17 -2
  22. data/lib/rails_ops.rb +1 -0
  23. data/rails_ops.gemspec +25 -27
  24. data/test/dummy/app/models/animal.rb +1 -0
  25. data/test/dummy/app/models/bird.rb +1 -0
  26. data/test/dummy/app/models/cat.rb +1 -0
  27. data/test/dummy/app/models/dog.rb +1 -0
  28. data/test/dummy/app/models/flower.rb +7 -0
  29. data/test/dummy/app/models/nightingale.rb +1 -0
  30. data/test/dummy/app/models/phoenix.rb +1 -0
  31. data/test/dummy/app/models/user.rb +3 -0
  32. data/test/dummy/config/application.rb +12 -1
  33. data/test/dummy/config/environments/test.rb +1 -1
  34. data/test/dummy/config/initializers/backtrace_silencers.rb +1 -1
  35. data/test/dummy/db/schema.rb +8 -0
  36. data/test/test_helper.rb +2 -0
  37. data/test/unit/rails_ops/generators/operation_generator_test.rb +170 -0
  38. data/test/unit/rails_ops/operation/model/sti_test.rb +70 -0
  39. data/test/unit/rails_ops/operation/model/update_test.rb +26 -0
  40. data/test/unit/rails_ops/operation/update_auth_test.rb +62 -0
  41. data/test/unit/rails_ops/operation/update_lazy_auth_test.rb +63 -0
  42. metadata +99 -10
  43. data/.travis.yml +0 -11
@@ -0,0 +1 @@
1
+ class Bird < Animal; end
@@ -0,0 +1 @@
1
+ class Cat < Animal; end
@@ -0,0 +1 @@
1
+ class Dog < Animal; end
@@ -0,0 +1,7 @@
1
+ class Flower < ApplicationRecord
2
+ # default_scope ->() { where(planted: true) }
3
+
4
+ def self.default_scope
5
+ where(planted: true)
6
+ end
7
+ end
@@ -0,0 +1 @@
1
+ class Nightingale < Bird; end
@@ -0,0 +1 @@
1
+ class Phoenix < Bird; end
@@ -0,0 +1,3 @@
1
+ class User < ApplicationRecord
2
+ belongs_to :group
3
+ end
@@ -1,6 +1,17 @@
1
1
  require_relative 'boot'
2
2
 
3
- require 'rails/all'
3
+ require 'rails'
4
+
5
+ require 'active_model/railtie'
6
+ require 'active_job/railtie'
7
+ require 'active_record/railtie'
8
+ # require "active_storage/engine"
9
+ require 'action_controller/railtie'
10
+ require 'action_mailer/railtie'
11
+ require 'action_view/railtie'
12
+ require 'action_cable/engine'
13
+ require 'sprockets/railtie'
14
+ require 'rails/test_unit/railtie'
4
15
 
5
16
  Bundler.require(*Rails.groups)
6
17
  require 'rails_ops'
@@ -10,7 +10,7 @@ Rails.application.configure do
10
10
  # Do not eager load code on boot. This avoids loading your whole application
11
11
  # just for the purpose of running a single test. If you are using a tool that
12
12
  # preloads Rails for running tests, you may have to set it to true.
13
- config.eager_load = false
13
+ config.eager_load = true
14
14
 
15
15
  # Configure public file server for tests with Cache-Control for performance.
16
16
  config.public_file_server.enabled = true
@@ -4,4 +4,4 @@
4
4
  # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ }
5
5
 
6
6
  # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code.
7
- # Rails.backtrace_cleaner.remove_silencers!
7
+ Rails.backtrace_cleaner.remove_silencers!
@@ -6,4 +6,12 @@ ActiveRecord::Schema.define do
6
6
  t.string :color
7
7
  t.timestamps
8
8
  end
9
+
10
+ create_table :animals, force: true do |t|
11
+ t.string :type
12
+ end
13
+
14
+ create_table :flowers, force: true do |t|
15
+ t.boolean :planted, null: false, default: true
16
+ end
9
17
  end
data/test/test_helper.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  require File.expand_path('../../test/dummy/config/environment.rb', __FILE__)
2
2
  # ActiveRecord::Migrator.migrations_paths = [File.expand_path("../../test/dummy/db/migrate", __FILE__)]
3
3
  require 'rails/test_help'
4
+ require 'pry'
5
+ require 'colorize'
4
6
 
5
7
  # Filter out Minitest backtrace while allowing backtrace from other libraries
6
8
  # to be shown.
@@ -0,0 +1,170 @@
1
+ require 'test_helper'
2
+ require 'generators/operation/operation_generator'
3
+
4
+ class OperationGeneratorTest < Rails::Generators::TestCase
5
+ tests OperationGenerator
6
+ destination File.expand_path('../../../tmp', File.dirname(__FILE__))
7
+
8
+ setup do
9
+ prepare_destination
10
+
11
+ # Add an empty routes file
12
+ Dir.mkdir(File.join(destination_root, 'config'))
13
+ File.open(File.join(destination_root, 'config', 'routes.rb'), 'w') do |f|
14
+ f.write <<~ROUTES
15
+ Rails.application.routes.draw do
16
+ end
17
+ ROUTES
18
+ end
19
+ end
20
+
21
+ def test_all
22
+ run_generator ['User']
23
+
24
+ # Check that operations are generated
25
+ assert_operations
26
+
27
+ # Check that views are generated
28
+ assert_views
29
+
30
+ # Check that the controller is generated
31
+ assert_controller
32
+
33
+ # Check that the routes entry is added
34
+ assert_routes
35
+ end
36
+
37
+ def test_no_views
38
+ run_generator ['User', '--skip-views']
39
+
40
+ assert_operations
41
+ assert_controller
42
+ assert_routes
43
+
44
+ # Check that the views were skipped
45
+ %w(index show new edit).each do |view|
46
+ assert_no_file "app/views/users/#{view}.html.haml"
47
+ end
48
+ end
49
+
50
+ def test_no_controller
51
+ run_generator ['User', '--skip-controller']
52
+
53
+ assert_operations
54
+ assert_views
55
+ assert_routes
56
+
57
+ # Check that the controller was skipped
58
+ assert_no_file 'app/controllers/users_controller.rb'
59
+ end
60
+
61
+ def test_no_routes
62
+ run_generator ['User', '--skip-routes']
63
+
64
+ assert_operations
65
+ assert_views
66
+ assert_controller
67
+
68
+ # Check that the routes were not added
69
+ assert_file 'config/routes.rb' do |routes|
70
+ assert_no_match(/resources :users/, routes)
71
+ end
72
+ end
73
+
74
+ def test_skip_all
75
+ run_generator ['User', '--skip-controller', '--skip-routes', '--skip-views']
76
+
77
+ assert_operations
78
+
79
+ # Check that the controller was skipped
80
+ assert_no_file 'app/controllers/users_controller.rb'
81
+
82
+ # Check that the routes were not added
83
+ assert_file 'config/routes.rb' do |routes|
84
+ assert_no_match(/resources :users/, routes)
85
+ end
86
+
87
+ # Check that the views were skipped
88
+ %w(index show new edit).each do |view|
89
+ assert_no_file "app/views/users/#{view}.html.haml"
90
+ end
91
+ end
92
+
93
+ def test_only_operations
94
+ run_generator ['User', '--only-operations']
95
+
96
+ assert_operations
97
+
98
+ # Check that the controller was skipped
99
+ assert_no_file 'app/controllers/users_controller.rb'
100
+
101
+ # Check that the routes were not added
102
+ assert_file 'config/routes.rb' do |routes|
103
+ assert_no_match(/resources :users/, routes)
104
+ end
105
+
106
+ # Check that the views were skipped
107
+ %w(index show new edit).each do |view|
108
+ assert_no_file "app/views/users/#{view}.html.haml"
109
+ end
110
+ end
111
+
112
+ def test_lowercase_name
113
+ run_generator ['user']
114
+
115
+ # Check that operations are generated
116
+ assert_operations
117
+
118
+ # Check that views are generated
119
+ assert_views
120
+
121
+ # Check that the controller is generated
122
+ assert_controller
123
+
124
+ # Check that the routes entry is added
125
+ assert_routes
126
+ end
127
+
128
+ private
129
+
130
+ def assert_operations
131
+ assert_file 'app/operations/user/create.rb' do |operation|
132
+ assert_match(/module Operations::User/, operation)
133
+ assert_match(/class Create < RailsOps::Operation::Model::Create/, operation)
134
+ assert_match(/model ::User/, operation)
135
+ end
136
+ assert_file 'app/operations/user/destroy.rb' do |operation|
137
+ assert_match(/module Operations::User/, operation)
138
+ assert_match(/class Destroy < RailsOps::Operation::Model::Destroy/, operation)
139
+ assert_match(/model ::User/, operation)
140
+ end
141
+ assert_file 'app/operations/user/load.rb' do |operation|
142
+ assert_match(/module Operations::User/, operation)
143
+ assert_match(/class Load < RailsOps::Operation::Model::Load/, operation)
144
+ assert_match(/model ::User/, operation)
145
+ end
146
+ assert_file 'app/operations/user/update.rb' do |operation|
147
+ assert_match(/module Operations::User/, operation)
148
+ assert_match(/class Update < RailsOps::Operation::Model::Update/, operation)
149
+ assert_match(/model ::User/, operation)
150
+ end
151
+ end
152
+
153
+ def assert_views
154
+ %w(index show new edit).each do |view|
155
+ assert_file "app/views/users/#{view}.html.haml"
156
+ end
157
+ end
158
+
159
+ def assert_controller
160
+ assert_file 'app/controllers/users_controller.rb' do |controller|
161
+ assert_match(/class UsersController < ApplicationController/, controller)
162
+ end
163
+ end
164
+
165
+ def assert_routes
166
+ assert_file 'config/routes.rb' do |routes|
167
+ assert_match(/resources :users/, routes)
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,70 @@
1
+ require 'test_helper'
2
+
3
+ class RailsOps::Operation::Model::StiTest < ActiveSupport::TestCase
4
+ include TestHelper
5
+
6
+ setup do
7
+ @dog = Dog.create!
8
+ @cat = Cat.create!
9
+ @phoenix = Phoenix.create!
10
+ @nightingale = Nightingale.create!
11
+ end
12
+
13
+ LOAD_ANIMAL_OP = Class.new(RailsOps::Operation::Model::Load) do
14
+ model Animal do
15
+ attribute :my_virtual_animal_name
16
+ end
17
+ end
18
+
19
+ LOAD_DOG_OP = Class.new(RailsOps::Operation::Model::Load) do
20
+ model Dog do
21
+ attribute :my_virtual_dog_name
22
+ end
23
+ end
24
+
25
+ LOAD_BIRD_OP = Class.new(RailsOps::Operation::Model::Load) do
26
+ model Bird do
27
+ attribute :my_virtual_bird_name
28
+ end
29
+ end
30
+
31
+ LOAD_PHOENIX_OP = Class.new(RailsOps::Operation::Model::Load) do
32
+ model Phoenix do
33
+ attribute :my_virtual_phoenix_name
34
+ end
35
+ end
36
+
37
+ def test_load_animal
38
+ model = LOAD_ANIMAL_OP.new(id: @dog.id).model
39
+ assert_equal 'Dog', model.type
40
+ assert model.is_a?(LOAD_ANIMAL_OP.model)
41
+ assert_nothing_raised { model.my_virtual_animal_name = 'Lenny' }
42
+ end
43
+
44
+ def test_load_dog
45
+ model = LOAD_DOG_OP.new(id: @dog.id).model
46
+
47
+ assert_equal 'Dog', model.type
48
+ assert model.is_a?(LOAD_DOG_OP.model)
49
+ assert_nothing_raised { model.my_virtual_dog_name = 'Lenny' }
50
+ end
51
+
52
+ def test_load_birds
53
+ phoenix = LOAD_BIRD_OP.new(id: @phoenix.id).model
54
+ nightingale = LOAD_BIRD_OP.new(id: @nightingale.id).model
55
+
56
+ assert_equal 'Phoenix', phoenix.type
57
+ assert phoenix.is_a?(LOAD_BIRD_OP.model)
58
+ assert nightingale.is_a?(LOAD_BIRD_OP.model)
59
+ assert_nothing_raised { phoenix.my_virtual_bird_name = 'Lenny' }
60
+ assert_nothing_raised { nightingale.my_virtual_bird_name = 'Lenny' }
61
+ end
62
+
63
+ def test_load_phoenix
64
+ model = LOAD_PHOENIX_OP.new(id: @phoenix.id).model
65
+
66
+ assert_equal 'Phoenix', model.type
67
+ assert model.is_a?(LOAD_PHOENIX_OP.model)
68
+ assert_nothing_raised { model.my_virtual_phoenix_name = 'Lenny' }
69
+ end
70
+ end
@@ -7,6 +7,12 @@ class RailsOps::Operation::Model::UpdateTest < ActiveSupport::TestCase
7
7
  model Group
8
8
  end
9
9
 
10
+ FLOWER_OP = Class.new(RailsOps::Operation::Model::Update) do
11
+ model Flower do
12
+ attribute :color
13
+ end
14
+ end
15
+
10
16
  def test_basic
11
17
  g = Group.create
12
18
  op = BASIC_OP.new(id: g.id)
@@ -19,4 +25,24 @@ class RailsOps::Operation::Model::UpdateTest < ActiveSupport::TestCase
19
25
  op = BASIC_OP.new(id: g.id)
20
26
  assert_equal op, op.model.parent_op
21
27
  end
28
+
29
+ def test_issue_59992
30
+ flower1 = Flower.create!(planted: true)
31
+ flower2 = Flower.create!(planted: false)
32
+
33
+ op = FLOWER_OP.new(id: flower1.id)
34
+
35
+ assert op.model.respond_to?(:color)
36
+ assert op.model.is_a?(FLOWER_OP.model)
37
+
38
+ op.model.color = :red
39
+
40
+ assert_equal :red, op.model.color
41
+
42
+ op = FLOWER_OP.new(id: flower2.id)
43
+
44
+ assert_raises ActiveRecord::RecordNotFound do
45
+ op.model
46
+ end
47
+ end
22
48
  end
@@ -0,0 +1,62 @@
1
+ require 'test_helper'
2
+ require 'rails_ops/authorization_backend/can_can_can'
3
+ require 'cancancan'
4
+
5
+ class RailsOps::Operation::UpdateLazyAuthTest < ActiveSupport::TestCase
6
+ include TestHelper
7
+
8
+ BASIC_OP = Class.new(RailsOps::Operation::Model::Update) do
9
+ model ::Group
10
+
11
+ model_authorization_action :update
12
+
13
+ def perform
14
+ fail osparams.exception if osparams.exception
15
+ @done = true
16
+ end
17
+ end
18
+
19
+ ABILITY = Class.new do
20
+ include CanCan::Ability
21
+
22
+ def initialize(read: false, update: false)
23
+ can :read, Group if read
24
+ can :update, Group if update
25
+ end
26
+ end
27
+
28
+ setup do
29
+ Group.create!(id: 1, name: 'Group')
30
+ RailsOps.config.authorization_backend = 'RailsOps::AuthorizationBackend::CanCanCan'
31
+ end
32
+
33
+ def test_unpermitted_read
34
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true))
35
+ assert_raises CanCan::AccessDenied do
36
+ BASIC_OP.new(ctx, id: 1)
37
+ end
38
+ end
39
+
40
+ def test_permitted_read
41
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true))
42
+ assert_nothing_raised do
43
+ BASIC_OP.new(ctx, id: 1)
44
+ end
45
+ end
46
+
47
+ def test_unpermitted_update
48
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true))
49
+ op = BASIC_OP.new(ctx, id: 1)
50
+ assert_raises CanCan::AccessDenied do
51
+ op.run!
52
+ end
53
+ end
54
+
55
+ def test_permitted_update
56
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true, update: true))
57
+ op = BASIC_OP.new(ctx, id: 1)
58
+ assert_nothing_raised do
59
+ op.run!
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,63 @@
1
+ require 'test_helper'
2
+ require 'rails_ops/authorization_backend/can_can_can'
3
+ require 'cancancan'
4
+
5
+ class RailsOps::Operation::UpdateLazyAuthTest < ActiveSupport::TestCase
6
+ include TestHelper
7
+
8
+ BASIC_OP = Class.new(RailsOps::Operation::Model::Update) do
9
+ model ::Group
10
+
11
+ model_authorization_action :update, lazy: true
12
+
13
+ def perform
14
+ fail osparams.exception if osparams.exception
15
+ @done = true
16
+ end
17
+ end
18
+
19
+ ABILITY = Class.new do
20
+ include CanCan::Ability
21
+
22
+ def initialize(read: false, update: false)
23
+ can :read, Group if read
24
+ can :update, Group if update
25
+ end
26
+ end
27
+
28
+ setup do
29
+ Group.delete_all
30
+ Group.create!(id: 1, name: 'Group')
31
+ RailsOps.config.authorization_backend = 'RailsOps::AuthorizationBackend::CanCanCan'
32
+ end
33
+
34
+ def test_unpermitted_read
35
+ ctx = RailsOps::Context.new(ability: ABILITY.new)
36
+ assert_raises CanCan::AccessDenied do
37
+ BASIC_OP.new(ctx, id: 1)
38
+ end
39
+ end
40
+
41
+ def test_permitted_read
42
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true))
43
+ assert_nothing_raised do
44
+ BASIC_OP.new(ctx, id: 1)
45
+ end
46
+ end
47
+
48
+ def test_unpermitted_update
49
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true))
50
+ op = BASIC_OP.new(ctx, id: 1)
51
+ assert_raises CanCan::AccessDenied do
52
+ op.run!
53
+ end
54
+ end
55
+
56
+ def test_permitted_update
57
+ ctx = RailsOps::Context.new(ability: ABILITY.new(read: true, update: true))
58
+ op = BASIC_OP.new(ctx, id: 1)
59
+ assert_nothing_raised do
60
+ op.run!
61
+ end
62
+ end
63
+ end