instructor 0.8.1 → 0.8.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2b9502c71a49683be1cef4a3dcfb742f211dfe2ff9794eab5245999a52f6cfb2
4
- data.tar.gz: 2eb47f8d9394e41d8eee700ba494f72b883f64513b1be4b3137850281fb4a426
3
+ metadata.gz: 7e3514be63395d163fbbffaf7f7bbe99495e537bf8ac54ba3def4d521a034155
4
+ data.tar.gz: 7e12e5505209f56e7d1040a3d095155c376c29bcb7afc713b6080897785cd215
5
5
  SHA512:
6
- metadata.gz: d17a0f57c140693e5159056c9c888d8404438b2b2ba31db9a71b5dc438e6f086d1b48e38a7ccc86fadc1906414a7ff1cb9d0adbb09869a02b684f3d95cb37bcd
7
- data.tar.gz: ee139f78a6327ed94dbf8a0e8269e37aa5d793475d09ece4ebf38b273c9793519944f5ed38178bfe3c42236018ee9ec8faf14ca57221061a74da0b09e284530e
6
+ metadata.gz: 1ad95ed38f49b3ae1b3cb5ecdc96e907c90c879a0ef9143083beedf16df8e08c0a4f8bf7a64ea508c59738567ce90c921aebf9fd1b56caeb612dbd848dc43ebf
7
+ data.tar.gz: 44dcf5d4a5ba8cf0a89fb2015b6fa7f5f13f8ac5ea4e379d7d70951056a604339cc44551e6fb399b3cc37ec2206e4ca6d8eaa450fcd8798434db4a80c31dc94c
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "concerns/callbacks"
4
+ require_relative "concerns/defaults"
5
+ require_relative "concerns/attributes"
6
+ require_relative "concerns/arguments"
7
+ require_relative "concerns/options"
8
+ require_relative "concerns/core"
9
+ require_relative "concerns/string"
10
+
11
+ module Instructor
12
+ class Base
13
+ include ShortCircuIt
14
+ include Technologic
15
+ include ActiveModel::Model
16
+ include ActiveModel::Validations::Callbacks
17
+ include Instructor::Callbacks
18
+ include Instructor::Defaults
19
+ include Instructor::Attributes
20
+ include Instructor::Arguments
21
+ include Instructor::Options
22
+ include Instructor::Core
23
+ include Instructor::String
24
+ end
25
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Arguments describe input required by an Instructor.
4
+ module Instructor
5
+ module Arguments
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_arguments, instance_writer: false, default: {}
10
+ set_callback :initialize, :after do
11
+ missing_arguments = _arguments.reject do |argument, options|
12
+ options[:allow_nil] ? input.key?(argument) : !input[argument].nil?
13
+ end
14
+
15
+ missing = missing_arguments.keys
16
+
17
+ raise ArgumentError, "Missing #{"argument".pluralize(missing.length)}: #{missing.join(", ")}" if missing.any?
18
+ end
19
+ end
20
+
21
+ class_methods do
22
+ def inherited(base)
23
+ dup = _arguments.dup
24
+ base._arguments = dup.each { |k, v| dup[k] = v.dup }
25
+ super
26
+ end
27
+
28
+ private
29
+
30
+ def argument(argument, allow_nil: true)
31
+ _arguments[argument] = { allow_nil: allow_nil }
32
+ define_attribute argument
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An Instructor's attributes provide accessors to the input data it was initialized with.
4
+ module Instructor
5
+ module Attributes
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveModel::AttributeMethods
10
+
11
+ class_attribute :_attributes, instance_writer: false, default: []
12
+ end
13
+
14
+ class_methods do
15
+ def inherited(base)
16
+ base._attributes = _attributes.dup
17
+ super
18
+ end
19
+
20
+ private
21
+
22
+ def define_attribute(attribute)
23
+ _attributes << attribute
24
+
25
+ attr_accessor attribute
26
+ define_attribute_methods attribute
27
+ end
28
+ alias_method :attribute, :define_attribute
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Callbacks provide an extensible mechanism for hooking into an Instructor.
4
+ module Instructor
5
+ module Callbacks
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ include ActiveSupport::Callbacks
10
+ define_callbacks :initialize
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ # An Instructor accepts input represented by arguments and options which initialize it.
4
+ module Instructor
5
+ module Core
6
+ extend ActiveSupport::Concern
7
+
8
+ attr_reader :input
9
+
10
+ def initialize(**input)
11
+ @input = input
12
+ run_callbacks(:initialize) do
13
+ input.each { |key, value| __send__("#{key}=".to_sym, value) }
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Defaults allow attributes to be initialized with a value if none have been provided.
4
+ module Instructor
5
+ module Defaults
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_defaults, instance_writer: false, default: {}
10
+ end
11
+
12
+ class_methods do
13
+ def inherited(base)
14
+ dup = _defaults.dup
15
+ base._defaults = dup.each { |k, v| dup[k] = v.dup }
16
+ super
17
+ end
18
+
19
+ private
20
+
21
+ def define_default(attribute, static: nil, &block)
22
+ _defaults[attribute] = Value.new(static: static, &block)
23
+ end
24
+ end
25
+
26
+ class Value
27
+ def initialize(static: nil, &block)
28
+ @value = (static.nil? && block_given?) ? block : static
29
+ end
30
+
31
+ def value
32
+ (@value.respond_to?(:call) ? instance_eval(&@value) : @value).dup
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Options describe input which may be provided to define or override default values.
4
+ module Instructor
5
+ module Options
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ class_attribute :_options, instance_writer: false, default: []
10
+
11
+ set_callback :initialize, :after do
12
+ _options.each do |option|
13
+ next unless _defaults.key?(option)
14
+
15
+ public_send("#{option}=".to_sym, _defaults[option].value) if public_send(option).nil?
16
+ end
17
+ end
18
+ end
19
+
20
+ class_methods do
21
+ def inherited(base)
22
+ base._options = _options.dup
23
+ super
24
+ end
25
+
26
+ private
27
+
28
+ def option(option, default: nil, &block)
29
+ _options << option
30
+ define_attribute option
31
+ define_default option, static: default, &block
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Formats the Instructor as a string.
4
+ module Instructor
5
+ module String
6
+ extend ActiveSupport::Concern
7
+
8
+ def to_s
9
+ string_for(__method__)
10
+ end
11
+
12
+ def inspect
13
+ string_for(__method__)
14
+ end
15
+
16
+ private
17
+
18
+ def stringable_attributes
19
+ self.class._attributes
20
+ end
21
+
22
+ def string_for(method)
23
+ "#<#{self.class.name} #{attribute_string(method)}>"
24
+ end
25
+
26
+ def attribute_string(method)
27
+ stringable_attribute_values.map { |attribute, value| "#{attribute}=#{value.public_send(method)}" }.join(" ")
28
+ end
29
+
30
+ def stringable_attribute_values
31
+ stringable_attributes.each_with_object({}) { |attribute, result| result[attribute] = safe_send(attribute) }
32
+ end
33
+
34
+ def safe_send(method)
35
+ public_send(method)
36
+ rescue StandardError
37
+ nil
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.argument`
4
+ #
5
+ # class Example < Instructor::Base
6
+ # argument :foo
7
+ # argument :bar, allow_nil: false
8
+ # end
9
+ #
10
+ # RSpec.describe Example, type: :instructor do
11
+ # subject { described_class.new(**input) }
12
+ #
13
+ # let(:input) { {} }
14
+ #
15
+ # it { is_expected.to define_argument :foo }
16
+ # it { is_expected.to define_argument :bar, allow_nil: false }
17
+ # end
18
+
19
+ RSpec::Matchers.define :define_argument do |argument, allow_nil: true|
20
+ match { |instance| expect(instance._arguments[argument]).to eq(allow_nil: allow_nil) }
21
+ description { "define argument #{argument}" }
22
+ failure_message do
23
+ "expected #{described_class} to define argument #{argument} #{prohibit_nil_description unless allow_nil}"
24
+ end
25
+
26
+ def prohibit_nil_description
27
+ "and prohibit a nil value"
28
+ end
29
+ end
@@ -0,0 +1,21 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.attribute`
4
+ #
5
+ # class Example < Instructor::Base
6
+ # attribute :foo
7
+ # end
8
+ #
9
+ # RSpec.describe Example, type: :instructor do
10
+ # subject { described_class.new(**input) }
11
+ #
12
+ # let(:input) { {} }
13
+ #
14
+ # it { is_expected.to define_attribute :foo }
15
+ # end
16
+
17
+ RSpec::Matchers.define :define_attribute do |attribute|
18
+ match { |instance| expect(instance._attributes).to include attribute }
19
+ description { "define attribute #{attribute}" }
20
+ failure_message { "expected #{described_class} to defines attribute #{attribute}" }
21
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ # RSpec matcher that tests usage of `.option`
4
+ #
5
+ # class Example < Instructor::Base
6
+ # option :foo
7
+ # option :bar, default: :baz
8
+ # option(:gaz) { :haz }
9
+ # end
10
+ #
11
+ # RSpec.describe Example, type: :instructor do
12
+ # subject { described_class.new(**input) }
13
+ #
14
+ # let(:input) { {} }
15
+ #
16
+ # it { is_expected.to define_option :foo }
17
+ # it { is_expected.to define_option :bar, default: :baz }
18
+ # it { is_expected.to define_option :gaz, default: :haz }
19
+ # end
20
+
21
+ RSpec::Matchers.define :define_option do |option, default: nil|
22
+ match do |instance|
23
+ expect(instance._defaults[option]&.value).to eq default
24
+ expect(instance._options).to include option
25
+ end
26
+ description { "define option #{option}" }
27
+ failure_message { "expected #{described_class} to define option #{option} #{for_default(default)}" }
28
+
29
+ def for_default(default)
30
+ return "without a default value" if default.nil?
31
+
32
+ "with default value #{default}"
33
+ end
34
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "custom_matchers/define_argument"
4
+ require_relative "custom_matchers/define_attribute"
5
+ require_relative "custom_matchers/define_option"
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ if defined?(Shoulda::Matchers::ActiveModel)
4
+ RSpec.configure do |config|
5
+ config.include(Shoulda::Matchers::ActiveModel, type: :instructor)
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "custom_matchers"
4
+ require_relative "shoulda_matcher_helper"
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Instructor
4
4
  # This constant is managed by spicerack
5
- VERSION = "0.8.1"
5
+ VERSION = "0.8.2"
6
6
  end
data/lib/instructor.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "active_support"
4
+ require "active_model"
5
+
6
+ require "short_circu_it"
7
+ require "technologic"
4
8
 
5
9
  require "instructor/version"
6
10
 
7
- module Instructor
8
- # Your code goes here...
9
- end
11
+ require "instructor/base"
12
+
13
+ module Instructor; end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: instructor
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.1
4
+ version: 0.8.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Garside
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-05-12 00:00:00.000000000 Z
11
+ date: 2019-05-13 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - "~>"
25
25
  - !ruby/object:Gem::Version
26
26
  version: 5.2.1
27
+ - !ruby/object:Gem::Dependency
28
+ name: activemodel
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 5.2.1
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 5.2.1
27
41
  description: Input structure base object for capturing and validating input with a
28
42
  nice DSL
29
43
  email:
@@ -35,6 +49,20 @@ files:
35
49
  - LICENSE.txt
36
50
  - README.md
37
51
  - lib/instructor.rb
52
+ - lib/instructor/base.rb
53
+ - lib/instructor/concerns/arguments.rb
54
+ - lib/instructor/concerns/attributes.rb
55
+ - lib/instructor/concerns/callbacks.rb
56
+ - lib/instructor/concerns/core.rb
57
+ - lib/instructor/concerns/defaults.rb
58
+ - lib/instructor/concerns/options.rb
59
+ - lib/instructor/concerns/string.rb
60
+ - lib/instructor/custom_matchers.rb
61
+ - lib/instructor/custom_matchers/define_argument.rb
62
+ - lib/instructor/custom_matchers/define_attribute.rb
63
+ - lib/instructor/custom_matchers/define_option.rb
64
+ - lib/instructor/shoulda_matcher_helper.rb
65
+ - lib/instructor/spec_helper.rb
38
66
  - lib/instructor/version.rb
39
67
  homepage: https://www.freshly.com
40
68
  licenses: