coppertone 0.0.3 → 0.0.4

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
  SHA1:
3
- metadata.gz: d50206e3df522c6100a46e2a6f64989d10da5774
4
- data.tar.gz: 773a9709e679f5d45e5a7a6f779112ffea0e9aff
3
+ metadata.gz: 98149a0b1ea7cda00c82becc04efa776fe05d04c
4
+ data.tar.gz: 4b5c342217d8ea99622fc3b7e0d75a2d49d008b5
5
5
  SHA512:
6
- metadata.gz: 2752ba8667d8f7d8458cfa0429a4140f59a42af7a7ad52881b5a75f1e4881d2b4038a5fb1a8ca22dff54ca7301941da42400b94f87ddf5b6972ffb9de3900c06
7
- data.tar.gz: 90cb6889a3ed66eb50663e0681ffc011641d20329c0ffb86b7d6af780d40189beb36fac80c78f4e1107295134d643a104bd7933c10ca99d265e693ff0590b90a
6
+ metadata.gz: 3e739ea5580ee84d251ffae5f170d1116e40a5c789b94320878727de04c4a3dc8e6ea00641e14a0454c8aba087b9579c133362b1075ca20d1a25cdbc3188e2ff
7
+ data.tar.gz: 89ce502d95dd1e60dafe3398d39788548f70758f3b435e3949f9fe7d62bc9356d12765b4898dd96d24eb6db76a6824dabbb74f0c5e05705c7ba015702092d9a3
data/.travis.yml CHANGED
@@ -5,5 +5,7 @@ rvm:
5
5
  - 2.0.0
6
6
  - 2.1.2
7
7
  - jruby-19mode
8
- - rbx
8
+ - rbx-2.2.10
9
+ allow_failures:
10
+ - rvm: rbx-2.2.10
9
11
 
@@ -1,11 +1,15 @@
1
1
  require 'coppertone/mechanism'
2
2
  require 'coppertone/qualifier'
3
+ require 'active_support/core_ext/module/delegation'
3
4
 
4
5
  module Coppertone
5
6
  # Instances of this class represent directive terms, as defined by the
6
7
  # SPF specification (see section 4.6.1).
7
8
  class Directive
8
9
  attr_reader :qualifier, :mechanism
10
+ delegate :context_dependent?, :dns_lookup_term?,
11
+ :includes_ptr?, to: :mechanism
12
+
9
13
  def initialize(qualifier, mechanism)
10
14
  @qualifier = qualifier
11
15
  @mechanism = mechanism
@@ -19,8 +23,9 @@ module Coppertone
19
23
  end
20
24
  end
21
25
 
22
- def context_dependent?
23
- mechanism.context_dependent?
26
+ def target_domain
27
+ fail NeedsContextError unless dns_lookup_term?
28
+ mechanism.target_domain
24
29
  end
25
30
 
26
31
  def all?
@@ -45,4 +45,8 @@ module Coppertone
45
45
  class TermLimitExceededError < PermerrorError; end
46
46
  class VoidLimitExceededError < PermerrorError; end
47
47
  class MXLimitExceededError < PermerrorError; end
48
+
49
+ # Raised when context is required to evaluate a value, but
50
+ # context is not available
51
+ class NeedsContextError < Coppertone::Error; end
48
52
  end
@@ -41,5 +41,10 @@ module Coppertone
41
41
  return false unless other.instance_of? self.class
42
42
  macro_text == other.macro_text
43
43
  end
44
+ alias_method :eql?, :==
45
+
46
+ def hash
47
+ macro_text.hash
48
+ end
44
49
  end
45
50
  end
@@ -27,6 +27,21 @@ module Coppertone
27
27
  return false unless domain_spec
28
28
  domain_spec.includes_ptr?
29
29
  end
30
+
31
+ def target_domain
32
+ fail Coppertone::NeedsContextError if context_dependent?
33
+ domain_spec.to_s
34
+ end
35
+
36
+ def ==(other)
37
+ return false unless other.instance_of? self.class
38
+ domain_spec == other.domain_spec
39
+ end
40
+ alias_method :eql?, :==
41
+
42
+ def hash
43
+ domain_spec.hash
44
+ end
30
45
  end
31
46
  end
32
47
  end
@@ -37,11 +37,6 @@ module Coppertone
37
37
  def handle_invalid_domain(_macro_context, _options)
38
38
  fail RecordParsingError
39
39
  end
40
-
41
- def ==(other)
42
- return false unless other.instance_of? self.class
43
- domain_spec == other.domain_spec
44
- end
45
40
  end
46
41
  end
47
42
  end
@@ -28,11 +28,6 @@ module Coppertone
28
28
  def handle_invalid_domain(_macro_context, _options)
29
29
  fail RecordParsingError
30
30
  end
31
-
32
- def ==(other)
33
- return false unless other.instance_of? self.class
34
- domain_spec == other.domain_spec
35
- end
36
31
  end
37
32
  end
38
33
  end
@@ -25,14 +25,6 @@ module Coppertone
25
25
  @ip_v6_cidr_length ||= 128
26
26
  end
27
27
 
28
- def cidr_length(macro_context)
29
- if macro_context.original_ip_v6?
30
- ip_v6_cidr_length
31
- else
32
- ip_v4_cidr_length
33
- end
34
- end
35
-
36
28
  def match?(macro_context, request_context)
37
29
  request_context.register_dns_lookup_term
38
30
  target_name = generate_target_name(macro_context, request_context)
@@ -47,6 +39,7 @@ module Coppertone
47
39
  def parse_argument(attributes)
48
40
  fail InvalidMechanismError if attributes.blank?
49
41
  cidr_matches = CIDR_REGEXP.match(attributes)
42
+ fail InvalidMechanismError unless cidr_matches
50
43
  macro_string, raw_ip_v4_cidr_length, raw_ip_v6_cidr_length =
51
44
  clean_matches(attributes, cidr_matches)
52
45
  process_matches(macro_string, raw_ip_v4_cidr_length,
@@ -65,14 +58,10 @@ module Coppertone
65
58
  end
66
59
 
67
60
  def clean_matches(attributes, cidr_matches)
68
- if cidr_matches
69
- raw_ip_v4_cidr_length = cidr_matches[2] unless cidr_matches[2].blank?
70
- raw_ip_v6_cidr_length = cidr_matches[4] unless cidr_matches[4].blank?
71
- term = cidr_matches[0]
72
- domain_spec_end = term.blank? ? -1 : (-1 - term.length)
73
- else
74
- domain_spec_end = -1
75
- end
61
+ raw_ip_v4_cidr_length = cidr_matches[2] unless cidr_matches[2].blank?
62
+ raw_ip_v6_cidr_length = cidr_matches[4] unless cidr_matches[4].blank?
63
+ term = cidr_matches[0]
64
+ domain_spec_end = term.blank? ? -1 : (-1 - term.length)
76
65
  macro_string = parse_domain_spec(attributes, domain_spec_end)
77
66
  [macro_string, raw_ip_v4_cidr_length, raw_ip_v6_cidr_length]
78
67
  end
@@ -110,6 +99,11 @@ module Coppertone
110
99
  ip_v4_cidr_length == other.ip_v4_cidr_length &&
111
100
  ip_v6_cidr_length == other.ip_v6_cidr_length
112
101
  end
102
+ alias_method :eql?, :==
103
+
104
+ def hash
105
+ domain_spec.hash ^ ip_v4_cidr_length.hash ^ ip_v6_cidr_length.hash
106
+ end
113
107
  end
114
108
  end
115
109
  end
@@ -21,15 +21,6 @@ module Coppertone
21
21
  RecordFinder.new(request_context.dns_client, target_name).record
22
22
  end
23
23
 
24
- def context_dependent_result?(request_context,
25
- macro_context =
26
- Coppertone::NullMacroContext.new)
27
- target_name =
28
- target_name_from_domain_spec(macro_context, request_context)
29
- included_record(request_context, target_name)
30
- .context_dependent_result?(request_context)
31
- end
32
-
33
24
  def self.label
34
25
  'include'
35
26
  end
@@ -2,7 +2,7 @@ module Coppertone
2
2
  class Mechanism # rubocop:disable Style/Documentation
3
3
  # Implements the ip4 mechanism.
4
4
  class IPMechanism < Mechanism
5
- attr_reader :ip_network
5
+ attr_reader :netblock
6
6
  def self.create(attributes)
7
7
  new(attributes)
8
8
  end
@@ -11,9 +11,9 @@ module Coppertone
11
11
  super(attributes)
12
12
  unless attributes.blank?
13
13
  attributes = attributes[1..-1] if attributes[0] == ':'
14
- @ip_network = parse_ip_network(attributes)
14
+ @netblock = parse_netblock(attributes)
15
15
  end
16
- fail Coppertone::InvalidMechanismError if @ip_network.nil?
16
+ fail Coppertone::InvalidMechanismError if @netblock.nil?
17
17
  end
18
18
 
19
19
  LEADING_ZEROES_IN_CIDR_REGEXP = /\/0\d/
@@ -29,7 +29,7 @@ module Coppertone
29
29
  IP_PARSE_ERROR = IPAddr::Error
30
30
  end
31
31
 
32
- def parse_ip_network(ip_as_s)
32
+ def parse_netblock(ip_as_s)
33
33
  validate_no_leading_zeroes_in_cidr(ip_as_s)
34
34
  addr, cidr_length, dual = ip_as_s.split('/')
35
35
  return nil if dual
@@ -43,13 +43,13 @@ module Coppertone
43
43
  def match?(macro_context, _request_context)
44
44
  ip = ip_for_match(macro_context)
45
45
  return false unless ip
46
- return false unless ip.ipv4? == @ip_network.ipv4?
47
- @ip_network.include?(ip)
46
+ return false unless ip.ipv4? == @netblock.ipv4?
47
+ @netblock.include?(ip)
48
48
  end
49
49
 
50
50
  def ==(other)
51
51
  return false unless other.instance_of? self.class
52
- ip_network == other.ip_network
52
+ netblock == other.netblock
53
53
  end
54
54
  end
55
55
  end
@@ -16,11 +16,9 @@ module Coppertone
16
16
  RedirectRecordFinder.new(self, macro_context, request_context).record
17
17
  end
18
18
 
19
- def context_dependent_result?(request_context,
20
- macro_context =
21
- Coppertone::NullMacroContext.new)
22
- included_record(macro_context, request_context)
23
- .context_dependent_result?(request_context)
19
+ def target_domain
20
+ fail NeedsContextError if context_dependent?
21
+ arguments
24
22
  end
25
23
 
26
24
  def self.label
@@ -1,12 +1,10 @@
1
1
  module Coppertone
2
2
  class Modifier # rubocop:disable Style/Documentation
3
3
  class Unknown < Modifier
4
- def self.create(attributes)
5
- new(attributes)
6
- end
7
-
8
- def initialize(attributes)
4
+ attr_reader :label
5
+ def initialize(label, attributes)
9
6
  super(attributes)
7
+ @label = label
10
8
  @macro_string = Coppertone::MacroString.new(attributes)
11
9
  rescue Coppertone::MacroStringParsingError
12
10
  raise Coppertone::InvalidModifierError
@@ -19,11 +17,6 @@ module Coppertone
19
17
  def includes_ptr?
20
18
  false
21
19
  end
22
-
23
- def self.label
24
- 'unknown'
25
- end
26
20
  end
27
- register(Coppertone::Modifier::Unknown)
28
21
  end
29
22
  end
@@ -20,7 +20,11 @@ module Coppertone
20
20
  return nil unless matches
21
21
  type = matches[1]
22
22
  attributes = matches[2]
23
- build(type, attributes) || build('unknown', attributes)
23
+ build(type, attributes) || build_unknown(type, attributes)
24
+ end
25
+
26
+ def self.build_unknown(type, attributes)
27
+ Coppertone::Modifier::Unknown.new(type, attributes)
24
28
  end
25
29
 
26
30
  attr_reader :arguments
@@ -28,8 +32,12 @@ module Coppertone
28
32
  @arguments = arguments
29
33
  end
30
34
 
35
+ def label
36
+ self.class.label
37
+ end
38
+
31
39
  def to_s
32
- "#{self.class.label}=#{arguments}"
40
+ "#{label}=#{arguments}"
33
41
  end
34
42
  end
35
43
  end
@@ -29,13 +29,12 @@ module Coppertone
29
29
  all_directive ? true : false
30
30
  end
31
31
 
32
- def default_result
33
- return Result.neutral unless all_directive
34
- Result.from_directive(all_directive)
35
- end
36
-
37
- def safe_to_include?
38
- include_all?
32
+ def dns_lookup_term_count
33
+ @dns_lookup_term_count ||=
34
+ begin
35
+ base = redirect.nil? ? 0 : 1
36
+ base + directives.select(&:dns_lookup_term?).size
37
+ end
39
38
  end
40
39
 
41
40
  def includes
@@ -51,32 +50,36 @@ module Coppertone
51
50
  @modifiers ||= @terms.select { |t| t.is_a?(Coppertone::Modifier) }
52
51
  end
53
52
 
54
- def find_redirect
55
- find_modifier(Coppertone::Modifier::Redirect)
56
- end
57
-
58
53
  def redirect
59
54
  @redirect ||= find_redirect
60
55
  end
61
56
 
62
- def exp
63
- @exp ||= find_modifier(Coppertone::Modifier::Exp)
57
+ def redirect_with_directives?
58
+ redirect && directives.any?
64
59
  end
65
60
 
66
- def context_dependent_result?(request_context)
61
+ def netblock_mechanisms
62
+ @netblock_mechanisms ||=
63
+ directives.select { |d| d.mechanism.is_a?(Coppertone::Mechanism::IPMechanism) }
64
+ end
65
+
66
+ def netblocks_only?
67
+ return false if redirect
68
+ directives.reject(&:all?).reject do |d|
69
+ d.mechanism.is_a?(Coppertone::Mechanism::IPMechanism)
70
+ end.empty?
71
+ end
72
+
73
+ def context_dependent_evaluation?
67
74
  return true if directives.exist?(&:context_dependent)
68
- return true if context_dependent_redirect_result?(request_context)
69
- return false if includes.blank?
70
- includes.exists? { |i| i.context_dependent_result?(request_context) }
75
+ redirect && redirect.context_dependent?
71
76
  end
72
77
 
73
- def context_dependent_redirect_result?(request_context)
74
- return false unless redirect
75
- redirect.context_dependent? ||
76
- redirect.context_dependent_result?(request_context)
78
+ def exp
79
+ @exp ||= find_modifier(Coppertone::Modifier::Exp)
77
80
  end
78
81
 
79
- def context_dependent_message?
82
+ def context_dependent_explanation?
80
83
  exp && exp.context_dependent?
81
84
  end
82
85
 
@@ -93,6 +96,10 @@ module Coppertone
93
96
  arr.first
94
97
  end
95
98
 
99
+ def find_redirect
100
+ find_modifier(Coppertone::Modifier::Redirect)
101
+ end
102
+
96
103
  def self.version_str
97
104
  Coppertone::RecordTermParser::VERSION_STR
98
105
  end
@@ -10,22 +10,16 @@ module Coppertone
10
10
  RECORD_REGEXP.match(text.strip) ? true : false
11
11
  end
12
12
 
13
- attr_reader :terms
13
+ attr_reader :text, :terms
14
14
  def initialize(text)
15
15
  fail RecordParsingError unless self.class.record?(text)
16
16
  fail RecordParsingError unless ALLOWED_CHARACTERS.match(text)
17
- @terms = term_tokens(text).map { |token| parse_token(token) }
17
+ @text = text
18
+ @terms = Coppertone::TermsParser.new(terms_segment).terms
18
19
  end
19
20
 
20
- def term_tokens(text)
21
- text_without_prefix = text[VERSION_STR.length..-1]
22
- text_without_prefix.strip.split(/ /).select { |s| !s.blank? }
23
- end
24
-
25
- def parse_token(token)
26
- term = Term.build_from_token(token)
27
- fail RecordParsingError unless term
28
- term
21
+ def terms_segment
22
+ text[VERSION_STR.length..-1].strip
29
23
  end
30
24
  end
31
25
  end
@@ -0,0 +1,23 @@
1
+ module Coppertone
2
+ # Parses a un-prefixed string into terms
3
+ class TermsParser
4
+ attr_reader :text
5
+ def initialize(text)
6
+ @text = text
7
+ end
8
+
9
+ def terms
10
+ tokens.map { |token| parse_token(token) }
11
+ end
12
+
13
+ def tokens
14
+ text.split(/ /).select { |s| !s.blank? }
15
+ end
16
+
17
+ def parse_token(token)
18
+ term = Term.build_from_token(token)
19
+ fail RecordParsingError unless term
20
+ term
21
+ end
22
+ end
23
+ end
@@ -26,6 +26,11 @@ module Coppertone
26
26
  NO_DASH_REGEXP.match(l) || DASH_REGEXP.match(l)
27
27
  end
28
28
 
29
+ def self.valid_ldh_domain?(domain)
30
+ return false unless valid?(domain)
31
+ to_labels(domain).all? { |l| valid_hostname_label?(l) }
32
+ end
33
+
29
34
  def self.valid_label?(l)
30
35
  (l.length >= 0) && (l.length <= 63)
31
36
  end
@@ -1,3 +1,3 @@
1
1
  module Coppertone # rubocop:disable Style/Documentation
2
- VERSION = '0.0.3'.freeze
2
+ VERSION = '0.0.4'.freeze
3
3
  end
data/lib/coppertone.rb CHANGED
@@ -42,6 +42,7 @@ require 'coppertone/domain_spec'
42
42
  require 'coppertone/directive'
43
43
  require 'coppertone/modifier'
44
44
  require 'coppertone/term'
45
+ require 'coppertone/terms_parser'
45
46
  require 'coppertone/record_term_parser'
46
47
  require 'coppertone/record'
47
48
  require 'coppertone/record_evaluator'
@@ -51,4 +51,37 @@ describe Coppertone::Directive do
51
51
  end
52
52
  end
53
53
  end
54
+
55
+ context '#target_domain' do
56
+ it 'yields the target domain when the mechanism is not context dependent' do
57
+ d = Coppertone::Term.build_from_token('include:_spf.example.org')
58
+ expect(d.target_domain).to eq('_spf.example.org')
59
+ end
60
+
61
+ it 'raises an error when the mechanism is context dependent' do
62
+ d = Coppertone::Term.build_from_token('include:_spf.%{h}.example.org')
63
+ expect do
64
+ d.target_domain
65
+ end.to raise_error Coppertone::NeedsContextError
66
+ end
67
+
68
+ it 'raises an error when the mechanism does not support a target domain' do
69
+ d = Coppertone::Term.build_from_token('ip4:1.2.3.4')
70
+ expect do
71
+ d.target_domain
72
+ end.to raise_error Coppertone::NeedsContextError
73
+ end
74
+ end
75
+
76
+ context '#to_s' do
77
+ it 'should hide a default qualifier' do
78
+ d = Coppertone::Term.build_from_token('~include:_spf.%{h}.example.org')
79
+ expect(d.to_s).to eq('~include:_spf.%{h}.example.org')
80
+ end
81
+
82
+ it 'should hide a default qualifier' do
83
+ d = Coppertone::Term.build_from_token('+include:_spf.%{h}.example.org')
84
+ expect(d.to_s).to eq('include:_spf.%{h}.example.org')
85
+ end
86
+ end
54
87
  end
@@ -11,6 +11,7 @@ describe Coppertone::Mechanism::A do
11
11
  expect(mech.to_s).to eq('a')
12
12
  expect(mech).not_to be_includes_ptr
13
13
  expect(mech).to be_context_dependent
14
+ expect(mech).to be_dns_lookup_term
14
15
  end
15
16
 
16
17
  it 'should not fail if called with a blank argument' do
@@ -22,6 +23,7 @@ describe Coppertone::Mechanism::A do
22
23
  expect(mech.to_s).to eq('a')
23
24
  expect(mech).not_to be_includes_ptr
24
25
  expect(mech).to be_context_dependent
26
+ expect(mech).to be_dns_lookup_term
25
27
  end
26
28
 
27
29
  it 'should fail if called with an invalid macrostring' do
@@ -40,6 +42,7 @@ describe Coppertone::Mechanism::A do
40
42
  expect(mech.to_s).to eq('a:_spf.example.com')
41
43
  expect(mech).not_to be_includes_ptr
42
44
  expect(mech).not_to be_context_dependent
45
+ expect(mech).to be_dns_lookup_term
43
46
  end
44
47
 
45
48
  it 'should parse a context dependent domain spec' do
@@ -52,6 +55,7 @@ describe Coppertone::Mechanism::A do
52
55
  expect(mech.to_s).to eq('a:_spf.%{d}.example.com')
53
56
  expect(mech).not_to be_includes_ptr
54
57
  expect(mech).to be_context_dependent
58
+ expect(mech).to be_dns_lookup_term
55
59
  end
56
60
 
57
61
  it 'should parse a domain spec with a ptr' do
@@ -64,6 +68,7 @@ describe Coppertone::Mechanism::A do
64
68
  expect(mech.to_s).to eq('a:_spf.%{p}.example.com')
65
69
  expect(mech).to be_includes_ptr
66
70
  expect(mech).to be_context_dependent
71
+ expect(mech).to be_dns_lookup_term
67
72
  end
68
73
 
69
74
  it 'should parse a valid IP v4 CIDR length with a domain spec' do
@@ -7,6 +7,7 @@ describe Coppertone::Mechanism::All do
7
7
  expect(subject.to_s).to eq('all')
8
8
  expect(subject).not_to be_includes_ptr
9
9
  expect(subject).not_to be_context_dependent
10
+ expect(subject).not_to be_dns_lookup_term
10
11
  end
11
12
 
12
13
  it 'should not allow creation of new instances' do
@@ -28,6 +28,7 @@ describe Coppertone::Mechanism::Exists do
28
28
  expect(mech.to_s).to eq('exists:_spf.example.com')
29
29
  expect(mech).not_to be_includes_ptr
30
30
  expect(mech).not_to be_context_dependent
31
+ expect(mech).to be_dns_lookup_term
31
32
  end
32
33
 
33
34
  it 'should parse a context dependent domain spec' do
@@ -38,6 +39,7 @@ describe Coppertone::Mechanism::Exists do
38
39
  expect(mech.to_s).to eq('exists:_spf.%{d}.example.com')
39
40
  expect(mech).not_to be_includes_ptr
40
41
  expect(mech).to be_context_dependent
42
+ expect(mech).to be_dns_lookup_term
41
43
  end
42
44
 
43
45
  it 'should parse a domain spec with a ptr' do
@@ -48,6 +50,7 @@ describe Coppertone::Mechanism::Exists do
48
50
  expect(mech.to_s).to eq('exists:_spf.%{p}.example.com')
49
51
  expect(mech).to be_includes_ptr
50
52
  expect(mech).to be_context_dependent
53
+ expect(mech).to be_dns_lookup_term
51
54
  end
52
55
  end
53
56
 
@@ -28,6 +28,8 @@ describe Coppertone::Mechanism::Include do
28
28
  expect(mech.to_s).to eq('include:_spf.example.com')
29
29
  expect(mech).not_to be_includes_ptr
30
30
  expect(mech).not_to be_context_dependent
31
+ expect(mech).to be_dns_lookup_term
32
+ expect(mech.target_domain).to eq('_spf.example.com')
31
33
  end
32
34
 
33
35
  it 'should parse a context dependent domain spec' do
@@ -38,6 +40,10 @@ describe Coppertone::Mechanism::Include do
38
40
  expect(mech.to_s).to eq('include:_spf.%{d}.example.com')
39
41
  expect(mech).not_to be_includes_ptr
40
42
  expect(mech).to be_context_dependent
43
+ expect(mech).to be_dns_lookup_term
44
+ expect do
45
+ mech.target_domain
46
+ end.to raise_error Coppertone::NeedsContextError
41
47
  end
42
48
 
43
49
  it 'should parse a domain spec with a ptr' do
@@ -48,6 +54,10 @@ describe Coppertone::Mechanism::Include do
48
54
  expect(mech.to_s).to eq('include:_spf.%{p}.example.com')
49
55
  expect(mech).to be_includes_ptr
50
56
  expect(mech).to be_context_dependent
57
+ expect(mech).to be_dns_lookup_term
58
+ expect do
59
+ mech.target_domain
60
+ end.to raise_error Coppertone::NeedsContextError
51
61
  end
52
62
  end
53
63
 
@@ -22,26 +22,29 @@ describe Coppertone::Mechanism::IP4 do
22
22
 
23
23
  it 'should not fail if called with an IP v6' do
24
24
  mech = Coppertone::Mechanism::IP4.new(':fe80::202:b3ff:fe1e:8329')
25
- expect(mech.ip_network).to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
25
+ expect(mech.netblock).to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
26
26
  expect(mech.to_s).to eq('ip4:fe80::202:b3ff:fe1e:8329')
27
27
  expect(mech).not_to be_includes_ptr
28
28
  expect(mech).not_to be_context_dependent
29
+ expect(mech).not_to be_dns_lookup_term
29
30
  end
30
31
 
31
32
  it 'should work if called with an IP4' do
32
33
  mech = Coppertone::Mechanism::IP4.new(':1.2.3.4')
33
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
34
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4'))
34
35
  expect(mech.to_s).to eq('ip4:1.2.3.4')
35
36
  expect(mech).not_to be_includes_ptr
36
37
  expect(mech).not_to be_context_dependent
38
+ expect(mech).not_to be_dns_lookup_term
37
39
  end
38
40
 
39
41
  it 'should work if called with an IP4 with a pfxlen' do
40
42
  mech = Coppertone::Mechanism::IP4.new(':1.2.3.4/4')
41
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4/4'))
43
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4/4'))
42
44
  expect(mech.to_s).to eq('ip4:1.2.3.4/4')
43
45
  expect(mech).not_to be_includes_ptr
44
46
  expect(mech).not_to be_context_dependent
47
+ expect(mech).not_to be_dns_lookup_term
45
48
  end
46
49
  end
47
50
 
@@ -66,17 +69,17 @@ describe Coppertone::Mechanism::IP4 do
66
69
 
67
70
  it 'should not fail if called with an IP v6' do
68
71
  mech = Coppertone::Mechanism::IP4.create(':fe80::202:b3ff:fe1e:8329')
69
- expect(mech.ip_network).to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
72
+ expect(mech.netblock).to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
70
73
  end
71
74
 
72
75
  it 'should work if called with an IP4' do
73
76
  mech = Coppertone::Mechanism::IP4.create(':1.2.3.4')
74
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
77
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4'))
75
78
  end
76
79
 
77
80
  it 'should work if called with an IP4 with a pfxlen' do
78
81
  mech = Coppertone::Mechanism::IP4.create(':1.2.3.4/4')
79
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4/4'))
82
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4/4'))
80
83
  end
81
84
 
82
85
  it 'should fail if called with an invalid pfxlen' do
@@ -22,19 +22,22 @@ describe Coppertone::Mechanism::IP6 do
22
22
 
23
23
  it 'should not fail if called with an IP v4' do
24
24
  mech = Coppertone::Mechanism::IP6.new(':1.2.3.4')
25
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
25
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4'))
26
+ expect(mech).not_to be_dns_lookup_term
26
27
  end
27
28
 
28
29
  it 'should work if called with an IP6' do
29
30
  mech = Coppertone::Mechanism::IP6.new(':fe80::202:b3ff:fe1e:8329')
30
- expect(mech.ip_network)
31
+ expect(mech.netblock)
31
32
  .to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
33
+ expect(mech).not_to be_dns_lookup_term
32
34
  end
33
35
 
34
36
  it 'should work if called with an IP6 with a pfxlen' do
35
37
  mech = Coppertone::Mechanism::IP6.new(':fe80::202:b3ff:fe1e:8329/64')
36
- expect(mech.ip_network)
38
+ expect(mech.netblock)
37
39
  .to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329/64'))
40
+ expect(mech).not_to be_dns_lookup_term
38
41
  end
39
42
 
40
43
  it 'should fail if called with an invalid pfxlen' do
@@ -66,14 +69,14 @@ describe Coppertone::Mechanism::IP6 do
66
69
 
67
70
  it 'should not fail if called with an IP v4' do
68
71
  mech = Coppertone::Mechanism::IP6.create(':1.2.3.4')
69
- expect(mech.ip_network).to eq(IPAddr.new('1.2.3.4'))
72
+ expect(mech.netblock).to eq(IPAddr.new('1.2.3.4'))
70
73
  expect(mech).not_to be_includes_ptr
71
74
  expect(mech).not_to be_context_dependent
72
75
  end
73
76
 
74
77
  it 'should work if called with an IP6' do
75
78
  mech = Coppertone::Mechanism::IP6.create(':fe80::202:b3ff:fe1e:8329')
76
- expect(mech.ip_network)
79
+ expect(mech.netblock)
77
80
  .to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329'))
78
81
  expect(mech).not_to be_includes_ptr
79
82
  expect(mech).not_to be_context_dependent
@@ -81,7 +84,7 @@ describe Coppertone::Mechanism::IP6 do
81
84
 
82
85
  it 'should work if called with an IP6 with a pfxlen' do
83
86
  mech = Coppertone::Mechanism::IP6.create(':fe80::202:b3ff:fe1e:8329/64')
84
- expect(mech.ip_network)
87
+ expect(mech.netblock)
85
88
  .to eq(IPAddr.new('fe80::202:b3ff:fe1e:8329/64'))
86
89
  expect(mech).not_to be_includes_ptr
87
90
  expect(mech).not_to be_context_dependent
@@ -11,6 +11,7 @@ describe Coppertone::Mechanism::MX do
11
11
  expect(mech.to_s).to eq('mx')
12
12
  expect(mech).not_to be_includes_ptr
13
13
  expect(mech).to be_context_dependent
14
+ expect(mech).to be_dns_lookup_term
14
15
  end
15
16
 
16
17
  it 'should not fail if called with a blank argument' do
@@ -22,6 +23,7 @@ describe Coppertone::Mechanism::MX do
22
23
  expect(mech.to_s).to eq('mx')
23
24
  expect(mech).not_to be_includes_ptr
24
25
  expect(mech).to be_context_dependent
26
+ expect(mech).to be_dns_lookup_term
25
27
  end
26
28
 
27
29
  it 'should fail if called with an invalid macrostring' do
@@ -39,6 +41,7 @@ describe Coppertone::Mechanism::MX do
39
41
  expect(mech.to_s).to eq('mx/24')
40
42
  expect(mech).not_to be_includes_ptr
41
43
  expect(mech).to be_context_dependent
44
+ expect(mech).to be_dns_lookup_term
42
45
  end
43
46
 
44
47
  it 'should process the domain spec if it includes a IP v6 CIDR' do
@@ -61,6 +64,7 @@ describe Coppertone::Mechanism::MX do
61
64
  expect(mech.to_s).to eq('mx/28//96')
62
65
  expect(mech).not_to be_includes_ptr
63
66
  expect(mech).to be_context_dependent
67
+ expect(mech).to be_dns_lookup_term
64
68
  end
65
69
 
66
70
  it 'should not fail if called with a fixed domain spec without explicit CIDRs' do
@@ -83,6 +87,7 @@ describe Coppertone::Mechanism::MX do
83
87
  expect(mech.ip_v4_cidr_length).to eq(28)
84
88
  expect(mech.ip_v6_cidr_length).to eq(96)
85
89
  expect(mech.to_s).to eq('mx:mx.example.com/28//96')
90
+ expect(mech).to be_dns_lookup_term
86
91
  end
87
92
 
88
93
  it 'should not fail if called with a context-dependent domain spec without explicit CIDRs' do
@@ -95,6 +100,7 @@ describe Coppertone::Mechanism::MX do
95
100
  expect(mech.to_s).to eq('mx:%{d}.example.com')
96
101
  expect(mech).not_to be_includes_ptr
97
102
  expect(mech).to be_context_dependent
103
+ expect(mech).to be_dns_lookup_term
98
104
  end
99
105
 
100
106
  it 'should not fail if called with a fixed domain spec with explicit CIDRs' do
@@ -107,6 +113,7 @@ describe Coppertone::Mechanism::MX do
107
113
  expect(mech.to_s).to eq('mx:%{d}.example.com/28//96')
108
114
  expect(mech).not_to be_includes_ptr
109
115
  expect(mech).to be_context_dependent
116
+ expect(mech).to be_dns_lookup_term
110
117
  end
111
118
 
112
119
  it 'should not fail if called with a context-dependent domain spec without explicit CIDRs with PTR' do
@@ -119,6 +126,7 @@ describe Coppertone::Mechanism::MX do
119
126
  expect(mech.to_s).to eq('mx:%{p}.example.com')
120
127
  expect(mech).to be_includes_ptr
121
128
  expect(mech).to be_context_dependent
129
+ expect(mech).to be_dns_lookup_term
122
130
  end
123
131
 
124
132
  it 'should not fail if called with a fixed domain spec with explicit CIDRs with PTR' do
@@ -131,6 +139,7 @@ describe Coppertone::Mechanism::MX do
131
139
  expect(mech.to_s).to eq('mx:%{p}.example.com/28//96')
132
140
  expect(mech).to be_includes_ptr
133
141
  expect(mech).to be_context_dependent
142
+ expect(mech).to be_dns_lookup_term
134
143
  end
135
144
  end
136
145
 
@@ -8,6 +8,7 @@ describe Coppertone::Mechanism::Ptr do
8
8
  expect(mech.domain_spec).to be_nil
9
9
  expect(mech).not_to be_includes_ptr
10
10
  expect(mech).to be_context_dependent
11
+ expect(mech).to be_dns_lookup_term
11
12
  end
12
13
 
13
14
  it 'should not fail if called with a blank argument' do
@@ -16,6 +17,7 @@ describe Coppertone::Mechanism::Ptr do
16
17
  expect(mech.domain_spec).to be_nil
17
18
  expect(mech).not_to be_includes_ptr
18
19
  expect(mech).to be_context_dependent
20
+ expect(mech).to be_dns_lookup_term
19
21
  end
20
22
 
21
23
  it 'should fail if called with an invalid macrostring' do
@@ -11,6 +11,7 @@ describe Coppertone::Modifier::Redirect do
11
11
  .to eq('redirect=test.example.com')
12
12
  expect(modifier).not_to be_includes_ptr
13
13
  expect(modifier).not_to be_context_dependent
14
+ expect(modifier.target_domain).to eq('test.example.com')
14
15
  end
15
16
 
16
17
  it 'should work with a context independent domain spec' do
@@ -22,6 +23,9 @@ describe Coppertone::Modifier::Redirect do
22
23
  .to eq('redirect=%{d}.example.com')
23
24
  expect(modifier).not_to be_includes_ptr
24
25
  expect(modifier).to be_context_dependent
26
+ expect do
27
+ modifier.target_domain
28
+ end.to raise_error Coppertone::NeedsContextError
25
29
  end
26
30
 
27
31
  it 'should work with a context independent domain spec with a PTR' do
@@ -33,6 +37,9 @@ describe Coppertone::Modifier::Redirect do
33
37
  .to eq('redirect=%{p}.example.com')
34
38
  expect(modifier).to be_includes_ptr
35
39
  expect(modifier).to be_context_dependent
40
+ expect do
41
+ modifier.target_domain
42
+ end.to raise_error Coppertone::NeedsContextError
36
43
  end
37
44
  end
38
45
 
@@ -42,4 +49,19 @@ describe Coppertone::Modifier::Redirect do
42
49
  .to eq('redirect=test.example.com')
43
50
  end
44
51
  end
52
+
53
+ context '#included_record' do
54
+ it 'should delegate to included_record' do
55
+ modifier = Coppertone::Modifier::Redirect.new('test.example.com')
56
+ record = double(:record)
57
+ finder = double(:finder)
58
+ expect(finder).to receive(:record).and_return(record)
59
+ macro_ctx = double(:macro_ctx)
60
+ request_ctx = double(:request_ctx)
61
+ expect(Coppertone::RedirectRecordFinder).to receive(:new)
62
+ .with(modifier, macro_ctx, request_ctx).and_return(finder)
63
+ expect(modifier.included_record(macro_ctx, request_ctx))
64
+ .to eq(record)
65
+ end
66
+ end
45
67
  end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Coppertone::Modifier::Unknown do
4
+ it 'should always be context independent and never require macro evaluation' do
5
+ unk = Coppertone::Modifier::Unknown.new('abcd', 'wxyz')
6
+ expect(unk).not_to be_context_dependent
7
+ expect(unk).not_to be_includes_ptr
8
+ expect(unk.to_s).to eq('abcd=wxyz')
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Coppertone::NullMacroContext do
4
+ %w(s l o d i p v h c r t).each do |i|
5
+ it "should raise an error for #{i}" do
6
+ expect do
7
+ Coppertone::NullMacroContext::NULL_CONTEXT.send(i)
8
+ end.to raise_error ArgumentError
9
+ end
10
+
11
+ it "should raise an error for #{i.upcase}" do
12
+ expect do
13
+ Coppertone::NullMacroContext::NULL_CONTEXT.send(i.upcase)
14
+ end.to raise_error ArgumentError
15
+ end
16
+ end
17
+
18
+ it 'should return self for with_domain' do
19
+ n = Coppertone::NullMacroContext::NULL_CONTEXT
20
+ expect(n.with_domain('abcd')).to eq(n)
21
+ end
22
+ end
@@ -11,6 +11,7 @@ describe Coppertone::Qualifier do
11
11
  it "should map from #{k} to the correct value" do
12
12
  expect(Coppertone::Qualifier.find_by_text(k)).to eq(v)
13
13
  expect(v.text).to eq(k)
14
+ expect(v.to_s).to eq(k)
14
15
  end
15
16
  end
16
17
  end
@@ -22,6 +23,15 @@ describe Coppertone::Qualifier do
22
23
  end
23
24
  end
24
25
 
26
+ context '#default?' do
27
+ it 'should produce the right values for default?' do
28
+ expect(Coppertone::Qualifier::PASS).to be_default
29
+ expect(Coppertone::Qualifier::FAIL).not_to be_default
30
+ expect(Coppertone::Qualifier::SOFTFAIL).not_to be_default
31
+ expect(Coppertone::Qualifier::NEUTRAL).not_to be_default
32
+ end
33
+ end
34
+
25
35
  context '#qualifiers' do
26
36
  it 'should have the correct contents' do
27
37
  qualifiers = Coppertone::Qualifier.qualifiers
data/spec/record_spec.rb CHANGED
@@ -40,6 +40,7 @@ describe Coppertone::Record do
40
40
  expect(directive.qualifier).to eq(Coppertone::Qualifier::SOFTFAIL)
41
41
  expect(directive.mechanism).to eq(Coppertone::Mechanism::All.instance)
42
42
  expect(record.modifiers).to be_empty
43
+ expect(record.to_s).to eq('v=spf1 ~all')
43
44
  end
44
45
 
45
46
  it 'be case insensitive when parsing the version string' do
@@ -50,6 +51,7 @@ describe Coppertone::Record do
50
51
  expect(directive.qualifier).to eq(Coppertone::Qualifier::SOFTFAIL)
51
52
  expect(directive.mechanism).to eq(Coppertone::Mechanism::All.instance)
52
53
  expect(record.modifiers).to be_empty
54
+ expect(record.to_s).to eq('v=spf1 ~all')
53
55
  end
54
56
 
55
57
  it 'should parse more complex records' do
@@ -71,6 +73,7 @@ describe Coppertone::Record do
71
73
  expect(record.redirect).to be_nil
72
74
  expect(record.exp)
73
75
  .to eq(Coppertone::Modifier::Exp.new('explain._spf.%{d}'))
76
+ expect(record.to_s).to eq('v=spf1 mx -all exp=explain._spf.%{d}')
74
77
  end
75
78
 
76
79
  it 'should fail on more records with duplicate modifiers' do
@@ -118,4 +121,27 @@ describe Coppertone::Record do
118
121
  expect(Coppertone::Record.new('v=spf1 mx -all redirect=explain._spf.%{d}').unknown_modifiers).to be_empty
119
122
  end
120
123
  end
124
+
125
+ context '#dns_lookup_term_count' do
126
+ it 'should calculate correctly' do
127
+ expect(Coppertone::Record.new('v=spf1 -all exp=explain._spf.%{d}').dns_lookup_term_count).to eq(0)
128
+ expect(Coppertone::Record.new('v=spf1 a:example.test.com -exists:some.domain.com ~all').dns_lookup_term_count).to eq(2)
129
+ expect(Coppertone::Record.new('v=spf1 ip4:1.2.3.4 -exists:some.domain.com ~all').dns_lookup_term_count).to eq(1)
130
+ end
131
+ end
132
+
133
+ context '#includes' do
134
+ it 'should yield an empty array when there are no include' do
135
+ expect(Coppertone::Record.new('v=spf1 mx -all exp=explain._spf.%{d}').includes).to be_empty
136
+ expect(Coppertone::Record.new('v=spf1 ip4:1.2.3.4 ~all').includes).to be_empty
137
+ end
138
+
139
+ it 'should yield a set of the includes, in order' do
140
+ r = Coppertone::Record
141
+ .new('v=spf1 include:_spf.domain1.com ip4:1.2.3.4 include:_spf.domain2.com ~all exp=explain._spf.%{d}')
142
+ includes = r.includes
143
+ expect(includes.map(&:mechanism).all? {|i| i.is_a?(Coppertone::Mechanism::Include)}).to be_truthy
144
+ expect(includes.map(&:target_domain)).to eq(%w(_spf.domain1.com _spf.domain2.com))
145
+ end
146
+ end
121
147
  end
metadata CHANGED
@@ -1,136 +1,138 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: coppertone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Peter M. Goldstein
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-10-10 00:00:00.000000000 Z
11
+ date: 2014-11-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
+ name: dns_adapter
14
15
  requirement: !ruby/object:Gem::Requirement
15
16
  requirements:
16
- - - '>='
17
+ - - ">="
17
18
  - !ruby/object:Gem::Version
18
19
  version: '0'
19
- name: dns_adapter
20
- prerelease: false
21
20
  type: :runtime
21
+ prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
+ name: i18n
28
29
  requirement: !ruby/object:Gem::Requirement
29
30
  requirements:
30
- - - '>='
31
+ - - ">="
31
32
  - !ruby/object:Gem::Version
32
33
  version: '0'
33
- name: i18n
34
- prerelease: false
35
34
  type: :runtime
35
+ prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - '>='
38
+ - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
+ name: addressable
42
43
  requirement: !ruby/object:Gem::Requirement
43
44
  requirements:
44
- - - '>='
45
+ - - ">="
45
46
  - !ruby/object:Gem::Version
46
47
  version: '0'
47
- name: addressable
48
- prerelease: false
49
48
  type: :runtime
49
+ prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - '>='
52
+ - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
+ name: activesupport
56
57
  requirement: !ruby/object:Gem::Requirement
57
58
  requirements:
58
- - - '>='
59
+ - - ">="
59
60
  - !ruby/object:Gem::Version
60
61
  version: '3.0'
61
- name: activesupport
62
- prerelease: false
63
62
  type: :runtime
63
+ prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
- - - '>='
66
+ - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '3.0'
69
69
  - !ruby/object:Gem::Dependency
70
+ name: bundler
70
71
  requirement: !ruby/object:Gem::Requirement
71
72
  requirements:
72
- - - '>='
73
+ - - ">="
73
74
  - !ruby/object:Gem::Version
74
75
  version: '0'
75
- name: bundler
76
- prerelease: false
77
76
  type: :development
77
+ prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '>='
80
+ - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
83
  - !ruby/object:Gem::Dependency
84
+ name: rake
84
85
  requirement: !ruby/object:Gem::Requirement
85
86
  requirements:
86
- - - ~>
87
+ - - "~>"
87
88
  - !ruby/object:Gem::Version
88
89
  version: '10.0'
89
- name: rake
90
- prerelease: false
91
90
  type: :development
91
+ prerelease: false
92
92
  version_requirements: !ruby/object:Gem::Requirement
93
93
  requirements:
94
- - - ~>
94
+ - - "~>"
95
95
  - !ruby/object:Gem::Version
96
96
  version: '10.0'
97
97
  - !ruby/object:Gem::Dependency
98
+ name: rspec
98
99
  requirement: !ruby/object:Gem::Requirement
99
100
  requirements:
100
- - - '>='
101
+ - - ">="
101
102
  - !ruby/object:Gem::Version
102
103
  version: '3.0'
103
- name: rspec
104
- prerelease: false
105
104
  type: :development
105
+ prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
- - - '>='
108
+ - - ">="
109
109
  - !ruby/object:Gem::Version
110
110
  version: '3.0'
111
111
  - !ruby/object:Gem::Dependency
112
+ name: rubocop
112
113
  requirement: !ruby/object:Gem::Requirement
113
114
  requirements:
114
- - - '>='
115
+ - - ">="
115
116
  - !ruby/object:Gem::Version
116
117
  version: '0'
117
- name: rubocop
118
- prerelease: false
119
118
  type: :development
119
+ prerelease: false
120
120
  version_requirements: !ruby/object:Gem::Requirement
121
121
  requirements:
122
- - - '>='
122
+ - - ">="
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
- description: Coppertone includes tools for parsing SPF DNS records, evaluating the result of SPF checks for received emails, and creating appropriate email headers from SPF results.
125
+ description: Coppertone includes tools for parsing SPF DNS records, evaluating the
126
+ result of SPF checks for received emails, and creating appropriate email headers
127
+ from SPF results.
126
128
  email:
127
129
  - peter.m.goldstein@gmail.com
128
130
  executables: []
129
131
  extensions: []
130
132
  extra_rdoc_files: []
131
133
  files:
132
- - .gitignore
133
- - .travis.yml
134
+ - ".gitignore"
135
+ - ".travis.yml"
134
136
  - Gemfile
135
137
  - LICENSE
136
138
  - README.md
@@ -183,6 +185,7 @@ files:
183
185
  - lib/coppertone/sender_identity.rb
184
186
  - lib/coppertone/spf_service.rb
185
187
  - lib/coppertone/term.rb
188
+ - lib/coppertone/terms_parser.rb
186
189
  - lib/coppertone/utils.rb
187
190
  - lib/coppertone/utils/domain_utils.rb
188
191
  - lib/coppertone/utils/host_utils.rb
@@ -208,7 +211,9 @@ files:
208
211
  - spec/mechanism_spec.rb
209
212
  - spec/modifier/exp_spec.rb
210
213
  - spec/modifier/redirect_spec.rb
214
+ - spec/modifier/unknown_spec.rb
211
215
  - spec/modifier_spec.rb
216
+ - spec/null_macro_context_spec.rb
212
217
  - spec/open_spf/ALL_mechanism_syntax_spec.rb
213
218
  - spec/open_spf/A_mechanism_syntax_spec.rb
214
219
  - spec/open_spf/EXISTS_mechanism_syntax_spec.rb
@@ -245,24 +250,24 @@ homepage: https://github.com/petergoldstein/coppertone
245
250
  licenses:
246
251
  - Apache
247
252
  metadata: {}
248
- post_install_message:
253
+ post_install_message:
249
254
  rdoc_options: []
250
255
  require_paths:
251
256
  - lib
252
257
  required_ruby_version: !ruby/object:Gem::Requirement
253
258
  requirements:
254
- - - '>='
259
+ - - ">="
255
260
  - !ruby/object:Gem::Version
256
261
  version: '0'
257
262
  required_rubygems_version: !ruby/object:Gem::Requirement
258
263
  requirements:
259
- - - '>='
264
+ - - ">="
260
265
  - !ruby/object:Gem::Version
261
266
  version: '0'
262
267
  requirements: []
263
- rubyforge_project:
264
- rubygems_version: 2.1.9
265
- signing_key:
268
+ rubyforge_project:
269
+ rubygems_version: 2.4.4
270
+ signing_key:
266
271
  specification_version: 4
267
272
  summary: A Sender Policy Framework (SPF) toolkit
268
273
  test_files:
@@ -285,7 +290,9 @@ test_files:
285
290
  - spec/mechanism_spec.rb
286
291
  - spec/modifier/exp_spec.rb
287
292
  - spec/modifier/redirect_spec.rb
293
+ - spec/modifier/unknown_spec.rb
288
294
  - spec/modifier_spec.rb
295
+ - spec/null_macro_context_spec.rb
289
296
  - spec/open_spf/ALL_mechanism_syntax_spec.rb
290
297
  - spec/open_spf/A_mechanism_syntax_spec.rb
291
298
  - spec/open_spf/EXISTS_mechanism_syntax_spec.rb