snfoil 0.9.0 → 1.0.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 +4 -4
- data/CHANGELOG.md +5 -0
- data/Gemfile +6 -0
- data/LICENSE.txt +201 -0
- data/README.md +95 -505
- data/docs/build-context.md +56 -0
- data/docs/create-context.md +109 -0
- data/docs/destroy-context.md +102 -0
- data/docs/index-context.md +70 -0
- data/docs/show-context.md +71 -0
- data/docs/update-context.md +107 -0
- data/lib/{sn_foil → snfoil}/adapters/orms/active_record.rb +14 -0
- data/lib/{sn_foil → snfoil}/adapters/orms/base_adapter.rb +15 -1
- data/lib/snfoil/crud/build_context.rb +49 -0
- data/lib/snfoil/crud/change_context.rb +48 -0
- data/lib/snfoil/crud/context.rb +41 -0
- data/lib/snfoil/crud/create_context.rb +45 -0
- data/lib/snfoil/crud/destroy_context.rb +57 -0
- data/lib/snfoil/crud/index_context.rb +46 -0
- data/lib/snfoil/crud/setup_context.rb +90 -0
- data/lib/snfoil/crud/show_context.rb +52 -0
- data/lib/snfoil/crud/update_context.rb +60 -0
- data/lib/snfoil/version.rb +19 -0
- data/lib/snfoil.rb +69 -0
- data/snfoil.gemspec +47 -0
- metadata +100 -33
- data/Rakefile +0 -4
- data/lib/sn_foil/context.rb +0 -24
- data/lib/sn_foil/contexts/build_context.rb +0 -67
- data/lib/sn_foil/contexts/change_context.rb +0 -101
- data/lib/sn_foil/contexts/create_context.rb +0 -158
- data/lib/sn_foil/contexts/destroy_context.rb +0 -164
- data/lib/sn_foil/contexts/index_context.rb +0 -64
- data/lib/sn_foil/contexts/setup_context.rb +0 -119
- data/lib/sn_foil/contexts/show_context.rb +0 -61
- data/lib/sn_foil/contexts/update_context.rb +0 -168
- data/lib/sn_foil/policy.rb +0 -55
- data/lib/sn_foil/searcher.rb +0 -123
- data/lib/sn_foil/version.rb +0 -5
- data/lib/sn_foil.rb +0 -49
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/concern'
|
18
|
+
require_relative './change_context'
|
19
|
+
|
20
|
+
module SnFoil
|
21
|
+
module CRUD
|
22
|
+
module CreateContext
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
included do
|
26
|
+
include BuildContext
|
27
|
+
include ChangeContext
|
28
|
+
|
29
|
+
action :create, with: :create_action
|
30
|
+
|
31
|
+
setup_create { |options| run_interval(:setup, **options) }
|
32
|
+
setup_create { |options| run_interval(:setup_build, **options) }
|
33
|
+
setup_create { |options| run_interval(:setup_change, **options) }
|
34
|
+
before_create { |options| run_interval(:before_change, **options) }
|
35
|
+
after_create_success { |options| run_interval(:after_change_success, **options) }
|
36
|
+
after_create_failure { |options| run_interval(:after_change_failure, **options) }
|
37
|
+
after_create { |options| run_interval(:after_change, **options) }
|
38
|
+
|
39
|
+
def create_action(options)
|
40
|
+
wrap_object(options[:object]).save
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/concern'
|
18
|
+
require_relative './setup_context'
|
19
|
+
require_relative './change_context'
|
20
|
+
|
21
|
+
module SnFoil
|
22
|
+
module CRUD
|
23
|
+
module DestroyContext
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
|
26
|
+
included do
|
27
|
+
include SetupContext
|
28
|
+
include ChangeContext
|
29
|
+
|
30
|
+
action :destroy, with: :destroy_action
|
31
|
+
|
32
|
+
setup_destroy { |options| run_interval(:setup, **options) }
|
33
|
+
setup_destroy { |options| run_interval(:setup_change, **options) }
|
34
|
+
before_destroy { |options| run_interval(:before_change, **options) }
|
35
|
+
after_destroy_success { |options| run_interval(:after_change_success, **options) }
|
36
|
+
after_destroy_failure { |options| run_interval(:after_change_failure, **options) }
|
37
|
+
after_destroy { |options| run_interval(:after_change, **options) }
|
38
|
+
|
39
|
+
setup_destroy do |options|
|
40
|
+
raise ArgumentError, 'one of the following keywords is required: id, object' unless options[:id] || options[:object]
|
41
|
+
|
42
|
+
options
|
43
|
+
end
|
44
|
+
|
45
|
+
before_destroy do |options|
|
46
|
+
options[:object] ||= scope.resolve.find(options[:id])
|
47
|
+
|
48
|
+
options
|
49
|
+
end
|
50
|
+
|
51
|
+
def destroy_action(options)
|
52
|
+
wrap_object(options[:object]).destroy
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/concern'
|
18
|
+
require_relative './change_context'
|
19
|
+
|
20
|
+
module SnFoil
|
21
|
+
module CRUD
|
22
|
+
module IndexContext
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
included do
|
26
|
+
include SetupContext
|
27
|
+
|
28
|
+
action :index, with: :index_action
|
29
|
+
|
30
|
+
setup_index { |options| run_interval(:setup, **options) }
|
31
|
+
|
32
|
+
before_index do |options|
|
33
|
+
options[:object] ||= options.fetch(:searcher) { self.class.snfoil_searcher }
|
34
|
+
.new(scope: scope.resolve)
|
35
|
+
.search(options.fetch(:params) { {} })
|
36
|
+
|
37
|
+
options
|
38
|
+
end
|
39
|
+
|
40
|
+
def index_action(**options)
|
41
|
+
options[:object]
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'snfoil/context'
|
18
|
+
|
19
|
+
require 'active_support/concern'
|
20
|
+
require 'active_support/core_ext/string/inflections'
|
21
|
+
|
22
|
+
module SnFoil
|
23
|
+
module CRUD
|
24
|
+
module SetupContext
|
25
|
+
extend ActiveSupport::Concern
|
26
|
+
|
27
|
+
included do
|
28
|
+
include SnFoil::Context
|
29
|
+
|
30
|
+
interval :setup
|
31
|
+
|
32
|
+
authorize do |options|
|
33
|
+
options.fetch(:policy) { policy }
|
34
|
+
.new(entity, options[:object], **options)
|
35
|
+
.send(options.fetch(:authorize) { "#{options[:action]}?" })
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class_methods do
|
40
|
+
attr_reader :snfoil_model, :snfoil_policy, :snfoil_searcher
|
41
|
+
|
42
|
+
def searcher(klass = nil)
|
43
|
+
@snfoil_searcher = klass
|
44
|
+
end
|
45
|
+
|
46
|
+
def model(klass = nil)
|
47
|
+
@snfoil_model = klass
|
48
|
+
end
|
49
|
+
|
50
|
+
def policy(klass = nil)
|
51
|
+
@snfoil_policy = klass
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def model
|
56
|
+
self.class.snfoil_model
|
57
|
+
end
|
58
|
+
|
59
|
+
def policy
|
60
|
+
self.class.snfoil_policy
|
61
|
+
end
|
62
|
+
|
63
|
+
def scope(_object_class = nil, **options)
|
64
|
+
"#{policy.name}::Scope".safe_constantize.new(wrap_object(model), entity, **options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def wrap_object(object)
|
68
|
+
return object unless adapter
|
69
|
+
|
70
|
+
adapter.new(object)
|
71
|
+
end
|
72
|
+
|
73
|
+
def unwrap_object(object)
|
74
|
+
return object unless adapter
|
75
|
+
|
76
|
+
adapter?(object) ? object.__getobj__ : object
|
77
|
+
end
|
78
|
+
|
79
|
+
def adapter?(object)
|
80
|
+
return false unless adapter
|
81
|
+
|
82
|
+
object.instance_of? adapter
|
83
|
+
end
|
84
|
+
|
85
|
+
def adapter
|
86
|
+
@adapter ||= SnFoil.adapter
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/concern'
|
18
|
+
require_relative './setup_context'
|
19
|
+
|
20
|
+
module SnFoil
|
21
|
+
module CRUD
|
22
|
+
module ShowContext
|
23
|
+
extend ActiveSupport::Concern
|
24
|
+
|
25
|
+
included do
|
26
|
+
include SetupContext
|
27
|
+
|
28
|
+
action :show, with: :show_action
|
29
|
+
|
30
|
+
setup_show do |options|
|
31
|
+
raise ArgumentError, 'one of the following keywords is required: id, object' unless options[:id] || options[:object]
|
32
|
+
|
33
|
+
options
|
34
|
+
end
|
35
|
+
|
36
|
+
setup_show do |options|
|
37
|
+
run_interval(:setup, **options)
|
38
|
+
end
|
39
|
+
|
40
|
+
before_show do |**options|
|
41
|
+
options[:object] ||= scope.resolve.find(options[:id])
|
42
|
+
|
43
|
+
options
|
44
|
+
end
|
45
|
+
|
46
|
+
def show_action(options)
|
47
|
+
options[:object]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/concern'
|
18
|
+
require_relative './setup_context'
|
19
|
+
require_relative './change_context'
|
20
|
+
|
21
|
+
module SnFoil
|
22
|
+
module CRUD
|
23
|
+
module UpdateContext
|
24
|
+
extend ActiveSupport::Concern
|
25
|
+
|
26
|
+
included do
|
27
|
+
include SetupContext
|
28
|
+
include ChangeContext
|
29
|
+
|
30
|
+
action :update, with: :update_action
|
31
|
+
|
32
|
+
setup_update { |options| run_interval(:setup, **options) }
|
33
|
+
setup_update { |options| run_interval(:setup_change, **options) }
|
34
|
+
before_update { |options| run_interval(:before_change, **options) }
|
35
|
+
after_update_success { |options| run_interval(:after_change_success, **options) }
|
36
|
+
after_update_failure { |options| run_interval(:after_change_failure, **options) }
|
37
|
+
after_update { |options| run_interval(:after_change, **options) }
|
38
|
+
|
39
|
+
setup_update do |options|
|
40
|
+
raise ArgumentError, 'one of the following keywords is required: id, object' unless options[:id] || options[:object]
|
41
|
+
|
42
|
+
options
|
43
|
+
end
|
44
|
+
|
45
|
+
before_update do |options|
|
46
|
+
params = options.fetch(:params) { {} }
|
47
|
+
options[:object] ||= scope.resolve.find(options[:id])
|
48
|
+
|
49
|
+
wrap_object(options[:object]).attributes = params
|
50
|
+
|
51
|
+
options
|
52
|
+
end
|
53
|
+
|
54
|
+
def update_action(options)
|
55
|
+
wrap_object(options[:object]).save
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
module SnFoil
|
18
|
+
VERSION = '1.0.0'
|
19
|
+
end
|
data/lib/snfoil.rb
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Copyright 2021 Matthew Howes
|
4
|
+
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
6
|
+
# you may not use this file except in compliance with the License.
|
7
|
+
# You may obtain a copy of the License at
|
8
|
+
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
14
|
+
# See the License for the specific language governing permissions and
|
15
|
+
# limitations under the License.
|
16
|
+
|
17
|
+
require 'active_support/core_ext/module/attribute_accessors'
|
18
|
+
require 'logger'
|
19
|
+
require 'snfoil/context'
|
20
|
+
require 'snfoil/policy'
|
21
|
+
require 'snfoil/searcher'
|
22
|
+
|
23
|
+
require_relative 'snfoil/version'
|
24
|
+
|
25
|
+
require_relative 'snfoil/crud/build_context'
|
26
|
+
require_relative 'snfoil/crud/change_context'
|
27
|
+
require_relative 'snfoil/crud/create_context'
|
28
|
+
require_relative 'snfoil/crud/destroy_context'
|
29
|
+
require_relative 'snfoil/crud/index_context'
|
30
|
+
require_relative 'snfoil/crud/setup_context'
|
31
|
+
require_relative 'snfoil/crud/show_context'
|
32
|
+
require_relative 'snfoil/crud/update_context'
|
33
|
+
require_relative 'snfoil/crud/context'
|
34
|
+
|
35
|
+
require_relative 'snfoil/adapters/orms/base_adapter'
|
36
|
+
require_relative 'snfoil/adapters/orms/active_record'
|
37
|
+
|
38
|
+
module SnFoil
|
39
|
+
class Error < StandardError; end
|
40
|
+
|
41
|
+
mattr_accessor :orm, default: 'active_record'
|
42
|
+
mattr_writer :logger
|
43
|
+
|
44
|
+
class << self
|
45
|
+
def logger
|
46
|
+
@logger ||= Logger.new($stdout).tap do |log|
|
47
|
+
log.progname = name
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def adapter
|
52
|
+
return @adapter if @adapter
|
53
|
+
|
54
|
+
@adapter ||= if orm.instance_of?(String) || orm.instance_of?(Symbol)
|
55
|
+
if Object.const_defined?("SnFoil::Adapters::ORMs::#{orm.camelcase}")
|
56
|
+
"SnFoil::Adapters::ORMs::#{orm.camelcase}".constantize
|
57
|
+
else
|
58
|
+
orm.constantize
|
59
|
+
end
|
60
|
+
else
|
61
|
+
orm
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def configure
|
66
|
+
yield self
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
data/snfoil.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'lib/snfoil/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'snfoil'
|
7
|
+
spec.version = SnFoil::VERSION
|
8
|
+
spec.authors = ['Matthew Howes', 'Danny Murphy', 'Cliff Campbell']
|
9
|
+
spec.email = ['howeszy@gmail.com', 'dmurph24@gmail.com', 'cliffcampbell@hey.com']
|
10
|
+
|
11
|
+
spec.summary = 'A Toolbox of Context Behaviors'
|
12
|
+
spec.description = 'A collection of SnFoil gems and additional helper classes'
|
13
|
+
spec.homepage = 'https://github.com/limited-effort/snfoil'
|
14
|
+
spec.license = 'Apache-2.0'
|
15
|
+
spec.required_ruby_version = '>= 2.7'
|
16
|
+
|
17
|
+
spec.metadata['homepage_uri'] = spec.homepage
|
18
|
+
spec.metadata['source_code_uri'] = spec.homepage
|
19
|
+
spec.metadata['changelog_uri'] = 'https://github.com/limited-effort/snfoil/blob/main/CHANGELOG.md'
|
20
|
+
spec.metadata['rubygems_mfa_required'] = 'true'
|
21
|
+
|
22
|
+
# Specify which files should be added to the gem when it is released.
|
23
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
24
|
+
ignore_list = %r{\A(?:test/|spec/|bin/|features/|Rakefile|\.\w)}
|
25
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) { `git ls-files -z`.split("\x0").reject { |f| f.match(ignore_list) } }
|
26
|
+
|
27
|
+
spec.bindir = 'exe'
|
28
|
+
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
29
|
+
spec.require_paths = ['lib']
|
30
|
+
|
31
|
+
spec.add_dependency 'activesupport', '>= 5.2.6'
|
32
|
+
spec.add_dependency 'logger', '~> 1.0'
|
33
|
+
spec.add_dependency 'pundit', '~> 2.0'
|
34
|
+
spec.add_dependency 'snfoil-context', '~> 1.0'
|
35
|
+
spec.add_dependency 'snfoil-policy', '~> 1.0'
|
36
|
+
spec.add_dependency 'snfoil-searcher', '~> 1.0'
|
37
|
+
|
38
|
+
spec.add_development_dependency 'bundle-audit', '~> 0.1.0'
|
39
|
+
spec.add_development_dependency 'dry-struct', '~> 1.0'
|
40
|
+
spec.add_development_dependency 'fasterer', '~> 0.10.0'
|
41
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.9'
|
42
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
43
|
+
spec.add_development_dependency 'rspec', '~> 3.9'
|
44
|
+
spec.add_development_dependency 'rubocop', '~> 1.29'
|
45
|
+
spec.add_development_dependency 'rubocop-performance', '~> 1.11'
|
46
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 2.4'
|
47
|
+
end
|