temporality 0.0.4 → 0.0.5
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 +4 -4
- data/README.md +1 -14
- data/lib/temporality.rb +51 -1
- data/lib/temporality/associations.rb +38 -4
- data/lib/temporality/auto_close.rb +27 -1
- data/lib/temporality/completeness.rb +13 -3
- data/lib/temporality/day_count.rb +1 -1
- data/lib/temporality/errors.rb +6 -0
- data/lib/temporality/schema.rb +5 -4
- data/lib/temporality/transaction.rb +42 -0
- data/lib/temporality/validation.rb +12 -9
- data/lib/temporality/version.rb +1 -1
- metadata +16 -15
- data/lib/temporality/violation.rb +0 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b3427d7d435600ebdf7609aaf3fa32a929fcb49c
|
4
|
+
data.tar.gz: 6af589c77936c6c98573e129723c27ec4f4facc9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 10d40a04e6b406620aa808f615650ae5045ca897b6deefa0d509e59761736c03b9e3985dd1cdbd038957e052046df042eda0ae29d80aa362b6d16fac25f076ac
|
7
|
+
data.tar.gz: 6f2498c1fb678cdf6f54f29beb6913c79c4637196db1e0ab436e33931e3503d087fe5cfcebe1985985734f2aa1faffb10c6de086b37b15d58fa7bb63a9c9cfac
|
data/README.md
CHANGED
@@ -4,20 +4,6 @@ Temporality [
|
19
63
|
|
20
64
|
# Used when no end date is defined
|
21
65
|
FUTURE_INFINITY = Date.new(5000, 1, 1)
|
22
66
|
|
67
|
+
# Prepended modules
|
23
68
|
PREPENDS = [ AttributeOverrides, Validation ]
|
69
|
+
|
70
|
+
# Extensions to the included class
|
24
71
|
EXTENDS = [ Associations, Scopes ]
|
72
|
+
|
73
|
+
# Inclusions for the included class
|
25
74
|
INCLUDES = [ DefaultBoundaryValues ]
|
26
75
|
|
27
76
|
def self.included(base)
|
@@ -29,6 +78,7 @@ module Temporality
|
|
29
78
|
EXTENDS.each { |mod| base.extend(mod) }
|
30
79
|
INCLUDES.each { |mod| base.include(mod) }
|
31
80
|
|
81
|
+
# TODO : On va peut-être pas l'inclure 50 fois ce truc...
|
32
82
|
ActiveRecord::Base.send(:include, DayCount)
|
33
83
|
end
|
34
84
|
|
@@ -1,9 +1,22 @@
|
|
1
1
|
module Temporality
|
2
|
-
module Associations
|
3
2
|
|
4
|
-
|
3
|
+
# = Temporal associations
|
4
|
+
#
|
5
|
+
# This module overrides +ActiveRecord::Base.belongs_to+ with the ability to
|
6
|
+
# specifiy temporality options on the association.
|
7
|
+
#
|
8
|
+
# == Inverse associations
|
9
|
+
#
|
10
|
+
# It is necessary that ActiveRecord knows the inverse association on the
|
11
|
+
# +has_many+ side. If it isn't inferred automatically you must specify it
|
12
|
+
# using the +:inverse_of+ option on the +has_many+ declaration.
|
13
|
+
#
|
14
|
+
# @todo Use class-inheritable instance variables
|
15
|
+
#
|
16
|
+
module Associations
|
5
17
|
|
6
|
-
|
18
|
+
# The default temporality options
|
19
|
+
DEFAULTS = { inclusion: true, completeness: false, prevent_overlap: false, auto_close: false }.freeze
|
7
20
|
|
8
21
|
def belongs_to(*args, &block)
|
9
22
|
@temporality ||= {}
|
@@ -17,13 +30,34 @@ module Temporality
|
|
17
30
|
end
|
18
31
|
end
|
19
32
|
|
20
|
-
@temporality[assoc_name] = DEFAULTS.merge(opts)
|
33
|
+
@temporality[assoc_name] = with_implied_options(DEFAULTS.merge(opts))
|
21
34
|
end
|
22
35
|
end
|
23
36
|
|
24
37
|
super(*args, &block)
|
25
38
|
end
|
26
39
|
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
#
|
44
|
+
# Sets options implied by other options as follows:
|
45
|
+
#
|
46
|
+
# - +:auto_close+ implies +:completeness+
|
47
|
+
# - +:completeness+ implies +:prevent_overlap+
|
48
|
+
# - +:completeness+ implies +:inclusion+
|
49
|
+
#
|
50
|
+
# @param opts [Hash] The options hash
|
51
|
+
# @return [Hash] The options with implied options set
|
52
|
+
#
|
53
|
+
def with_implied_options(opts)
|
54
|
+
res = opts.dup
|
55
|
+
res[:completeness] ||= res[:auto_close]
|
56
|
+
res[:prevent_overlap] ||= res[:completeness]
|
57
|
+
res[:inclusion] ||= res[:completeness]
|
58
|
+
res
|
59
|
+
end
|
60
|
+
|
27
61
|
end
|
28
62
|
end
|
29
63
|
|
@@ -3,7 +3,33 @@ require 'temporality/validation_strategy'
|
|
3
3
|
module Temporality
|
4
4
|
class AutoClose < ValidationStrategy
|
5
5
|
|
6
|
-
def
|
6
|
+
def call
|
7
|
+
prev = nil
|
8
|
+
|
9
|
+
if @model.id
|
10
|
+
prev = inverse.order('starts_on ASC').where('id <> ?', @model.id).last
|
11
|
+
else
|
12
|
+
prev = inverse.order('starts_on ASC').last
|
13
|
+
end
|
14
|
+
|
15
|
+
if prev
|
16
|
+
if prev.starts_on >= @model.starts_on
|
17
|
+
raise Temporality::AutoCloseError.new("Can't auto-close a previous sibling with a fully overlapping record")
|
18
|
+
end
|
19
|
+
|
20
|
+
if prev.ends_on != Temporality::FUTURE_INFINITY
|
21
|
+
raise Temporality::AutoCloseError.new("Can't auto-close previous record if it's end date is finite")
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
if prev
|
26
|
+
if Temporality.active_transaction?
|
27
|
+
prev.ends_on = @model.starts_on - 1
|
28
|
+
prev.save
|
29
|
+
else
|
30
|
+
raise Temporality::NoTransactionError.new("Auto-closing previous records requires a Temporality transaction")
|
31
|
+
end
|
32
|
+
end
|
7
33
|
end
|
8
34
|
|
9
35
|
end
|
@@ -4,18 +4,28 @@ module Temporality
|
|
4
4
|
class Completeness < ValidationStrategy
|
5
5
|
|
6
6
|
def validate
|
7
|
+
if Temporality.active_transaction?
|
8
|
+
Temporality.defer(defer_key) { perform_validation }
|
9
|
+
else
|
10
|
+
perform_validation
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def perform_validation
|
7
15
|
days = @model.day_count + (inverse.where('id <> ?', @model.id || -1).map(&:day_count).inject(&:+) || 0)
|
8
16
|
parent_days = @model.send(@assoc).day_count
|
9
|
-
|
10
17
|
raise Temporality::Violation.new(error_message) unless (parent_days == days)
|
11
18
|
end
|
12
19
|
|
20
|
+
def defer_key
|
21
|
+
parent = @model.send(@assoc)
|
22
|
+
"#{parent.class.name}_#{parent.id}_#{inverse_name}"
|
23
|
+
end
|
24
|
+
|
13
25
|
def error_message
|
14
26
|
"#{@model.send(@assoc).class} record must have a temporally complete children collection for assocation #{inverse_name}"
|
15
27
|
end
|
16
28
|
|
17
|
-
# TODO : Check if in transaction ActiveRecord::Base.connection.open_transactions
|
18
|
-
|
19
29
|
end
|
20
30
|
end
|
21
31
|
|
data/lib/temporality/schema.rb
CHANGED
@@ -1,8 +1,9 @@
|
|
1
|
-
#
|
2
|
-
# Defines a `temporality` migration helper for use in a `create_table` block as well
|
3
|
-
# as a `temporality(:table)` helper to be used to alter existing table definitions.
|
4
|
-
#
|
5
1
|
module Temporality
|
2
|
+
|
3
|
+
#
|
4
|
+
# Defines a +temporality+ migration helper for use in a +create_table+ block as well
|
5
|
+
# as a +temporality(:table)+ helper to be used to alter existing table definitions.
|
6
|
+
#
|
6
7
|
module Schema
|
7
8
|
|
8
9
|
def self.include_helpers!
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Temporality
|
2
|
+
module Transaction
|
3
|
+
|
4
|
+
QUEUE = :deferred_temporality_constraints
|
5
|
+
|
6
|
+
def transaction(*args, &block)
|
7
|
+
raise RuntimeError.new("There already is a currently active temporality transaction") if active_transaction?
|
8
|
+
|
9
|
+
ActiveRecord::Base.transaction(*args) do
|
10
|
+
begin
|
11
|
+
init_queue
|
12
|
+
block.call
|
13
|
+
process_queue
|
14
|
+
ensure
|
15
|
+
cleanup_queue
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def defer(key, &block)
|
21
|
+
Thread.current[QUEUE][key] = block
|
22
|
+
end
|
23
|
+
|
24
|
+
def active_transaction?
|
25
|
+
!!Thread.current[QUEUE]
|
26
|
+
end
|
27
|
+
|
28
|
+
def process_queue
|
29
|
+
Thread.current[QUEUE].values.each(&:call)
|
30
|
+
end
|
31
|
+
|
32
|
+
def init_queue
|
33
|
+
Thread.current[QUEUE] = Hash.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def cleanup_queue
|
37
|
+
Thread.current[QUEUE] = nil
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
@@ -1,5 +1,3 @@
|
|
1
|
-
require 'temporality/violation'
|
2
|
-
|
3
1
|
require 'temporality/auto_close'
|
4
2
|
require 'temporality/completeness'
|
5
3
|
require 'temporality/overlap'
|
@@ -8,11 +6,10 @@ require 'temporality/inclusion'
|
|
8
6
|
module Temporality
|
9
7
|
module Validation
|
10
8
|
|
11
|
-
|
9
|
+
CONSTRAINTS = {
|
12
10
|
inclusion: Inclusion,
|
13
11
|
prevent_overlap: Overlap,
|
14
|
-
completeness: Completeness
|
15
|
-
auto_close: AutoClose
|
12
|
+
completeness: Completeness
|
16
13
|
}
|
17
14
|
|
18
15
|
DEFAULTS = { inclusion: true, completeness: false, prevent_overlap: false, auto_close: false }
|
@@ -28,15 +25,21 @@ module Temporality
|
|
28
25
|
def validate_temporality_contraints!
|
29
26
|
validate_bounds_order
|
30
27
|
|
31
|
-
temporal_associations.each do |assoc,
|
32
|
-
constraints
|
28
|
+
temporal_associations.each do |assoc, constrs|
|
29
|
+
constraints = constrs.dup
|
30
|
+
|
31
|
+
if constraints.delete(:auto_close)
|
32
|
+
AutoClose.new(self, assoc).call
|
33
|
+
end
|
34
|
+
|
35
|
+
constraints.map { |constraint, enabled| constraint if enabled }.compact.each do |constraint|
|
33
36
|
validate_constraint(assoc, constraint)
|
34
37
|
end
|
35
38
|
end
|
36
39
|
end
|
37
40
|
|
38
|
-
def validate_constraint(assoc,
|
39
|
-
|
41
|
+
def validate_constraint(assoc, constraint_name)
|
42
|
+
CONSTRAINTS[constraint_name].new(self, assoc).validate
|
40
43
|
end
|
41
44
|
|
42
45
|
def validate_bounds_order
|
data/lib/temporality/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: temporality
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David FRANCOIS
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-10-
|
11
|
+
date: 2016-10-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -98,44 +98,44 @@ dependencies:
|
|
98
98
|
name: activerecord
|
99
99
|
requirement: !ruby/object:Gem::Requirement
|
100
100
|
requirements:
|
101
|
-
- - "
|
101
|
+
- - "~>"
|
102
102
|
- !ruby/object:Gem::Version
|
103
|
-
version: '0'
|
103
|
+
version: '5.0'
|
104
104
|
type: :development
|
105
105
|
prerelease: false
|
106
106
|
version_requirements: !ruby/object:Gem::Requirement
|
107
107
|
requirements:
|
108
|
-
- - "
|
108
|
+
- - "~>"
|
109
109
|
- !ruby/object:Gem::Version
|
110
|
-
version: '0'
|
110
|
+
version: '5.0'
|
111
111
|
- !ruby/object:Gem::Dependency
|
112
112
|
name: sqlite3
|
113
113
|
requirement: !ruby/object:Gem::Requirement
|
114
114
|
requirements:
|
115
|
-
- - "
|
115
|
+
- - "~>"
|
116
116
|
- !ruby/object:Gem::Version
|
117
|
-
version: '
|
117
|
+
version: '1.3'
|
118
118
|
type: :development
|
119
119
|
prerelease: false
|
120
120
|
version_requirements: !ruby/object:Gem::Requirement
|
121
121
|
requirements:
|
122
|
-
- - "
|
122
|
+
- - "~>"
|
123
123
|
- !ruby/object:Gem::Version
|
124
|
-
version: '
|
124
|
+
version: '1.3'
|
125
125
|
- !ruby/object:Gem::Dependency
|
126
126
|
name: byebug
|
127
127
|
requirement: !ruby/object:Gem::Requirement
|
128
128
|
requirements:
|
129
|
-
- - "
|
129
|
+
- - "~>"
|
130
130
|
- !ruby/object:Gem::Version
|
131
|
-
version: '0'
|
131
|
+
version: '9.0'
|
132
132
|
type: :development
|
133
133
|
prerelease: false
|
134
134
|
version_requirements: !ruby/object:Gem::Requirement
|
135
135
|
requirements:
|
136
|
-
- - "
|
136
|
+
- - "~>"
|
137
137
|
- !ruby/object:Gem::Version
|
138
|
-
version: '0'
|
138
|
+
version: '9.0'
|
139
139
|
description: Give records the ability to validate temporal constraints on themselves
|
140
140
|
and associations
|
141
141
|
email:
|
@@ -153,6 +153,7 @@ files:
|
|
153
153
|
- lib/temporality/completeness.rb
|
154
154
|
- lib/temporality/day_count.rb
|
155
155
|
- lib/temporality/default_boundary_values.rb
|
156
|
+
- lib/temporality/errors.rb
|
156
157
|
- lib/temporality/inclusion.rb
|
157
158
|
- lib/temporality/overlap.rb
|
158
159
|
- lib/temporality/schema.rb
|
@@ -161,10 +162,10 @@ files:
|
|
161
162
|
- lib/temporality/slice_collection.rb
|
162
163
|
- lib/temporality/time_span.rb
|
163
164
|
- lib/temporality/time_span_collection.rb
|
165
|
+
- lib/temporality/transaction.rb
|
164
166
|
- lib/temporality/validation.rb
|
165
167
|
- lib/temporality/validation_strategy.rb
|
166
168
|
- lib/temporality/version.rb
|
167
|
-
- lib/temporality/violation.rb
|
168
169
|
homepage: https://paygun.fr
|
169
170
|
licenses:
|
170
171
|
- MIT
|