rails_ops 1.1.21 → 1.1.25

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 (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