sinclair 1.6.5 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (46) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +2 -2
  3. data/Dockerfile +2 -2
  4. data/README.md +3 -1
  5. data/config/check_specs.yml +3 -0
  6. data/config/yardstick.yml +7 -1
  7. data/lib/sinclair/config.rb +46 -0
  8. data/lib/sinclair/config_class.rb +15 -0
  9. data/lib/sinclair/configurable.rb +8 -0
  10. data/lib/sinclair/matchers/add_class_method.rb +16 -13
  11. data/lib/sinclair/matchers/add_class_method_to.rb +9 -23
  12. data/lib/sinclair/matchers/add_instance_method.rb +16 -18
  13. data/lib/sinclair/matchers/add_instance_method_to.rb +12 -16
  14. data/lib/sinclair/matchers/add_method.rb +39 -30
  15. data/lib/sinclair/matchers/add_method_to.rb +4 -56
  16. data/lib/sinclair/matchers/base.rb +45 -0
  17. data/lib/sinclair/matchers/change_class_method.rb +42 -0
  18. data/lib/sinclair/matchers/change_class_method_on.rb +64 -0
  19. data/lib/sinclair/matchers/change_instance_method.rb +42 -0
  20. data/lib/sinclair/matchers/change_instance_method_on.rb +98 -0
  21. data/lib/sinclair/matchers/change_method_on.rb +25 -0
  22. data/lib/sinclair/matchers/method_to.rb +82 -0
  23. data/lib/sinclair/matchers.rb +38 -30
  24. data/lib/sinclair/options/class_methods.rb +95 -0
  25. data/lib/sinclair/options.rb +10 -60
  26. data/lib/sinclair/version.rb +1 -1
  27. data/sinclair.gemspec +12 -12
  28. data/spec/integration/readme/my_class_spec.rb +1 -1
  29. data/spec/integration/yard/sinclair/config_spec.rb +27 -0
  30. data/spec/integration/yard/sinclair/options_parser_spec.rb +9 -0
  31. data/spec/lib/sinclair/config_class_spec.rb +1 -33
  32. data/spec/lib/sinclair/config_spec.rb +71 -0
  33. data/spec/lib/sinclair/matchers/add_class_method_to_spec.rb +40 -16
  34. data/spec/lib/sinclair/matchers/add_instance_method_to_spec.rb +36 -12
  35. data/spec/lib/sinclair/matchers/change_class_method_on_spec.rb +138 -0
  36. data/spec/lib/sinclair/matchers/change_class_method_spec.rb +38 -0
  37. data/spec/lib/sinclair/matchers/change_instance_method_on_spec.rb +149 -0
  38. data/spec/lib/sinclair/matchers/change_instance_method_spec.rb +38 -0
  39. data/spec/lib/sinclair/matchers_spec.rb +30 -0
  40. data/spec/lib/sinclair/options/class_methods_spec.rb +255 -0
  41. data/spec/lib/sinclair/options_spec.rb +28 -237
  42. data/spec/support/models/builder_options.rb +7 -0
  43. data/spec/support/models/login_configurable.rb +7 -0
  44. data/spec/support/models/open_options.rb +7 -0
  45. data/spec/support/shared_examples/config.rb +48 -0
  46. metadata +43 -28
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Matchers
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Checks if a class method was changed
9
+ # by the call of a block
10
+ #
11
+ # This is used with a RSpec DSL method
12
+ # change_class_method(method_name).on(class_object)
13
+ class ChangeClassMethodOn < ChangeMethodOn
14
+ # @param [Class] klass
15
+ # Class where the class method should be added to
16
+ #
17
+ # @param method_name [SYmbol,String] method name
18
+ def initialize(target, method_name)
19
+ @klass = target
20
+ super(method_name)
21
+ end
22
+
23
+ # Return expectaton description
24
+ #
25
+ # @return [String]
26
+ def description
27
+ "change class method '#{method_name}' on #{klass}"
28
+ end
29
+
30
+ # Returns message on expectation failure
31
+ #
32
+ # @return [String]
33
+ def failure_message_for_should
34
+ "expected class method '#{method_name}' to be changed on #{klass} but " \
35
+ "#{initial_state ? "it didn't" : "it didn't exist"}"
36
+ end
37
+
38
+ # Returns message on expectation failure for negative expectation
39
+ #
40
+ # @return [String]
41
+ def failure_message_for_should_not
42
+ "expected class method '#{method_name}' not to be changed on #{klass} but it was"
43
+ end
44
+
45
+ private
46
+
47
+ # Checks if class has instance method defined
48
+ #
49
+ # @return [Boolean]
50
+ def state
51
+ klass.methods(false).include?(method_name.to_sym) \
52
+ && klass.method(method_name)
53
+ end
54
+
55
+ # Raises when block was not given
56
+ #
57
+ # @raise SyntaxError
58
+ def raise_block_syntax_error
59
+ raise SyntaxError, 'Block not received by the `change_class_method_on` matcher. ' \
60
+ 'Perhaps you want to use `{ ... }` instead of do/end?'
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Matchers
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # AddInstanceMethod is able to build an instance of
9
+ # {Sinclair::Matchers::ChangeInstanceMethodOn}
10
+ class ChangeInstanceMethod < Base
11
+ include AddMethod
12
+
13
+ # @api public
14
+ #
15
+ # Builds final matcher
16
+ #
17
+ # @return [Sinclair::Matchers::ChangeInstanceMethodOn]
18
+ alias on to
19
+
20
+ private
21
+
22
+ # @private
23
+ #
24
+ # Error description on wrong usage
25
+ #
26
+ # @return String
27
+ def matcher_error
28
+ 'You should specify which instance the method is being changed on' \
29
+ "change_method(:#{method_name}).on(instance)"
30
+ end
31
+
32
+ # @private
33
+ #
34
+ # Class of the real matcher
35
+ #
36
+ # @return [Class<Sinclair::Matchers::Base>]
37
+ def add_method_to_class
38
+ ChangeInstanceMethodOn
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,98 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Matchers
5
+ # @api private
6
+ # @author darthjee
7
+ #
8
+ # Checks if a method was changed
9
+ # by the call of a block
10
+ #
11
+ # This is used with a RSpec DSL method
12
+ # change_method(method_name).on(class_object)
13
+ class ChangeInstanceMethodOn < ChangeMethodOn
14
+ # @overload initialize(klass, method_name)
15
+ # @param [Class] klass
16
+ # class where the method should be added to
17
+ #
18
+ # @overload initialize(instance, method_name)
19
+ # @param [Object] instance
20
+ # instance of the class where the method should be added to
21
+ #
22
+ # @param method_name [Symbol,String] method name
23
+ def initialize(target, method_name)
24
+ if target.is_a?(Class)
25
+ @klass = target
26
+ else
27
+ @instance = target
28
+ end
29
+
30
+ super(method_name)
31
+ end
32
+
33
+ # Returnst expectaton description
34
+ #
35
+ # @return [String]
36
+ def description
37
+ "change method '#{method_name}' on #{klass} instances"
38
+ end
39
+
40
+ # Returns message on expectation failure
41
+ #
42
+ # @return [String]
43
+ def failure_message_for_should
44
+ "expected '#{method_name}' to be changed on #{klass} but " \
45
+ "#{initial_state ? "it didn't" : "it didn't exist"}"
46
+ end
47
+
48
+ # Returns message on expectation failure for negative expectation
49
+ #
50
+ # @return [String]
51
+ def failure_message_for_should_not
52
+ "expected '#{method_name}' not to be changed on #{klass} but it was"
53
+ end
54
+
55
+ protected
56
+
57
+ # @method instance
58
+ # @api private
59
+ # @private
60
+ #
61
+ # Instance of the class where the method should be added
62
+ #
63
+ # @return [Object]
64
+ attr_reader :instance
65
+
66
+ # @private
67
+ #
68
+ # Class to be analised
69
+ #
70
+ # @return [Class]
71
+ def klass
72
+ @klass ||= instance.class
73
+ end
74
+
75
+ private
76
+
77
+ # @private
78
+ #
79
+ # Checks if class has instance method defined
80
+ #
81
+ # @return [Boolean]
82
+ def state
83
+ klass.method_defined?(method_name) &&
84
+ klass.instance_method(method_name)
85
+ end
86
+
87
+ # @private
88
+ #
89
+ # Raises when block was not given
90
+ #
91
+ # @raise SyntaxError
92
+ def raise_block_syntax_error
93
+ raise SyntaxError, 'Block not received by the `change_instance_method_on` matcher. ' \
94
+ 'Perhaps you want to use `{ ... }` instead of do/end?'
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Matchers
5
+ # @api private
6
+ # @author darthjee
7
+ # @abstract
8
+ #
9
+ # Base class for change_method_on matcher
10
+ class ChangeMethodOn < Base
11
+ include MethodTo
12
+
13
+ private
14
+
15
+ # @private
16
+ #
17
+ # Checks if a method was changed
18
+ #
19
+ # @return Boolean
20
+ def check
21
+ initial_state && initial_state != final_state
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Sinclair
4
+ module Matchers
5
+ # @api private
6
+ #
7
+ # Common methods on final matchers
8
+ module MethodTo
9
+ # Used for other versions of rspec
10
+ #
11
+ # Some versions call failure_message, others
12
+ # call failure_message_for_should
13
+ #
14
+ # @return [String]
15
+ def failure_message
16
+ failure_message_for_should
17
+ end
18
+
19
+ # Used for other versions of rspec
20
+ #
21
+ # Some versions call failure_message_when_negated, others
22
+ # call failure_message_for_should_not
23
+ #
24
+ # @return [String]
25
+ def failure_message_when_negated
26
+ failure_message_for_should_not
27
+ end
28
+
29
+ # Checks if expectation is true or not
30
+ #
31
+ # @return [Boolean] expectation check
32
+ def matches?(event_proc)
33
+ return false unless event_proc.is_a?(Proc)
34
+
35
+ raise_block_syntax_error if block_given?
36
+ perform_change(event_proc)
37
+ check
38
+ end
39
+
40
+ protected
41
+
42
+ # @method klass
43
+ # @api private
44
+ # @private
45
+ #
46
+ # Class where class method should be added to
47
+ #
48
+ # @return [Class]
49
+ attr_reader :klass
50
+
51
+ private
52
+
53
+ # @method initial_state
54
+ # @api private
55
+ # @private
56
+ #
57
+ # State before running the block
58
+ #
59
+ # @return [Object]
60
+
61
+ # @method final_state
62
+ # @api private
63
+ # @private
64
+ #
65
+ # State after running the block
66
+ #
67
+ # @return [Object]
68
+ attr_reader :initial_state, :final_state
69
+
70
+ # @private
71
+ #
72
+ # Call block to check if it aded a method or not
73
+ #
74
+ # @return [Boolan]
75
+ def perform_change(event_proc)
76
+ @initial_state = state
77
+ event_proc.call
78
+ @final_state = state
79
+ end
80
+ end
81
+ end
82
+ end
@@ -7,33 +7,21 @@ class Sinclair
7
7
  # Matchers module will have the DSL to be included in RSpec in order to have
8
8
  # access to the matchers
9
9
  #
10
- # @example
11
- # RSpec.configure do |config|
12
- # config.include Sinclair::Matchers
13
- # end
14
- #
15
- # class MyModel
16
- # end
17
- #
18
- # RSpec.describe 'my test' do
19
- # let(:klass) { Class.new(MyModel) }
20
- # let(:builder) { Sinclair.new(klass) }
21
- #
22
- # before do
23
- # builder.add_method(:class_name, 'self.class.name')
24
- # end
25
- #
26
- # it do
27
- # expect { builder.build }.to add_method(:class_name).to(klass)
28
- # end
29
- # end
10
+ # @example (see Sinclair::Matchers::AddMethod#to)
30
11
  module Matchers
31
- autoload :AddMethod, 'sinclair/matchers/add_method'
32
- autoload :AddInstanceMethod, 'sinclair/matchers/add_instance_method'
33
- autoload :AddClassMethod, 'sinclair/matchers/add_class_method'
34
- autoload :AddMethodTo, 'sinclair/matchers/add_method_to'
35
- autoload :AddInstanceMethodTo, 'sinclair/matchers/add_instance_method_to'
36
- autoload :AddClassMethodTo, 'sinclair/matchers/add_class_method_to'
12
+ autoload :Base, 'sinclair/matchers/base'
13
+ autoload :AddInstanceMethod, 'sinclair/matchers/add_instance_method'
14
+ autoload :AddClassMethod, 'sinclair/matchers/add_class_method'
15
+ autoload :AddMethod, 'sinclair/matchers/add_method'
16
+ autoload :AddMethodTo, 'sinclair/matchers/add_method_to'
17
+ autoload :AddInstanceMethodTo, 'sinclair/matchers/add_instance_method_to'
18
+ autoload :AddClassMethodTo, 'sinclair/matchers/add_class_method_to'
19
+ autoload :ChangeClassMethod, 'sinclair/matchers/change_class_method'
20
+ autoload :ChangeInstanceMethod, 'sinclair/matchers/change_instance_method'
21
+ autoload :ChangeMethodOn, 'sinclair/matchers/change_method_on'
22
+ autoload :ChangeClassMethodOn, 'sinclair/matchers/change_class_method_on'
23
+ autoload :ChangeInstanceMethodOn, 'sinclair/matchers/change_instance_method_on'
24
+ autoload :MethodTo, 'sinclair/matchers/method_to'
37
25
 
38
26
  # DSL to AddInstanceMethod
39
27
  #
@@ -41,8 +29,8 @@ class Sinclair
41
29
  # @example (see Sinclair::Matchers::AddInstanceMethod#to)
42
30
  #
43
31
  # @return [AddInstanceMethod] RSpec Matcher
44
- def add_method(method)
45
- Sinclair::Matchers::AddInstanceMethod.new(method)
32
+ def add_method(method_name)
33
+ Sinclair::Matchers::AddInstanceMethod.new(method_name)
46
34
  end
47
35
 
48
36
  # DSL to AddClassMethod
@@ -51,8 +39,28 @@ class Sinclair
51
39
  # @example (see Sinclair::Matchers::AddClassMethod#to)
52
40
  #
53
41
  # @return [AddClassMethod] RSpec Matcher
54
- def add_class_method(method)
55
- Sinclair::Matchers::AddClassMethod.new(method)
42
+ def add_class_method(method_name)
43
+ Sinclair::Matchers::AddClassMethod.new(method_name)
44
+ end
45
+
46
+ # DSL to ChangeInstanceMethod
47
+ #
48
+ # @example (see Sinclair::Matchers)
49
+ # @example (see Sinclair::Matchers::ChangeInstanceMethod#to)
50
+ #
51
+ # @return [ChangeInstanceMethod] RSpec Matcher
52
+ def change_method(method_name)
53
+ Sinclair::Matchers::ChangeInstanceMethod.new(method_name)
54
+ end
55
+
56
+ # DSL to ChangeClassMethod
57
+ #
58
+ # @example (see Sinclair::Matchers)
59
+ # @example (see Sinclair::Matchers::ChangeClassMethod#to)
60
+ #
61
+ # @return [ChangeClassMethod] RSpec Matcher
62
+ def change_class_method(method_name)
63
+ Sinclair::Matchers::ChangeClassMethod.new(method_name)
56
64
  end
57
65
  end
58
66
  end
@@ -0,0 +1,95 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'set'
4
+
5
+ class Sinclair
6
+ class Options
7
+ # Class Methods for {Sinclai::Options}
8
+ module ClassMethods
9
+ # @api private
10
+ #
11
+ # returns invalid options
12
+ #
13
+ # @return [Array<Symbol>]
14
+ def invalid_options_in(names)
15
+ names.map(&:to_sym) - allowed_options.to_a
16
+ end
17
+
18
+ # @api private
19
+ #
20
+ # Allow new option
21
+ #
22
+ # This does not create the method
23
+ #
24
+ # @param name [String,Symbol] options to be allowed
25
+ #
26
+ # @return [Set<Symbol>]
27
+ def allow(name)
28
+ allowed_options << name.to_sym
29
+ end
30
+
31
+ # @api private
32
+ # @private
33
+ #
34
+ # Options allowed when initializing options
35
+ #
36
+ # @return [Set<Symbol>]
37
+ def allowed_options
38
+ @allowed_options ||= superclass.try(:allowed_options).dup || Set.new
39
+ end
40
+
41
+ # @api private
42
+ #
43
+ # checks if class skips initialization validation
44
+ #
45
+ # @return [TrueClass,FalseClass]
46
+ def skip_validation?
47
+ @skip_validation ||= superclass.try(:skip_validation?) || false
48
+ end
49
+
50
+ # @api public
51
+ #
52
+ # Add available options
53
+ #
54
+ # @example (see Options)
55
+ #
56
+ # @return (see Sinclair#build)
57
+ #
58
+ # @overload with_options(*options)
59
+ # @param options [Array<Symbol>] list of accepted
60
+ # options
61
+ # @overload with_options(*options, **defaults)
62
+ # @param options [Array<Symbol>] list of accepted
63
+ # options
64
+ # @param defaults [Hash<Symbol,Object>] default options
65
+ # hash
66
+ def with_options(*options)
67
+ Builder.new(self, *options).build
68
+ end
69
+
70
+ # @api public
71
+ #
72
+ # Changes class to skip attributes validation
73
+ #
74
+ # when initializing options, options
75
+ # will accept any arguments when validation
76
+ # is skipped
77
+ #
78
+ # @return [TrueClass]
79
+ #
80
+ # @example
81
+ # class BuilderOptions < Sinclair::Options
82
+ # with_options :name
83
+ #
84
+ # skip_validation
85
+ # end
86
+ # options = BuilderOptions.new(name: 'Joe', age: 10)
87
+ #
88
+ # options.name # returns 'Joe'
89
+ # options.try(:age) # returns nil
90
+ def skip_validation
91
+ @skip_validation = true
92
+ end
93
+ end
94
+ end
95
+ end
@@ -20,64 +20,10 @@ class Sinclair
20
20
  # options.port # returns 8080
21
21
  # options.protocol # returns 'https'
22
22
  class Options
23
- autoload :Builder, 'sinclair/options/builder'
23
+ autoload :Builder, 'sinclair/options/builder'
24
+ autoload :ClassMethods, 'sinclair/options/class_methods'
24
25
 
25
- class << self
26
- # @api private
27
- #
28
- # returns invalid options
29
- #
30
- # @return [Array<Symbol>]
31
- def invalid_options_in(names)
32
- names.map(&:to_sym) - allowed_options.to_a
33
- end
34
-
35
- # @api private
36
- #
37
- # Allow new option
38
- #
39
- # This does not create the method
40
- #
41
- # @param name [String,Symbol] options to be allowed
42
- #
43
- # @return [Set<Symbol>]
44
- def allow(name)
45
- allowed_options << name.to_sym
46
- end
47
-
48
- # @api private
49
- # @private
50
- #
51
- # Options allowed when initializing options
52
- #
53
- # @return [Set<Symbol>]
54
- def allowed_options
55
- @allowed_options ||= superclass.try(:allowed_options).dup || Set.new
56
- end
57
-
58
- private
59
-
60
- # @api public
61
- # @!visibility public
62
- #
63
- # Add available options
64
- #
65
- # @example (see Options)
66
- #
67
- # @return (see Sinclair#build)
68
- #
69
- # @overload with_options(*options)
70
- # @param options [Array<Symbol>] list of accepted
71
- # options
72
- # @overload with_options(*options, **defaults)
73
- # @param options [Array<Symbol>] list of accepted
74
- # options
75
- # @param defaults [Hash<Symbol,Object>] default options
76
- # hash
77
- def with_options(*options)
78
- Builder.new(self, *options).build
79
- end
80
- end
26
+ extend ClassMethods
81
27
 
82
28
  # @param options [Hash] hash with options (see {.options}, {.with_options})
83
29
  # @example (see Options)
@@ -108,7 +54,7 @@ class Sinclair
108
54
  # # protocol: 'https'
109
55
  # # }
110
56
  def to_h
111
- self.class.allowed_options.inject({}) do |hash, option|
57
+ allowed_options.inject({}) do |hash, option|
112
58
  hash.merge(option => public_send(option))
113
59
  end
114
60
  end
@@ -121,13 +67,15 @@ class Sinclair
121
67
  def ==(other)
122
68
  return false unless self.class == other.class
123
69
 
124
- self.class.allowed_options.all? do |name|
70
+ allowed_options.all? do |name|
125
71
  public_send(name) == other.public_send(name)
126
72
  end
127
73
  end
128
74
 
129
75
  private
130
76
 
77
+ delegate :allowed_options, :skip_validation?, :invalid_options_in, to: :class
78
+
131
79
  # @private
132
80
  # @api private
133
81
  #
@@ -137,7 +85,9 @@ class Sinclair
137
85
  #
138
86
  # @return [NilClass]
139
87
  def check_options(options)
140
- invalid_keys = self.class.invalid_options_in(options.keys)
88
+ return if skip_validation?
89
+
90
+ invalid_keys = invalid_options_in(options.keys)
141
91
 
142
92
  return if invalid_keys.empty?
143
93
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class Sinclair
4
- VERSION = '1.6.5'
4
+ VERSION = '1.7.0'
5
5
  end
data/sinclair.gemspec CHANGED
@@ -21,20 +21,20 @@ Gem::Specification.new do |gem|
21
21
 
22
22
  gem.add_runtime_dependency 'activesupport', '~> 5.2.0'
23
23
 
24
- gem.add_development_dependency 'bundler', '1.16.1'
25
- gem.add_development_dependency 'pry', '0.12.2'
26
- gem.add_development_dependency 'pry-nav', '0.3.0'
24
+ gem.add_development_dependency 'bundler', '2.3.20'
25
+ gem.add_development_dependency 'pry', '0.14.1'
26
+ gem.add_development_dependency 'pry-nav', '1.0.0'
27
27
  gem.add_development_dependency 'rake', '13.0.1'
28
- gem.add_development_dependency 'reek', '5.6.0'
29
- gem.add_development_dependency 'rspec', '3.9.0'
30
- gem.add_development_dependency 'rspec-core', '3.9.1'
31
- gem.add_development_dependency 'rspec-expectations', '3.9.1'
32
- gem.add_development_dependency 'rspec-mocks', '3.9.1'
33
- gem.add_development_dependency 'rspec-support', '3.9.2'
28
+ gem.add_development_dependency 'reek', '6.0.3'
29
+ gem.add_development_dependency 'rspec', '3.11.0'
30
+ gem.add_development_dependency 'rspec-core', '3.11.0'
31
+ gem.add_development_dependency 'rspec-expectations', '3.11.0'
32
+ gem.add_development_dependency 'rspec-mocks', '3.11.1'
33
+ gem.add_development_dependency 'rspec-support', '3.11.0'
34
34
  gem.add_development_dependency 'rubocop', '0.80.1'
35
35
  gem.add_development_dependency 'rubocop-rspec', '1.38.1'
36
- gem.add_development_dependency 'rubycritic', '4.4.1'
37
- gem.add_development_dependency 'simplecov', '0.17.1'
38
- gem.add_development_dependency 'yard', '0.9.24'
36
+ gem.add_development_dependency 'rubycritic', '4.7.0'
37
+ gem.add_development_dependency 'simplecov', '0.21.2'
38
+ gem.add_development_dependency 'yard', '0.9.27'
39
39
  gem.add_development_dependency 'yardstick', '0.9.9'
40
40
  end
@@ -3,7 +3,7 @@
3
3
  require 'spec_helper'
4
4
 
5
5
  describe MyClass do
6
- subject(:model) { klass.new(attributes) }
6
+ subject(:model) { klass.new(**attributes) }
7
7
 
8
8
  let(:klass) { described_class }
9
9
  let(:name) { 'name' }
@@ -24,5 +24,32 @@ describe Sinclair::Config do
24
24
  .to eq('{"password":null,"username":"bob"}')
25
25
  end
26
26
  end
27
+
28
+ describe '#options' do
29
+ subject(:config) { configurable.config }
30
+
31
+ let(:configurable) { LoginConfigurable }
32
+
33
+ before do
34
+ LoginConfigurable.configure do |conf|
35
+ conf.username :some_username
36
+ conf.password :some_password
37
+ end
38
+ end
39
+
40
+ it 'returns options with correct values' do
41
+ expect(config.options.username).to eq(:some_username)
42
+ expect(config.options.password).to eq(:some_password)
43
+ end
44
+
45
+ context 'when merging with given attributes' do
46
+ subject(:options) { config.options(password: :correct_password) }
47
+
48
+ it 'returns options with custom values' do
49
+ expect(options.username).to eq(:some_username)
50
+ expect(options.password).to eq(:correct_password)
51
+ end
52
+ end
53
+ end
27
54
  end
28
55
  end