rails_readonly_injector 0.2.0 → 1.2.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 +5 -5
- data/.github/ISSUE_TEMPLATE.md +18 -0
- data/.github/PULL_REQUEST_TEMPLATE.md +24 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +7 -0
- data/.travis.yml +3 -1
- data/Appraisals +10 -1
- data/CHANGELOG.md +6 -0
- data/CODE_OF_CONDUCT.md +0 -0
- data/Gemfile +4 -2
- data/LICENSE.txt +0 -0
- data/README.md +25 -6
- data/Rakefile +4 -2
- data/bin/console +4 -3
- data/cucumber_features/configuration.feature +0 -0
- data/cucumber_features/step_definitions/.gitkeep +0 -0
- data/cucumber_features/step_definitions/generic_steps.rb +5 -3
- data/cucumber_features/step_definitions/user_steps.rb +18 -14
- data/cucumber_features/support/.gitkeep +0 -0
- data/cucumber_features/user_controller.feature +0 -0
- data/cucumber_features/user_controller_readonly.feature +0 -0
- data/cucumber_features/user_model.feature +0 -0
- data/cucumber_features/user_model_readonly.feature +0 -0
- data/development_tasks/support/file_helpers.rb +57 -0
- data/development_tasks/support/gem_helpers.rb +63 -0
- data/development_tasks/tests.rake +40 -93
- data/gemfiles/.bundle/config +0 -0
- data/gemfiles/rails_3.gemfile +0 -0
- data/gemfiles/rails_4_0.gemfile +0 -0
- data/gemfiles/rails_4_1.gemfile +0 -0
- data/gemfiles/rails_4_2.gemfile +0 -0
- data/gemfiles/rails_5_0.gemfile +0 -0
- data/gemfiles/rails_5_1.gemfile +0 -0
- data/gemfiles/rails_5_2.gemfile +10 -0
- data/lib/rails_readonly_injector.rb +37 -20
- data/lib/rails_readonly_injector/configuration.rb +86 -20
- data/lib/rails_readonly_injector/version.rb +3 -1
- data/rails_readonly_injector.gemspec +16 -14
- data/rspec_specs/.gitkeep +0 -0
- data/rspec_specs/rails_readonly_injector/configuration_spec.rb +167 -0
- data/rspec_specs/{readonly_site_toggle_spec.rb → rails_readonly_injector_spec.rb} +42 -1
- data/rspec_specs/support/.gitkeep +0 -0
- metadata +36 -18
- data/rspec_specs/readonly_site_toggle/configuration_spec.rb +0 -31
@@ -1,130 +1,77 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'support/file_helpers'
|
4
|
+
require_relative 'support/gem_helpers'
|
2
5
|
|
3
6
|
namespace :dev do
|
4
|
-
|
5
|
-
|
7
|
+
include GemHelpers
|
8
|
+
include FileHelpers
|
6
9
|
|
7
|
-
desc
|
10
|
+
desc 'Deploys a test rails application.'
|
8
11
|
task :deploy_test_app do
|
9
12
|
switch_to_gems_root_path
|
10
13
|
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
puts "Creating a new rails application..."
|
15
|
-
FileUtils.mkdir_p RAILS_APP_PATH
|
16
|
-
system("bundle exec rails new #{RAILS_APP_PATH}")
|
14
|
+
puts 'Creating a new rails application...'
|
15
|
+
generate_rails_application
|
17
16
|
|
18
17
|
switch_to_rails_app_path
|
19
18
|
|
20
|
-
|
21
|
-
# so we can write them to the Gemfile rails generated.
|
22
|
-
# i.e. To 'override'/force a specific version.
|
23
|
-
gems_defined_in_appraisal = parse_gemfile(ENV['BUNDLE_GEMFILE'])
|
24
|
-
gems_defined_in_gemfile = parse_gemfile('Gemfile').collect { |l| l.gem_name }
|
25
|
-
|
26
|
-
gems_to_override = gems_defined_in_appraisal.reject { |l| gems_defined_in_gemfile.include? l.gem_name }.collect { |gem| gem.original_line_in_gemfile }
|
19
|
+
ensure_gem_versions_defined_in_appraisal_are_used
|
27
20
|
|
28
21
|
# Add required gems to the gemfile
|
29
|
-
|
30
|
-
|
31
|
-
append_to_file 'Gemfile', %{gem "rails_readonly_injector", path: "#{GEM_ROOT_PATH}"\n}
|
22
|
+
add_gem 'simplecov', require: false, group: :test
|
23
|
+
add_gem 'rails_readonly_injector', path: gems_root_path
|
32
24
|
|
33
25
|
# Make sure we don't use the gemfile from Appraisal
|
34
|
-
|
35
|
-
ENV.delete('BUNDLE_BIN_PATH')
|
36
|
-
ENV.delete('RUBYOPT')
|
26
|
+
unset_appraisal_environment_variables
|
37
27
|
|
38
28
|
# Install gems
|
39
|
-
system("bundle install
|
29
|
+
system("bundle install")
|
40
30
|
|
41
|
-
puts
|
42
|
-
system(
|
43
|
-
|
44
|
-
puts "Installing RSpec..."
|
45
|
-
system("bundle exec rails generate rspec:install")
|
31
|
+
puts 'Executing Generators...'
|
32
|
+
system('bundle exec rails generate cucumber:install')
|
33
|
+
system('bundle exec rails generate rspec:install')
|
46
34
|
|
47
35
|
# RSpec: Include all files in support/
|
48
36
|
append_to_file 'spec/spec_helper.rb', "Dir.glob('support/**/*.rb').each { |rb| require rb }"
|
49
37
|
|
50
|
-
#
|
51
|
-
append_to_beginning_of_file 'spec/spec_helper.rb', %{
|
52
|
-
require 'simplecov'
|
53
|
-
require 'rails_readonly_injector'
|
54
|
-
}
|
55
|
-
append_to_beginning_of_file 'features/support/env.rb', "require 'simplecov'"
|
38
|
+
install_simplecov("#{gems_root_path}/coverage")
|
56
39
|
|
57
|
-
write_file_with_content '.simplecov', %{
|
58
|
-
SimpleCov.start do
|
59
|
-
coverage_dir '#{GEM_ROOT_PATH}/coverage'
|
60
|
-
end
|
61
|
-
}
|
62
|
-
|
63
40
|
# Prepare database migrations, etc
|
64
|
-
system(
|
41
|
+
system('bundle exec rails generate scaffold User name:string')
|
65
42
|
|
66
|
-
system(
|
67
|
-
end
|
68
|
-
|
69
|
-
desc "Synchronises tests from `cucumber_features` and `rspec_specs` into the rails application in #{RAILS_APP_PATH}, and runs the tests against the application."
|
70
|
-
task :run_tests do
|
71
|
-
switch_to_rails_app_path
|
72
|
-
|
73
|
-
# Set up the Cucumber and RSpec tests
|
74
|
-
FileUtils.cp_r File.join(GEM_ROOT_PATH, 'cucumber_features', '.'), 'features'
|
75
|
-
FileUtils.cp_r File.join(GEM_ROOT_PATH, 'rspec_specs', '.'), 'spec'
|
76
|
-
|
77
|
-
exit_code = system('bundle exec cucumber && bundle exec rspec')
|
78
|
-
exit exit_code
|
43
|
+
system('RAILS_ENV=test bundle exec rake db:migrate')
|
79
44
|
end
|
80
45
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
File.open(file_path).readlines.each do |line|
|
85
|
-
matches = line.match /^\s*gem\s+['|"]/
|
86
|
-
|
87
|
-
next if matches.nil?
|
46
|
+
desc 'Synchronises tests from `cucumber_features` and `rspec_specs` into the temporary rails app, and runs them.'
|
47
|
+
task run_tests: %i[run_features run_specs]
|
88
48
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
gems << OpenStruct.new({ gem_name: gem_name, original_line_in_gemfile: line })
|
94
|
-
end
|
95
|
-
|
96
|
-
gems
|
97
|
-
end
|
49
|
+
desc 'Synchronises features from `cucumber_features` into the temporary rails app, and runs them.'
|
50
|
+
task :run_features do
|
51
|
+
switch_to_rails_app_path
|
98
52
|
|
99
|
-
|
100
|
-
FileUtils.
|
101
|
-
end
|
53
|
+
# Synchronise the cucumber features
|
54
|
+
FileUtils.cp_r File.join(gems_root_path, 'cucumber_features'), 'features'
|
102
55
|
|
103
|
-
|
104
|
-
FileUtils.cd RAILS_APP_PATH
|
105
|
-
end
|
56
|
+
unset_appraisal_environment_variables
|
106
57
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
File.open(path_to_file, 'a') do |f|
|
111
|
-
f.write content
|
112
|
-
end
|
58
|
+
command_executed_successfully = system('bundle exec cucumber')
|
59
|
+
|
60
|
+
exit 1 unless command_executed_successfully
|
113
61
|
end
|
114
62
|
|
115
|
-
|
116
|
-
|
63
|
+
desc 'Synchronise specs from `rspec_specs` into the temporary rails app, and run rspec.'
|
64
|
+
task :run_specs do
|
65
|
+
switch_to_rails_app_path
|
117
66
|
|
118
|
-
|
67
|
+
# Synchronise the RSpec specs
|
68
|
+
FileUtils.cp_r File.join(gems_root_path, 'rspec_specs'), 'spec'
|
119
69
|
|
120
|
-
|
70
|
+
unset_appraisal_environment_variables
|
121
71
|
|
122
|
-
|
72
|
+
command_executed_successfully = system('bundle exec rspec')
|
73
|
+
|
74
|
+
exit 1 unless command_executed_successfully
|
123
75
|
end
|
124
76
|
|
125
|
-
def write_file_with_content(path_to_file, content)
|
126
|
-
File.open(path_to_file, 'w') do |f|
|
127
|
-
f.write content
|
128
|
-
end
|
129
|
-
end
|
130
77
|
end
|
data/gemfiles/.bundle/config
CHANGED
File without changes
|
data/gemfiles/rails_3.gemfile
CHANGED
File without changes
|
data/gemfiles/rails_4_0.gemfile
CHANGED
File without changes
|
data/gemfiles/rails_4_1.gemfile
CHANGED
File without changes
|
data/gemfiles/rails_4_2.gemfile
CHANGED
File without changes
|
data/gemfiles/rails_5_0.gemfile
CHANGED
File without changes
|
data/gemfiles/rails_5_1.gemfile
CHANGED
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# This file was generated by Appraisal
|
2
|
+
|
3
|
+
source "https://rubygems.org"
|
4
|
+
|
5
|
+
gem "rails", "~> 5.2.0"
|
6
|
+
gem "cucumber-rails", "~> 1.6.0", group: :test, require: false
|
7
|
+
gem "rspec-rails", "~> 3.7.2", group: :test
|
8
|
+
gem "database_cleaner", "~> 1.0.1"
|
9
|
+
|
10
|
+
gemspec path: "../"
|
@@ -1,33 +1,50 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'rails_readonly_injector/version'
|
4
|
+
require 'rails_readonly_injector/configuration'
|
3
5
|
|
4
6
|
module RailsReadonlyInjector
|
7
|
+
# Applies changes defined in the `config` object
|
8
|
+
# and resets `config.dirty?` to false
|
5
9
|
def self.reload!
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
else
|
12
|
-
descendants = ApplicationRecord.descendants
|
13
|
-
end
|
14
|
-
|
15
|
-
descendants.each do |descendant_class|
|
16
|
-
|
17
|
-
# Ensure excluded classes aren't set to read-only
|
18
|
-
if config.classes_to_exclude.include? descendant_class
|
19
|
-
restore_readonly_method(descendant_class)
|
10
|
+
config.classes_to_include.each do |klass|
|
11
|
+
# Ensure we restore classes that we want to exclude, to their defaults
|
12
|
+
# in case they were previously marked as read-only.
|
13
|
+
if config.classes_to_exclude.include? klass
|
14
|
+
restore_readonly_method(klass)
|
20
15
|
next
|
21
16
|
end
|
22
17
|
|
23
|
-
if
|
24
|
-
override_readonly_method(
|
18
|
+
if config.send(:read_only)
|
19
|
+
override_readonly_method(klass)
|
25
20
|
else
|
26
|
-
restore_readonly_method(
|
21
|
+
restore_readonly_method(klass)
|
27
22
|
end
|
28
23
|
end
|
29
24
|
|
30
25
|
inject_error_handler_into_actioncontroller_base
|
26
|
+
|
27
|
+
config.send(:reset_dirty_status!)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Returns the currently loaded `config.read_only` value.
|
31
|
+
# @return [Boolean] Whether the currently loaded config is set to read-only.
|
32
|
+
def self.in_read_only_mode?
|
33
|
+
if config.dirty? && config.changed_attributes.key?(:read_only)
|
34
|
+
# Return the previously stored value
|
35
|
+
config.changed_attributes[:read_only]
|
36
|
+
else
|
37
|
+
config.send(:read_only)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Sets the desired configuration object, if a block is provided,
|
42
|
+
# and then returns the current configuration object.
|
43
|
+
# @return [Configuration] The current configuration object.
|
44
|
+
def self.config
|
45
|
+
yield configuration if block_given?
|
46
|
+
|
47
|
+
configuration
|
31
48
|
end
|
32
49
|
|
33
50
|
private
|
@@ -53,7 +70,7 @@ module RailsReadonlyInjector
|
|
53
70
|
end
|
54
71
|
|
55
72
|
def self.inject_error_handler_into_actioncontroller_base
|
56
|
-
ActionController::Base.class_eval do
|
73
|
+
ActionController::Base.class_eval do
|
57
74
|
rescue_from ActiveRecord::ReadOnlyRecord, with: :rescue_from_readonly_failure
|
58
75
|
|
59
76
|
protected
|
@@ -1,44 +1,110 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module RailsReadonlyInjector
|
2
4
|
class Configuration
|
3
|
-
|
5
|
+
attr_reader :controller_rescue_action, :classes_to_include, :classes_to_exclude
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@read_only = false
|
9
|
+
@controller_rescue_action = proc {}
|
10
|
+
@classes_to_exclude = []
|
11
|
+
|
12
|
+
@changed_attributes = {}
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<Class>] An array of classes to include
|
16
|
+
# If not specified upon initialisation, it defaults to:
|
17
|
+
# ActiveRecord::Base.descendants on Rails < 5.0.0, or
|
18
|
+
# ApplicationRecord.descendants on Rails >= 5.0.0
|
19
|
+
def classes_to_include
|
20
|
+
return @classes_to_include if defined? @classes_to_include
|
21
|
+
|
22
|
+
Rails.application.eager_load!
|
23
|
+
|
24
|
+
if Rails::VERSION::STRING < '5.0.0'
|
25
|
+
ActiveRecord::Base.descendants
|
26
|
+
else
|
27
|
+
ApplicationRecord.descendants
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
#######################
|
32
|
+
# Setter Methods #
|
33
|
+
#######################
|
4
34
|
|
5
|
-
|
6
|
-
|
35
|
+
# @param new_value [Boolean] Whether the site should be in read-only mode
|
36
|
+
def read_only=(new_value)
|
37
|
+
update_instance_variable('@read_only', new_value)
|
7
38
|
end
|
8
39
|
|
40
|
+
# @param action [Lambda, Proc] The action to execute when rescuing from
|
41
|
+
# `ActiveRecord::RecordReadOnly` errors, within a controller
|
9
42
|
def controller_rescue_action=(action)
|
10
43
|
raise 'A lambda or proc must be specified' unless action.respond_to? :call
|
11
44
|
|
12
|
-
@controller_rescue_action
|
45
|
+
update_instance_variable('@controller_rescue_action', action)
|
13
46
|
end
|
14
47
|
|
15
|
-
|
16
|
-
|
48
|
+
# @param klasses [Array<Class>] The classes to exclude from being marked as read-only
|
49
|
+
def classes_to_exclude=(klasses)
|
50
|
+
update_instance_variable('@classes_to_exclude', klasses)
|
17
51
|
end
|
18
52
|
|
19
|
-
|
20
|
-
|
53
|
+
# @param klasses [Array<Class>] The classes to mark as read-only
|
54
|
+
def classes_to_include=(klasses)
|
55
|
+
update_instance_variable('@classes_to_include', klasses)
|
21
56
|
end
|
22
|
-
end
|
23
|
-
private_constant :Configuration
|
24
57
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
yield self.configuration if block_given?
|
58
|
+
#####################
|
59
|
+
# Instance methods #
|
60
|
+
#####################
|
29
61
|
|
30
|
-
|
31
|
-
|
62
|
+
# @return [Boolean] Whether the configuration
|
63
|
+
# has changed since the config was last reloaded
|
64
|
+
def dirty?
|
65
|
+
!changed_attributes.empty?
|
66
|
+
end
|
32
67
|
|
33
|
-
|
34
|
-
|
68
|
+
# @return [Hash] A hash of changed attributes
|
69
|
+
# and their previous values
|
70
|
+
attr_reader :changed_attributes
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Updates the value of the specified instance variable
|
75
|
+
# and tracks the attribute's previous value (for `#dirty?`)
|
76
|
+
def update_instance_variable(variable_name, new_value)
|
77
|
+
old_value = instance_variable_get(variable_name).freeze
|
78
|
+
|
79
|
+
instance_variable_set(variable_name.to_sym, new_value)
|
80
|
+
|
81
|
+
unless old_value == new_value
|
82
|
+
changed_attributes[variable_name.to_s.delete('@').to_sym] = old_value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Resets the changed attributes hash,
|
87
|
+
# so that `#dirty?` returns false
|
88
|
+
def reset_dirty_status!
|
89
|
+
@changed_attributes = {}
|
90
|
+
end
|
35
91
|
|
36
|
-
|
92
|
+
attr_reader :read_only
|
37
93
|
end
|
94
|
+
private_constant :Configuration
|
38
95
|
|
39
96
|
private
|
40
97
|
|
98
|
+
# @return [Configuration] The current configuration object
|
41
99
|
def self.configuration
|
42
100
|
@config ||= Configuration.new
|
43
101
|
end
|
44
|
-
|
102
|
+
|
103
|
+
# Resets the current configuration to the defaults
|
104
|
+
# and reloads RailsReadonlyInjector
|
105
|
+
def self.reset_configuration!
|
106
|
+
@config = Configuration.new
|
107
|
+
|
108
|
+
reload!
|
109
|
+
end
|
110
|
+
end
|
@@ -1,30 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
|
-
lib = File.expand_path(
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require 'rails_readonly_injector/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'rails_readonly_injector'
|
8
9
|
spec.version = RailsReadonlyInjector::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
10
|
+
spec.authors = ['Andrew Walter']
|
11
|
+
spec.email = ['andrew.walter@burnet.edu.au']
|
11
12
|
|
12
13
|
spec.summary = "Globally toggle 'read-only' mode in a Rails application, on-demand, without having to restart the server."
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
14
|
+
spec.homepage = 'https://www.github.com/xtrasimplicity/rails_readonly_injector'
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
18
|
f.match(%r{^(test|spec|features)/})
|
18
19
|
end
|
19
|
-
spec.bindir =
|
20
|
+
spec.bindir = 'exe'
|
20
21
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
21
|
-
spec.require_paths = [
|
22
|
+
spec.require_paths = ['lib']
|
22
23
|
|
23
|
-
spec.required_ruby_version =
|
24
|
+
spec.required_ruby_version = '>= 2.1.3'
|
25
|
+
|
26
|
+
spec.add_runtime_dependency 'rails', ['>= 3.0', '< 5.3']
|
24
27
|
|
25
|
-
spec.add_runtime_dependency "rails", [">= 3.0", "< 5.2"]
|
26
|
-
|
27
|
-
spec.add_development_dependency "bundler", "~> 1.16"
|
28
|
-
spec.add_development_dependency "rake", "~> 10.0"
|
29
28
|
spec.add_development_dependency 'appraisal', '~> 2.2'
|
29
|
+
spec.add_development_dependency 'bundler', '>= 1.16'
|
30
|
+
spec.add_development_dependency 'rake', '>= 12.3.3'
|
31
|
+
spec.add_development_dependency 'yard', '~> 0.9.12'
|
30
32
|
end
|