instructor 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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: