calendarium-romanum 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8ee4894a3821935522076c8a8c749be997e243ee
4
+ data.tar.gz: bbc5bc6e3c9dd24427eac5d9bef2fa51a73122e3
5
+ SHA512:
6
+ metadata.gz: 0d38d06d95abf7994515a338fff2cf2b08970b2a19952eb39132ccc058f4c2e19e6673267461a4fd0e41dd4e95618cc2a8067981ded24c9477f27743bdd8c5f3
7
+ data.tar.gz: 79b0b9fa0aaaf6f1a2fbec31661b53c15b38fd4b60e23d8585813cbc61c4b3f73dd5a8978d6e52deda6e76be19c2833f73d92ba92ba93fa9e88f7899589e67ca
@@ -0,0 +1,65 @@
1
+ #!/bin/env ruby
2
+
3
+ require 'thor'
4
+ require 'calendarium-romanum'
5
+ require 'log4r'
6
+
7
+ module CalendariumRomanum
8
+
9
+ class CLI < Thor
10
+ include CalendariumRomanum::Util
11
+
12
+ desc 'errors FILE1, ...', 'finds errors in sanctorale data files'
13
+ def errors(*files)
14
+ logger = Log4r::Logger['CalendariumRomanum::SanctoraleLoader']
15
+ logger.outputters << Log4r::StderrOutputter.new('stderr')
16
+
17
+ loader = SanctoraleLoader.new
18
+ files.each do |path|
19
+ s = Sanctorale.new
20
+ loader.load_from_file s, path
21
+ end
22
+ end
23
+
24
+ desc 'cmp FILE1, FILE2', 'detect differences in rank and colour of corresponding celebrations'
25
+ def rccompare(a, b)
26
+ loader = SanctoraleLoader.new
27
+ sanctorales = []
28
+
29
+ [a, b].each do |source|
30
+ s = Sanctorale.new
31
+ loader.load_from_file s, source
32
+ sanctorales << s
33
+ end
34
+
35
+ Year.new(1990).each_day do |d|
36
+ celebs = sanctorales.collect {|s| s.get d.month, d.day }
37
+ if celebs.find {|cc| cc.nil? }
38
+ next
39
+ end
40
+
41
+ celebs[0].each_index do |i|
42
+ if i >= celebs[1].size
43
+ break
44
+ end
45
+
46
+ ca = celebs[0][i]
47
+ cb = celebs[1][i]
48
+
49
+ _print_cel = Proc.new {|c| puts "#{c.rank.priority} #{c.colour} | #{c.title}" }
50
+
51
+ if ca.rank != cb.rank || ca.colour != cb.colour
52
+ puts "#{d.month}/#{d.day}"
53
+ _print_cel.call ca
54
+ _print_cel.call cb
55
+ puts
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ if __FILE__ == $0
64
+ CalendariumRomanum::CLI.start ARGV
65
+ end
@@ -0,0 +1,13 @@
1
+ %w{
2
+ enums
3
+ calendar
4
+ temporale
5
+ sanctorale
6
+ sanctoraleloader
7
+ transfers
8
+ day
9
+ abstract_date
10
+ util
11
+ }.each do |f|
12
+ require_relative File.join('calendarium-romanum', f)
13
+ end
@@ -0,0 +1,59 @@
1
+ module CalendariumRomanum
2
+
3
+ # a date not bound to a particular year
4
+ class AbstractDate
5
+ include Comparable
6
+
7
+ def initialize(month, day)
8
+ validate! month, day
9
+ @month = month
10
+ @day = day
11
+ end
12
+
13
+ attr_reader :month, :day
14
+
15
+ def <=>(other)
16
+ if month != other.month
17
+ month <=> other.month
18
+ else
19
+ day <=> other.day
20
+ end
21
+ end
22
+
23
+ def hash
24
+ (month * 100 + day).hash
25
+ end
26
+
27
+ def eql?(other)
28
+ month == other.month && day == other.day
29
+ end
30
+
31
+ def concretize(year)
32
+ Date.new(year, month, day)
33
+ end
34
+
35
+ private
36
+
37
+ def validate!(month, day)
38
+ unless month >= 1 && month <= 12
39
+ raise RangeError.new("Invalid month #{month}.")
40
+ end
41
+
42
+ day_lte = case month
43
+ when 2
44
+ 28
45
+ when 1, 3, 5, 7, 8, 10, 12
46
+ 31
47
+ else
48
+ 30
49
+ end
50
+
51
+ unless day > 0 && day <= 31
52
+ raise RangeError.new("Invalid day #{day}.")
53
+ end
54
+ unless day <= day_lte
55
+ raise RangeError.new("Invalid day #{day} for month #{month}.")
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,130 @@
1
+ require 'date'
2
+ require 'forwardable'
3
+
4
+ module CalendariumRomanum
5
+
6
+ # Provides complete information concerning a liturgical year,
7
+ # it's days and celebrations occurring on them.
8
+ class Calendar
9
+
10
+ extend Forwardable
11
+
12
+ # year: Integer
13
+ # returns a calendar for the liturgical year beginning with
14
+ # Advent of the specified civil year.
15
+ def initialize(year, sanctorale=nil)
16
+ @year = year
17
+ @temporale = Temporale.new(year)
18
+ @sanctorale = sanctorale || Sanctorale.new
19
+ @transferred = Transfers.new(@temporale, @sanctorale)
20
+ end
21
+
22
+ class << self
23
+ def mk_date(*args)
24
+ ex = TypeError.new('Date, DateTime or three Integers expected')
25
+
26
+ if args.size == 3 then
27
+ args.each do |a|
28
+ unless a.is_a? Integer
29
+ raise ex
30
+ end
31
+ end
32
+ return Date.new *args
33
+
34
+ elsif args.size == 1 then
35
+ a = args.first
36
+ unless a.is_a? Date or a.is_a? DateTime
37
+ raise ex
38
+ end
39
+ return a
40
+
41
+ else
42
+ raise ex
43
+ end
44
+ end
45
+
46
+ # creates a Calendar for the liturgical year including given
47
+ # date
48
+ def for_day(date, sanctorale=nil)
49
+ return new(Temporale.liturgical_year(date), sanctorale)
50
+ end
51
+ end # class << self
52
+
53
+ def_delegators :@temporale, :range_check, :season
54
+ attr_reader :year
55
+ attr_reader :temporale
56
+ attr_reader :sanctorale
57
+
58
+ # returns a Calendar for the subsequent year
59
+ def succ
60
+ c = Calendar.new @year + 1, @sanctorale
61
+ return c
62
+ end
63
+
64
+ # returns a Calendar for the previous year
65
+ def pred
66
+ c = Calendar.new @year - 1, @sanctorale
67
+ return c
68
+ end
69
+
70
+ def ==(obj)
71
+ unless obj.is_a? Calendar
72
+ return false
73
+ end
74
+
75
+ return year == obj.year
76
+ end
77
+
78
+ # returns filled Day for the specified day
79
+ def day(*args)
80
+ if args.size == 2
81
+ date = Date.new(@year, *args)
82
+ unless @temporale.date_range.include? date
83
+ date = Date.new(@year + 1, *args)
84
+ end
85
+ else
86
+ date = self.class.mk_date *args
87
+ range_check date
88
+ end
89
+
90
+ s = @temporale.season(date)
91
+ return Day.new(
92
+ date: date,
93
+ season: s,
94
+ season_week: @temporale.season_week(s, date),
95
+ celebrations: celebrations_for(date)
96
+ )
97
+ end
98
+
99
+ # Sunday lectionary cycle
100
+ def lectionary
101
+ LECTIONARY_CYCLES[@year % 3]
102
+ end
103
+
104
+ # Ferial lectionary cycle
105
+ def ferial_lectionary
106
+ @year % 2 + 1
107
+ end
108
+
109
+ def celebrations_for(date)
110
+ tr = @transferred.get(date)
111
+ return [tr] if tr
112
+
113
+ t = @temporale.get date
114
+ st = @sanctorale.get date
115
+
116
+ unless st.empty?
117
+ if st.first.rank > t.rank
118
+ if st.first.rank == Ranks::MEMORIAL_OPTIONAL
119
+ st.unshift t
120
+ return st
121
+ else
122
+ return st
123
+ end
124
+ end
125
+ end
126
+
127
+ return [t]
128
+ end
129
+ end # class Calendar
130
+ end
@@ -0,0 +1,59 @@
1
+ require 'forwardable'
2
+
3
+ module CalendariumRomanum
4
+
5
+ # information on one particular day of the liturgical year
6
+ class Day
7
+ def initialize(args={})
8
+ %i(date season season_week celebrations).each do |a|
9
+ if args.include? a
10
+ instance_variable_set "@#{a}", args.delete(a)
11
+ end
12
+ end
13
+
14
+ unless args.empty?
15
+ raise ArgumentError.new "Unexpected arguments #{args.keys.join(', ')}"
16
+ end
17
+ end
18
+
19
+ attr_reader :date
20
+
21
+ def weekday
22
+ date.wday
23
+ end
24
+
25
+ # one of the Seasons (Symbol)
26
+ attr_reader :season
27
+
28
+ # week of the season (Integer)
29
+ attr_reader :season_week
30
+
31
+ # an Array of Celebrations, possibly empty
32
+ attr_reader :celebrations
33
+
34
+ # Celebration of the following day if it has first vespers
35
+ attr_reader :vespers
36
+ end
37
+
38
+ # information on one particular celebration of the liturgical year
39
+ # (like a Sunday, feast or memorial);
40
+ # some days have no (ferial office is used), some have one,
41
+ # some have more among which one may and may not be chosen
42
+ class Celebration
43
+ extend Forwardable
44
+
45
+ def initialize(title='', rank=Ranks::FERIAL, colour=Colours::GREEN)
46
+ @title = title
47
+ @rank = rank
48
+ @colour = colour
49
+ end
50
+
51
+ attr_reader :rank
52
+ def_delegators :@rank, :solemnity?, :feast?, :memorial?
53
+
54
+ attr_reader :title
55
+
56
+ attr_reader :colour
57
+ alias_method :color, :colour
58
+ end
59
+ end
@@ -0,0 +1,84 @@
1
+ module CalendariumRomanum
2
+
3
+ module Seasons
4
+ ADVENT = :advent
5
+ CHRISTMAS = :christmas
6
+ LENT = :lent
7
+ EASTER = :easter
8
+ ORDINARY = :ordinary
9
+ # is Triduum Sacrum a special season? For now I count Friday and Saturday
10
+ # to the Lent, Sunday to the Easter time
11
+ end
12
+
13
+ LECTIONARY_CYCLES = [:A, :B, :C]
14
+
15
+ class Rank < Struct.new(:priority, :desc, :short_desc)
16
+ include Comparable
17
+
18
+ @@instances = {}
19
+
20
+ def initialize(*args)
21
+ super(*args)
22
+
23
+ @@instances[self.priority] = self
24
+ end
25
+
26
+ def <=>(b)
27
+ b.priority <=> self.priority
28
+ end
29
+
30
+ alias_method :to_f, :priority
31
+ alias_method :to_s, :desc
32
+
33
+ def self.[](priority)
34
+ @@instances[priority]
35
+ end
36
+
37
+ def solemnity?
38
+ priority.to_i == 1
39
+ end
40
+
41
+ def feast?
42
+ priority.to_i == 2
43
+ end
44
+
45
+ def memorial?
46
+ priority.to_i == 3
47
+ end
48
+ end
49
+
50
+ # ranks of celebrations
51
+ module Ranks
52
+ # Values are at the same time references to sections
53
+ # of the Table of Liturgical Days.
54
+ # The lower value, the higher rank.
55
+ TRIDUUM = Rank.new 1.1, 'Easter triduum'
56
+ PRIMARY = Rank.new 1.2, 'Primary liturgical days' # description may not be exact
57
+ SOLEMNITY_GENERAL = Rank.new 1.3, 'Solemnities in the General Calendar', 'solemnity' # description may not be exact
58
+ SOLEMNITY_PROPER = Rank.new 1.4, 'Proper solemnities', 'solemnity'
59
+
60
+ FEAST_LORD_GENERAL = Rank.new 2.5, 'Feasts of the Lord in the General Calendar', 'feast'
61
+ SUNDAY_UNPRIVILEGED = Rank.new 2.6, 'Unprivileged Sundays'
62
+ FEAST_GENERAL = Rank.new 2.7, 'Feasts of saints in the General Calendar', 'feast'
63
+ FEAST_PROPER = Rank.new 2.8, 'Proper feasts', 'feast'
64
+ FERIAL_PRIVILEGED = Rank.new 2.9, 'Privileged ferials'
65
+
66
+ MEMORIAL_GENERAL = Rank.new 3.10, 'Obligatory memorials in the General Calendar', 'memorial'
67
+ MEMORIAL_PROPER = Rank.new 3.11, 'Proper obligatory memorials', 'memorial'
68
+ MEMORIAL_OPTIONAL = Rank.new 3.12, 'Optional memorials', 'optional memorial'
69
+ FERIAL = Rank.new 3.13, 'Unprivileged ferials', 'ferial'
70
+
71
+ def self.[](priority)
72
+ Rank[priority]
73
+ end
74
+ end
75
+
76
+ module Colours
77
+ GREEN = :green
78
+ VIOLET = :violet
79
+ WHITE = :white
80
+ RED = :red
81
+ end
82
+
83
+ Colors = Colours
84
+ end
@@ -0,0 +1,80 @@
1
+ module CalendariumRomanum
2
+
3
+ # knows the fixed-date celebrations
4
+ class Sanctorale
5
+
6
+ def initialize
7
+ @days = {}
8
+
9
+ @solemnities = {}
10
+ end
11
+
12
+ attr_reader :solemnities
13
+
14
+ def add(month, day, celebration)
15
+ date = AbstractDate.new(month, day)
16
+ unless @days.has_key? date
17
+ @days[date] = []
18
+ end
19
+
20
+ if celebration.solemnity?
21
+ @solemnities[date] = celebration
22
+ end
23
+
24
+ @days[date] << celebration
25
+ end
26
+
27
+ # replaces content of the given day by given celebrations
28
+ def replace(month, day, celebrations)
29
+ date = AbstractDate.new(month, day)
30
+
31
+ if celebrations.first.solemnity?
32
+ @solemnities[date] = celebrations.first
33
+ elsif @solemnities.has_key? date
34
+ @solemnities.delete date
35
+ end
36
+
37
+ @days[date] = celebrations
38
+ end
39
+
40
+ # adds all Celebrations from another instance
41
+ def update(sanctorale)
42
+ sanctorale.each_day do |date, celebrations|
43
+ replace date.month, date.day, celebrations
44
+ end
45
+ end
46
+
47
+ # returns an Array with one or more Celebrations
48
+ # scheduled for the given day
49
+ #
50
+ # expected arguments: Date or two Integers (month, day)
51
+ def get(*args)
52
+ if args.size == 1 && args[0].is_a?(Date)
53
+ month = args[0].month
54
+ day = args[0].day
55
+ else
56
+ month, day = args
57
+ end
58
+
59
+ date = AbstractDate.new(month, day)
60
+ return @days[date] || []
61
+ end
62
+
63
+ # for each day for which an entry is available
64
+ # yields an AbstractDate and an Array of Celebrations
65
+ def each_day
66
+ @days.each_pair do |date, celebrations|
67
+ yield date, celebrations
68
+ end
69
+ end
70
+
71
+ # returns count of the _days_ with celebrations filled
72
+ def size
73
+ @days.size
74
+ end
75
+
76
+ def empty?
77
+ @days.empty?
78
+ end
79
+ end
80
+ end