acts_as_span 1.1.0 → 1.1.1

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: 57404b0d110113b5f9c52537aa90970a560d69f4a5b7d5d318caacc229157679
4
- data.tar.gz: 5702fafe912d1bc56b633aec32dd2ae76da9f886608c182eed29c0ca7d2ed509
3
+ metadata.gz: fec6db2024d649082face6a15ed22acffb3828ca67de3c54c509f940ade832eb
4
+ data.tar.gz: 67c0277e44d09fb7b5b73e4eb73327c4e8ff796109975f77f5afe42dc2120db3
5
5
  SHA512:
6
- metadata.gz: b1e7265d3cfc7433b331f407fe991eb5549091e10c5dcef0298468099f1769dfaca71a7355363af8f6f88c381362d518fa494366d3f1dead8de8072458d95b2c
7
- data.tar.gz: 6304cb8600c553e60bc7e470ed4f87e6cc145a4239243f5109556278f76d88b24d1624bbc904e499d7ce95d7196967d4d8822c962571c9184c875dbd20d6d57f
6
+ metadata.gz: 9fbe5d893be0359e262ed8cfb5d1fc58551317d103fc07098c1d5b6f5955fc95a520129c6d0d2493fda975681a9508484bc5550e17a187af9827fc867564ccc6
7
+ data.tar.gz: 9fc6775556dca3cd990b2988de635beb11311c683e4a104951faf33245b1f5b36ecdd3d66d501c57092a517e6bbba15ec181def50cbea01c6de5b50271bc377a
@@ -1,51 +1,83 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_model'
2
4
 
3
5
  module ActsAsSpan
6
+ # Validator that checks whether a record is overlapping with others
7
+ #
8
+ # Takes options `:instance_scope` (optional) and `:scope` (required):
9
+ # * `instance_scope` is a proc which, when evaluated by the record, returns
10
+ # a boolean value. When false, the validatior will not check for overlap.
11
+ # When true, the validator checks normally.
12
+ # * `scope` is also a proc. This is must return an ActiveRecord Relation that
13
+ # determines which records' spans to compare.
14
+ #
15
+ # Usage:
16
+ # Given a record with `siblings` defined, the most basic use case is:
17
+ # ```
18
+ # validates_with ActsAsSpan::NoOverlapValidator,
19
+ # scope: proc { siblings }
20
+ # ```
21
+ # When this record is validated, every record in the ActiveRecord relation
22
+ # `record.siblings` is checked for mutual overlap with `record`.
23
+ #
24
+ # Use `instance_scope` if there is some condition where a record oughtn't be
25
+ # validated for whatever reason:
26
+ # ```
27
+ # validates_with ActsAsSpan::NoOverlapValidator,
28
+ # scope: proc { siblings }, instance_scope: proc { favorite? }
29
+ # ```
30
+ # Now, when this record is validated, if `record.favorite?` is `true`,
31
+ # `record` must pass the overlap check with its siblings.
32
+ # If `record.favorite?` is `false`, it is under less scrutiny.
33
+ #
4
34
  class NoOverlapValidator < ActiveModel::Validator
5
35
  def validate(record)
6
36
  overlapping_records = temporally_overlapping_for(record)
7
37
  instance_scope = if options[:instance_scope].is_a? Proc
8
- record.instance_eval&options[:instance_scope]
38
+ record.instance_eval(&options[:instance_scope])
9
39
  else
10
40
  true
11
41
  end
12
42
 
13
- if overlapping_records.any? && instance_scope
14
-
15
- record.errors.add(
16
- :base,
17
- :no_overlap,
18
- model_name: record.class.model_name.human,
19
- model_name_plural: record.class.model_name.plural.humanize,
20
- start_date: record.span.start_date,
21
- end_date: record.span.end_date,
22
- count: overlapping_records.size,
23
- overlapping_records_s: overlapping_records.join(",")
24
- )
25
- end
43
+ return unless overlapping_records.any? && instance_scope
44
+
45
+ record.errors.add(
46
+ :base,
47
+ :no_overlap,
48
+ model_name: record.class.model_name.human,
49
+ model_name_plural: record.class.model_name.plural.humanize,
50
+ start_date: record.span.start_date,
51
+ end_date: record.span.end_date,
52
+ count: overlapping_records.size,
53
+ overlapping_records_s: overlapping_records.join(', ')
54
+ )
26
55
  end
27
56
 
28
- #TODO add back condition for start_date nil
29
- #TODO add support for multiple spans (currently only checks :default)
57
+ # TODO: add back condition for start_date nil
58
+ # TODO: add support for multiple spans (currently only checks :default)
30
59
  def temporally_overlapping_for(record)
31
60
  scope = record.instance_eval(&options[:scope])
32
61
 
33
62
  start_date = record.span.start_date || Date.current
63
+
34
64
  end_date = record.span.end_date
65
+ end_field = record.span.end_field
66
+
35
67
  arel_table = record.class.arel_table
36
68
 
37
69
  if end_date
38
70
  scope.where(
39
71
  arel_table[record.span.start_field].lteq(end_date)
40
72
  .and(
41
- arel_table[record.span.end_field].gteq(start_date)
42
- .or(arel_table[record.span.end_field].eq(nil))
73
+ arel_table[end_field].gteq(start_date)
74
+ .or(arel_table[end_field].eq(nil))
43
75
  )
44
76
  )
45
77
  else
46
78
  scope.where(
47
- arel_table[record.span.end_field].gteq(start_date)
48
- .or(arel_table[record.span.end_field].eq(nil))
79
+ arel_table[end_field].gteq(start_date)
80
+ .or(arel_table[end_field].eq(nil))
49
81
  )
50
82
  end
51
83
  end
@@ -2,7 +2,7 @@ module ActsAsSpan
2
2
  module VERSION
3
3
  MAJOR = 1
4
4
  MINOR = 1
5
- TINY = 0
5
+ TINY = 1
6
6
  PRE = nil
7
7
 
8
8
  STRING = [MAJOR, MINOR, TINY, PRE].compact.join('.')
@@ -42,6 +42,29 @@ RSpec.describe ActsAsSpan::NoOverlapValidator do
42
42
 
43
43
  let(:all_siblings) { [brother, sister, brother_from_another_mother] }
44
44
 
45
+ describe 'instance_scope' do
46
+ let(:child_class) { OneParentChild }
47
+
48
+ let(:new_child_start_date) { Date.current - 2 }
49
+ let(:new_child_end_date) { Date.current + 3 }
50
+
51
+ before { new_child.favorite = favorite }
52
+
53
+ context 'when instance_scope evaluates to false' do
54
+ let(:favorite) { false }
55
+ it 'skips validation on the record for which instance_scope is false' do
56
+ expect(new_child).to be_valid
57
+ end
58
+ end
59
+
60
+ context 'when instance_scope evaluates to true' do
61
+ let(:favorite) { true }
62
+ it 'validates normally' do
63
+ expect(new_child).not_to be_valid
64
+ end
65
+ end
66
+ end
67
+
45
68
  describe 'an object with a single parent' do
46
69
  let(:child_class) { OneParentChild }
47
70
 
@@ -28,14 +28,22 @@ Temping.create :one_parent_child do
28
28
 
29
29
  t.date :start_date
30
30
  t.date :end_date
31
+
32
+ # every one-parent child is a favorite! ...by default
33
+ t.boolean :favorite, default: true
31
34
  end
32
35
 
33
36
  acts_as_span
34
37
 
38
+ def favorite?
39
+ favorite
40
+ end
41
+
35
42
  belongs_to :mama
36
43
  has_siblings through: [:mama]
37
44
 
38
- validates_with ActsAsSpan::NoOverlapValidator, scope: proc { siblings }
45
+ validates_with ActsAsSpan::NoOverlapValidator,
46
+ scope: proc { siblings }, instance_scope: proc { favorite? }
39
47
  validates_with ActsAsSpan::WithinParentDateSpanValidator, parents: [:mama]
40
48
  end
41
49
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: acts_as_span
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Eric Sullivan
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-31 00:00:00.000000000 Z
11
+ date: 2020-04-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -199,7 +199,7 @@ requirements: []
199
199
  rubygems_version: 3.1.2
200
200
  signing_key:
201
201
  specification_version: 4
202
- summary: acts_as_span 1.1.0
202
+ summary: acts_as_span 1.1.1
203
203
  test_files:
204
204
  - spec/lib/acts_as_span_spec.rb
205
205
  - spec/lib/delegation_spec.rb