sinclair 1.6.5 → 1.7.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.
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