uses 1.0.0.pre.beta1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.circleci/config.yml +35 -0
- data/.gitignore +9 -0
- data/.rspec +1 -0
- data/.ruby-version +1 -0
- data/CODE_OF_CONDUCT.md +74 -0
- data/CONTRIBUTING.md +5 -0
- data/Gemfile +6 -0
- data/LICENSE.md +33 -0
- data/README.md +368 -0
- data/Rakefile +2 -0
- data/bin/ci +10 -0
- data/bin/console +14 -0
- data/bin/rspec +29 -0
- data/bin/setup +6 -0
- data/lib/uses/circular_dependency/analyzer.rb +60 -0
- data/lib/uses/circular_dependency/base_notifier.rb +18 -0
- data/lib/uses/circular_dependency/error.rb +7 -0
- data/lib/uses/circular_dependency/ignore_notifier.rb +10 -0
- data/lib/uses/circular_dependency/log_notifier.rb +10 -0
- data/lib/uses/circular_dependency/raise_error_notifier.rb +11 -0
- data/lib/uses/config.rb +38 -0
- data/lib/uses/error.rb +4 -0
- data/lib/uses/initializer/base_initializer.rb +19 -0
- data/lib/uses/initializer/from_initializers.rb +9 -0
- data/lib/uses/initializer/new_no_args.rb +11 -0
- data/lib/uses/initializer/proc_based.rb +7 -0
- data/lib/uses/initializer.rb +35 -0
- data/lib/uses/inject_double.rb +76 -0
- data/lib/uses/invalid_method_name.rb +5 -0
- data/lib/uses/method.rb +87 -0
- data/lib/uses/method_name.rb +23 -0
- data/lib/uses/uses_method_args.rb +24 -0
- data/lib/uses/version.rb +3 -0
- data/lib/uses.rb +49 -0
- data/uses.gemspec +29 -0
- metadata +138 -0
@@ -0,0 +1,60 @@
|
|
1
|
+
require_relative "log_notifier"
|
2
|
+
require_relative "ignore_notifier"
|
3
|
+
require_relative "raise_error_notifier"
|
4
|
+
|
5
|
+
module Uses
|
6
|
+
module CircularDependency
|
7
|
+
class Analyzer
|
8
|
+
def initialize(uses_method_args)
|
9
|
+
@uses_method_args = uses_method_args
|
10
|
+
end
|
11
|
+
|
12
|
+
def analyze!
|
13
|
+
@dependency, @path_to_dependency = transitive_dependency?(@uses_method_args.klass_with_uses,@uses_method_args.klass_being_used)
|
14
|
+
end
|
15
|
+
|
16
|
+
def circular_dependency?
|
17
|
+
!!@dependency
|
18
|
+
end
|
19
|
+
|
20
|
+
def notify!
|
21
|
+
raise "You have not called analyze!" if @dependency.nil?
|
22
|
+
notifier = case @uses_method_args.uses_config.on_circular_dependency
|
23
|
+
when :warn then Uses::CircularDependency::LogNotifer.new(@uses_method_args, @path_to_dependency)
|
24
|
+
when :ignore then Uses::CircularDependency::IgnoreNotifier.new(@uses_method_args, @path_to_dependency)
|
25
|
+
when :raise_error then Uses::CircularDependency::RaiseErrorNotifier.new(@uses_method_args, @path_to_dependency)
|
26
|
+
end
|
27
|
+
notifier.notify!
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
|
32
|
+
def transitive_dependency?(klass_with_uses,klass_being_analyzed, path=[])
|
33
|
+
other_class_has_uses = klass_being_analyzed.respond_to?(:__uses_dependent_classes)
|
34
|
+
|
35
|
+
if other_class_has_uses
|
36
|
+
if klass_with_uses == klass_being_analyzed
|
37
|
+
[ true, path ]
|
38
|
+
else
|
39
|
+
# Want to stop searching as soon as we find something
|
40
|
+
procs_to_check_for_transitive_dependencies = klass_being_analyzed.__uses_dependent_classes.keys.map { |klass|
|
41
|
+
->() { transitive_dependency?(klass_with_uses,klass, path + [ klass_being_analyzed ]) }
|
42
|
+
}
|
43
|
+
first_proc_to_find_a_dependency = procs_to_check_for_transitive_dependencies.detect { |p|
|
44
|
+
transitive_dependency, _ = p.()
|
45
|
+
transitive_dependency
|
46
|
+
}
|
47
|
+
if first_proc_to_find_a_dependency
|
48
|
+
_, path_to_dependency = first_proc_to_find_a_dependency.()
|
49
|
+
[ true, path_to_dependency ]
|
50
|
+
else
|
51
|
+
false
|
52
|
+
end
|
53
|
+
end
|
54
|
+
else
|
55
|
+
false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Uses
|
2
|
+
module CircularDependency
|
3
|
+
class BaseNotifier
|
4
|
+
def initialize(uses_method_args, path_to_dependency)
|
5
|
+
path = if path_to_dependency.empty?
|
6
|
+
nil
|
7
|
+
else
|
8
|
+
" via #{path_to_dependency.map(&:to_s).join(',')}"
|
9
|
+
end
|
10
|
+
@message = "#{uses_method_args.klass_being_used} and #{uses_method_args.klass_with_uses} have a circular dependency#{path}. This may cause unforseen issues, or just be generally confusing"
|
11
|
+
end
|
12
|
+
|
13
|
+
def notify!
|
14
|
+
raise "subclass must implement"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/uses/config.rb
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
require "active_support/core_ext/object/inclusion"
|
2
|
+
module Uses
|
3
|
+
class Config
|
4
|
+
# Configure what should happen when a circular dependency is detected.
|
5
|
+
#
|
6
|
+
# :warn:: Emit a warning, but allow it (default)
|
7
|
+
# :raise_error:: Raise an exception, effectively making your app unusable until
|
8
|
+
# you resolve the circular dependencies
|
9
|
+
# :ignore:: Emit a warning at DEBUG level, effectively allowing you to ignore these issues.
|
10
|
+
attr_reader :on_circular_dependency
|
11
|
+
|
12
|
+
# The array of custom initializers. Generally you should use
|
13
|
+
# `Uses.initializers do |initializers|` to manipulate this
|
14
|
+
attr_reader :initializers
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
reset!
|
18
|
+
end
|
19
|
+
|
20
|
+
def reset!
|
21
|
+
self.on_circular_dependency = :warn
|
22
|
+
@initializers = {}
|
23
|
+
end
|
24
|
+
|
25
|
+
ON_CIRCULAR_DEPENDENCY_VALUES = [
|
26
|
+
:ignore,
|
27
|
+
:raise_error,
|
28
|
+
:warn,
|
29
|
+
]
|
30
|
+
def on_circular_dependency=(new_value)
|
31
|
+
if !new_value.in?(ON_CIRCULAR_DEPENDENCY_VALUES)
|
32
|
+
raise ArgumentError, "#{new_value} is not a valid value for on_circular_dependency. Use one of #{ON_CIRCULAR_DEPENDENCY_VALUES}"
|
33
|
+
end
|
34
|
+
@on_circular_dependency = new_value
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
data/lib/uses/error.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
module Uses
|
2
|
+
module Initializer
|
3
|
+
class BaseInitializer
|
4
|
+
def initialize(uses_method_args)
|
5
|
+
@proc = self.create_proc(uses_method_args)
|
6
|
+
end
|
7
|
+
|
8
|
+
def call
|
9
|
+
@proc.()
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def create_proc(uses_method_args)
|
15
|
+
raise "subclass must implement"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require_relative "base_initializer"
|
2
|
+
|
3
|
+
class Uses::Initializer::FromInitializers < Uses::Initializer::BaseInitializer
|
4
|
+
def create_proc(uses_method_args)
|
5
|
+
uses_method_args.uses_config.initializers.fetch(uses_method_args.klass_being_used)
|
6
|
+
rescue KeyError
|
7
|
+
raise "An initializer for #{uses_method_args.klass_being_used.name} has not been defined. #{uses_method_args.klass_with_uses.name} has set initialize: to :config_initializers, which means it's assuming some other file (e.g. in config/initializers) has called Uses.initializers to set up the initialization"
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require_relative "base_initializer"
|
2
|
+
|
3
|
+
class Uses::Initializer::NewNoArgs < Uses::Initializer::BaseInitializer
|
4
|
+
def create_proc(uses_method_args)
|
5
|
+
initialize_method = uses_method_args.klass_being_used.instance_method(:initialize)
|
6
|
+
if !initialize_method.arity.in?([0,-1])
|
7
|
+
raise "#{uses_method_args.klass_being_used}'s initializer has required arguments, but has been used in #{uses_method_args.klass_with_uses.class} to initializer with no arguments passed to ::new. Please use initialize: with a Proc or :config_initializers to control how the instance is created"
|
8
|
+
end
|
9
|
+
->() { uses_method_args.klass_being_used.new }
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require_relative "error"
|
2
|
+
require_relative "initializer/new_no_args"
|
3
|
+
require_relative "initializer/from_initializers"
|
4
|
+
require_relative "initializer/proc_based"
|
5
|
+
|
6
|
+
module Uses
|
7
|
+
module Initializer
|
8
|
+
def self.from_args(uses_method_args)
|
9
|
+
strategy_klass(uses_method_args).new(uses_method_args)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def self.strategy_klass(uses_method_args)
|
15
|
+
case uses_method_args.initializer_strategy
|
16
|
+
when :new_no_args then NewNoArgs
|
17
|
+
when :config_initializers then FromInitializers
|
18
|
+
when Proc then ProcBased
|
19
|
+
else
|
20
|
+
raise UnknownInitializerStrategy.new(uses_method_args.initializer_strategy)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
class UnknownInitializerStrategy < Uses::Error
|
26
|
+
def initialize(strategy)
|
27
|
+
if strategy.kind_of?(Symbol)
|
28
|
+
super("initialize: received #{strategy}, which is not supported. Should be either the symbol :config_initializers, a Proc, or simply omitted")
|
29
|
+
else
|
30
|
+
super("initialize: received a #{strategy.class}, which is not supported. Should be either the symbol :config_initializers, a Proc, or simply omitted")
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module Uses
|
2
|
+
# Convienience methods for test to inject mocks/doubles into a class under test.
|
3
|
+
#
|
4
|
+
# An advantage of "injecting" dependencies is that you can provide alternate implementations
|
5
|
+
# for testing that simplify your tests. While Ruby doesn't strictly require that you make
|
6
|
+
# dependencies injectible, it is nice to have a bit of help in doing to so for a test.
|
7
|
+
#
|
8
|
+
# If using RSpec, use `inject_rspec_double`.
|
9
|
+
module InjectDouble
|
10
|
+
# Inject an instantiated double into subject, returing the double.
|
11
|
+
#
|
12
|
+
# subject:: the instance of the class under test where you want a double injected
|
13
|
+
# injectsion:: a hash of size 1 where the key is the class given to `uses` and
|
14
|
+
# the value is the doubled object
|
15
|
+
def inject_double(subject, injections)
|
16
|
+
if injections.size != 1
|
17
|
+
raise "expected a single key/value to inject_double, but got #{injections.size}"
|
18
|
+
end
|
19
|
+
|
20
|
+
klass = injections.first[0]
|
21
|
+
instance = injections.first[1]
|
22
|
+
|
23
|
+
subject_must_use_uses!(subject)
|
24
|
+
injected_class_must_be_class!(klass)
|
25
|
+
|
26
|
+
name = dependency_method_name!(subject, klass)
|
27
|
+
|
28
|
+
subject.__uses_dependent_instances[name] = instance
|
29
|
+
|
30
|
+
instance
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# For Rspec users, you might do:
|
35
|
+
#
|
36
|
+
# dependent_service = instance_doule(DependentService)
|
37
|
+
# allow(DependentService).to receive(:new).and_return(dependent_service)
|
38
|
+
#
|
39
|
+
# The problem is that it would be nice to know if your class under test actually uses the
|
40
|
+
# dependent service, plus it's annoying to have to write two lines of code.
|
41
|
+
#
|
42
|
+
# Instead:
|
43
|
+
#
|
44
|
+
# dependent_service = instance_double(object_under_test, DependentService)
|
45
|
+
#
|
46
|
+
# If you depend in DependentService, this will replace the real instance with yours. If you do not
|
47
|
+
# it will raise an error.
|
48
|
+
def inject_rspec_double(subject, klass)
|
49
|
+
self.inject_double(subject, klass => instance_double(klass))
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def subject_must_use_uses!(subject)
|
55
|
+
if !subject.class.respond_to?(:__uses_dependent_classes)
|
56
|
+
raise Uses::Error, "#{subject.class} does not include Uses::Method, so you cannot inject a double into it"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def injected_class_must_be_class!(klass)
|
61
|
+
if !klass.kind_of?(Class)
|
62
|
+
raise Uses::Error, "Pass the actual class, not a #{klass.class}."
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def dependency_method_name!(subject, klass)
|
67
|
+
name = subject.class.__uses_dependent_classes[klass].to_s
|
68
|
+
|
69
|
+
if name.blank?
|
70
|
+
raise Uses::Error, "#{subject.class} does not depend on a #{klass}, so there is no reason to inject a mock"
|
71
|
+
end
|
72
|
+
name
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
end
|
data/lib/uses/method.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require "active_support/concern"
|
2
|
+
require "active_support/core_ext/string/inflections"
|
3
|
+
|
4
|
+
require_relative "config"
|
5
|
+
require_relative "method_name"
|
6
|
+
require_relative "initializer"
|
7
|
+
require_relative "uses_method_args"
|
8
|
+
require_relative "circular_dependency/analyzer"
|
9
|
+
|
10
|
+
module Uses
|
11
|
+
# Provides a very basic mechanism for dependency management between classes in your service
|
12
|
+
# layer. This is done via the method `uses`.
|
13
|
+
#
|
14
|
+
# The simplest use of this module is to create a base class for all classes in your service layer:
|
15
|
+
#
|
16
|
+
# # app/services/application_service.rb
|
17
|
+
# class ApplicationService
|
18
|
+
# include Uses::Method
|
19
|
+
# end
|
20
|
+
#
|
21
|
+
# Then, all services inherit from this and have access to the uses method.
|
22
|
+
#
|
23
|
+
# Note that any class or method without RubyDoc should be treated as private/internal, and you
|
24
|
+
# should not depend on.
|
25
|
+
module Method
|
26
|
+
|
27
|
+
extend ActiveSupport::Concern
|
28
|
+
|
29
|
+
class_methods do
|
30
|
+
# Declare that the class including the Uses::Method module depends on another
|
31
|
+
# class. This will create an instance method on this class that returns a memoized
|
32
|
+
# instance of the class passed in (klass).
|
33
|
+
#
|
34
|
+
# klass:: the class of what is dependend-upon.
|
35
|
+
# as:: if given, overrides the default naming for the instance method.
|
36
|
+
# By default (when as: is omitted or set to nil), the name will
|
37
|
+
# be `klass.underscore.gsub(/\//,"_")` (see Uses::MethodName),
|
38
|
+
# so for a class named SomeClass, it would be `some_class`, however for
|
39
|
+
# a class named SomeNamespace::SomeClass, it would be `some_namespace_some_class`.
|
40
|
+
# If you set a value for `as:` that value would be used instead of this auto-generated value.
|
41
|
+
# initialize: Controls how the instance is initialized:
|
42
|
+
# :new_no_args:: create the instance with `.new` an no args. This is the default, since
|
43
|
+
# most service-layer classes should not need initializer arguments.
|
44
|
+
# :config_initializers:: Indicates that an intiailzation proc has been previously
|
45
|
+
# configured and should be used. See ::initializers above.
|
46
|
+
# a Proc:: The `Proc` is called to return the new instance. Generally you would
|
47
|
+
# only use this if your class required special initialization but is only
|
48
|
+
# used in *this* class. Keep in mind that this couples the service with
|
49
|
+
# how to iniltialize its dependent, which is not often a good thing. But
|
50
|
+
# sometimes you have to.
|
51
|
+
def uses(klass, as: nil, initialize: :new_no_args)
|
52
|
+
uses_method_args = Uses::UsesMethodArgs.new(
|
53
|
+
klass_being_used: klass,
|
54
|
+
klass_with_uses: self,
|
55
|
+
method_name_override: as,
|
56
|
+
initializer_strategy: initialize,
|
57
|
+
uses_config: Uses.config
|
58
|
+
)
|
59
|
+
|
60
|
+
name = Uses::MethodName.new(uses_method_args)
|
61
|
+
circular_dependency_analyzer = Uses::CircularDependency::Analyzer.new(uses_method_args)
|
62
|
+
initializer = Uses::Initializer.from_args(uses_method_args)
|
63
|
+
|
64
|
+
circular_dependency_analyzer.analyze!
|
65
|
+
|
66
|
+
if circular_dependency_analyzer.circular_dependency?
|
67
|
+
circular_dependency_analyzer.notify!
|
68
|
+
end
|
69
|
+
|
70
|
+
self.__uses_dependent_classes[klass] = name
|
71
|
+
|
72
|
+
define_method name.to_s do
|
73
|
+
self.__uses_dependent_instances[name.to_s] ||= initializer.()
|
74
|
+
end
|
75
|
+
private name.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
def __uses_dependent_classes
|
79
|
+
@__uses_dependent_classes ||= {}
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def __uses_dependent_instances
|
84
|
+
@__uses_dependent_instances ||= {}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require_relative "invalid_method_name"
|
2
|
+
module Uses
|
3
|
+
class MethodName
|
4
|
+
|
5
|
+
def self.derive_method_name(klass)
|
6
|
+
klass.name.to_s.underscore.gsub(/\//,"_")
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(uses_method_args)
|
10
|
+
@name = if uses_method_args.method_name_override.nil?
|
11
|
+
self.class.derive_method_name(uses_method_args.klass_being_used)
|
12
|
+
else
|
13
|
+
uses_method_args.method_name_override.to_s
|
14
|
+
end
|
15
|
+
if @name !~ /^[a-z0-9_]+$/
|
16
|
+
raise Uses::InvalidMethodName.new("Cannot determine a default name for #{uses_method_args.klass_being_used} used by #{uses_method_args.klass_with_uses}. Use as: to specify the name")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
def to_s
|
20
|
+
@name
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Uses
|
2
|
+
class UsesMethodArgs
|
3
|
+
|
4
|
+
attr_reader :klass_being_used,
|
5
|
+
:klass_with_uses,
|
6
|
+
:method_name_override,
|
7
|
+
:initializer_strategy,
|
8
|
+
:uses_config
|
9
|
+
|
10
|
+
def initialize(klass_being_used:,
|
11
|
+
klass_with_uses:,
|
12
|
+
method_name_override:,
|
13
|
+
initializer_strategy:,
|
14
|
+
uses_config:)
|
15
|
+
|
16
|
+
@klass_being_used = klass_being_used
|
17
|
+
@klass_with_uses = klass_with_uses
|
18
|
+
@method_name_override = method_name_override
|
19
|
+
@initializer_strategy = initializer_strategy
|
20
|
+
@uses_config = uses_config
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/uses/version.rb
ADDED
data/lib/uses.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module Uses
|
2
|
+
# Yields a hash of initializer, with the intention that you insert
|
3
|
+
# the initializer for your service into this hash. The key should be the class name
|
4
|
+
# that would be given to a `uses` invocation, and the value should be a proc
|
5
|
+
# that returns an instance of that class.
|
6
|
+
#
|
7
|
+
# The reason you would do this is if your service requires special setup beyond calling
|
8
|
+
# new without arguments. For example:
|
9
|
+
#
|
10
|
+
#
|
11
|
+
# require "uses"
|
12
|
+
# Uses.initializers do |initializers|
|
13
|
+
# initializers[Aws::S3::Client] = ->(*) {
|
14
|
+
# Aws::S3::Client.new(
|
15
|
+
# access_key_id: ENV["AWS_ACCESS_KEY_ID"],
|
16
|
+
# secret_access_key: ENV["AWS_SECRET_ACCESS_KEY"],
|
17
|
+
# region: ENV["AWS_REGION"],
|
18
|
+
# )
|
19
|
+
# }
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
# # Then, in a service that uses this:
|
23
|
+
#
|
24
|
+
# class MyService
|
25
|
+
# include Uses::Method
|
26
|
+
#
|
27
|
+
# uses Aws::S3::Client, as: :s3, initialize: :config_initializers
|
28
|
+
#
|
29
|
+
# def some_method
|
30
|
+
# s3.whatever # s3 has been initialized using the Proc above
|
31
|
+
# end
|
32
|
+
# end
|
33
|
+
def self.initializers
|
34
|
+
yield(config.initializers) if block_given?
|
35
|
+
config.initializers
|
36
|
+
end
|
37
|
+
|
38
|
+
# Yields the Uses::Config instance governing this
|
39
|
+
# gem's behavior. You should call this in an intializer.
|
40
|
+
# See Uses::Config for what options exist
|
41
|
+
def self.config
|
42
|
+
@@config ||= Uses::Config.new
|
43
|
+
yield(@@config) if block_given?
|
44
|
+
@@config
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
require_relative "uses/version"
|
49
|
+
require_relative "uses/method"
|
data/uses.gemspec
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require_relative "lib/uses/version"
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "uses"
|
5
|
+
spec.version = Uses::VERSION
|
6
|
+
spec.authors = ["Dave Copeland"]
|
7
|
+
spec.email = ["davec@naildrivin5.com"]
|
8
|
+
spec.summary = %q{Declare that one classes uses an instance of another to help your code be a bit more sustainable}
|
9
|
+
spec.homepage = "https://github.com/sustainable-rails/uses"
|
10
|
+
spec.license = "Hippocratic"
|
11
|
+
|
12
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.5.0")
|
13
|
+
|
14
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
15
|
+
spec.metadata["source_code_uri"] = "https://github.com/sustainable-rails/uses"
|
16
|
+
spec.metadata["changelog_uri"] = "https://github.com/sustainable-rails/uses/releases"
|
17
|
+
|
18
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
19
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
20
|
+
end
|
21
|
+
spec.bindir = "exe"
|
22
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
spec.require_paths = ["lib"]
|
24
|
+
|
25
|
+
spec.add_dependency("activesupport")
|
26
|
+
spec.add_development_dependency("rspec")
|
27
|
+
spec.add_development_dependency("rspec_junit_formatter")
|
28
|
+
spec.add_development_dependency("confidence-check")
|
29
|
+
end
|