almanack 1.0.5 → 1.1.0.beta1
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/.travis.yml +2 -0
- data/almanack.gemspec +1 -0
- data/example.ru +4 -4
- data/lib/almanack/base.rb +22 -0
- data/lib/almanack/calendar.rb +9 -27
- data/lib/almanack/configuration.rb +15 -4
- data/lib/almanack/event.rb +56 -5
- data/lib/almanack/event_source/ical_feed.rb +6 -2
- data/lib/almanack/event_source/meetup_group.rb +26 -5
- data/lib/almanack/event_source/static.rb +5 -1
- data/lib/almanack/representation/ical_feed.rb +59 -0
- data/lib/almanack/representation/json_feed.rb +55 -0
- data/lib/almanack/serialized_transformation.rb +53 -0
- data/lib/almanack/server/environment.rb +38 -0
- data/lib/almanack/server/helpers.rb +32 -28
- data/lib/almanack/server.rb +23 -37
- data/lib/almanack/themes/legacy/views/events.erb +2 -2
- data/lib/almanack/themes/starter/views/events.erb +2 -2
- data/lib/almanack/version.rb +1 -1
- data/lib/almanack.rb +4 -18
- data/spec/calendar_spec.rb +10 -3
- data/spec/configuration_spec.rb +10 -0
- data/spec/event_source/ical_feed_spec.rb +15 -2
- data/spec/event_source/meetup_group_spec.rb +23 -2
- data/spec/event_source/static_spec.rb +25 -7
- data/spec/event_spec.rb +80 -11
- data/spec/features/api_feature_spec.rb +24 -0
- data/spec/features/calendar_feature_spec.rb +3 -3
- data/spec/features/subscription_feature_spec.rb +5 -5
- data/spec/representation/json_feed_spec.rb +44 -0
- data/spec/serialized_transformation_spec.rb +45 -0
- data/spec/spec_helper.rb +2 -0
- data/spec/support/event_matchers.rb +1 -1
- data/templates/new/config.ru.tt +1 -1
- metadata +29 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 49d69b1b032ff7f536d34807c859021af3840f3e
|
4
|
+
data.tar.gz: b5ea878e85c09b1e967054e7038400fcc0125120
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d40154226158a3bbf56e6de69e1cd495827eef1563cbf88e8823257cdf8653c2eec181070a6be6e2ae0f0b19d9fc9c56a5b02471945f6ae35bba68cecb6151b9
|
7
|
+
data.tar.gz: 0a8723f866ee7403cd8d66044c16c6e93c197c5a9b7485d7af61da75a00a8cc31f9c2a04fc3cdfc40d22b80c903b9e9ebd703f2e8704ab34e8246b8a302bd024
|
data/.travis.yml
CHANGED
data/almanack.gemspec
CHANGED
@@ -24,6 +24,7 @@ Gem::Specification.new do |spec|
|
|
24
24
|
spec.add_dependency "addressable"
|
25
25
|
spec.add_dependency "thor"
|
26
26
|
spec.add_dependency "faraday"
|
27
|
+
spec.add_dependency "rack-contrib"
|
27
28
|
|
28
29
|
spec.add_development_dependency "bundler", "~> 1.5"
|
29
30
|
spec.add_development_dependency "rake"
|
data/example.ru
CHANGED
@@ -11,13 +11,13 @@ Almanack.config do |calendar|
|
|
11
11
|
calendar.add_events [
|
12
12
|
{
|
13
13
|
title: "Hogswatch",
|
14
|
-
|
15
|
-
|
14
|
+
start_time: now + Almanack::ONE_DAY,
|
15
|
+
end_time: now + Almanack::ONE_DAY * 2,
|
16
16
|
description: 'The sausages have been strung, the wreaths of oakleaves hung and the stockings dangled. The pork pie, the sherry and the all-important turnip await their festive guests. The poker lent against the fireplace may or may not have been bent over the head of some nightmare creature.',
|
17
17
|
location: 'Castle of Bones'
|
18
18
|
},
|
19
|
-
{ title: "Soul Cake Tuesday",
|
20
|
-
{ title: "Eve of Small Gods",
|
19
|
+
{ title: "Soul Cake Tuesday", start_time: now + 10 * Almanack::ONE_DAY },
|
20
|
+
{ title: "Eve of Small Gods", start_time: now + 30 * Almanack::ONE_DAY },
|
21
21
|
]
|
22
22
|
end
|
23
23
|
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Almanack
|
2
|
+
ONE_HOUR = 60 * 60
|
3
|
+
ONE_DAY = 24 * ONE_HOUR
|
4
|
+
ONE_MONTH = 30 * ONE_DAY
|
5
|
+
ONE_YEAR = 365 * ONE_DAY
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def config(&block)
|
9
|
+
@config ||= Configuration.new
|
10
|
+
yield @config if block_given?
|
11
|
+
@config
|
12
|
+
end
|
13
|
+
|
14
|
+
def calendar
|
15
|
+
@calendar ||= Calendar.new(config)
|
16
|
+
end
|
17
|
+
|
18
|
+
def reset!
|
19
|
+
config.reset!
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
data/lib/almanack/calendar.rb
CHANGED
@@ -2,13 +2,11 @@ require 'forwardable'
|
|
2
2
|
|
3
3
|
module Almanack
|
4
4
|
class Calendar
|
5
|
-
ONE_HOUR = 60 * 60
|
6
|
-
ONE_DAY = 24 * ONE_HOUR
|
7
|
-
ONE_MONTH = 30 * ONE_DAY
|
8
|
-
ONE_YEAR = 365 * ONE_DAY
|
9
|
-
|
10
5
|
extend Forwardable
|
11
|
-
def_delegators :@config, :event_sources,
|
6
|
+
def_delegators :@config, :event_sources,
|
7
|
+
:title,
|
8
|
+
:days_lookahead,
|
9
|
+
:feed_lookahead
|
12
10
|
|
13
11
|
def initialize(config)
|
14
12
|
@config = config
|
@@ -26,32 +24,16 @@ module Almanack
|
|
26
24
|
end.flatten
|
27
25
|
|
28
26
|
event_list.sort_by do |event|
|
29
|
-
event.
|
27
|
+
event.start_time.to_time
|
30
28
|
end
|
31
29
|
end
|
32
30
|
|
33
31
|
def ical_feed
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
# Three hours is the duration for events missing end dates, a
|
38
|
-
# recommendation suggested by Meetup.com.
|
39
|
-
three_hours = 3 * ONE_HOUR
|
40
|
-
|
41
|
-
ical = RiCal.Calendar
|
42
|
-
|
43
|
-
events_between(now..future).each do |event|
|
44
|
-
ical_event = RiCal.Event
|
45
|
-
ical_event.summary = event.title
|
46
|
-
ical_event.dtstart = event.start_date.utc
|
47
|
-
ical_event.dtend = (event.end_date || event.start_date + three_hours).utc
|
48
|
-
ical_event.description = event.description if event.description
|
49
|
-
ical_event.location = event.location if event.location
|
50
|
-
|
51
|
-
ical.add_subcomponent(ical_event)
|
52
|
-
end
|
32
|
+
Representation::IcalFeed.from(self).to_s
|
33
|
+
end
|
53
34
|
|
54
|
-
|
35
|
+
def json_feed
|
36
|
+
Representation::JSONFeed.from(self).to_s
|
55
37
|
end
|
56
38
|
end
|
57
39
|
end
|
@@ -4,9 +4,15 @@ module Almanack
|
|
4
4
|
|
5
5
|
DEFAULT_THEME = "legacy"
|
6
6
|
DEFAULT_DAYS_LOOKAHEAD = 30
|
7
|
+
DEFAULT_FEED_LOOKAHEAD = 365
|
7
8
|
|
8
9
|
attr_reader :event_sources
|
9
|
-
attr_accessor :title,
|
10
|
+
attr_accessor :title,
|
11
|
+
:theme,
|
12
|
+
:theme_paths,
|
13
|
+
:theme_root,
|
14
|
+
:days_lookahead,
|
15
|
+
:feed_lookahead
|
10
16
|
|
11
17
|
def initialize
|
12
18
|
reset!
|
@@ -19,6 +25,7 @@ module Almanack
|
|
19
25
|
def reset!
|
20
26
|
@theme = DEFAULT_THEME
|
21
27
|
@days_lookahead = DEFAULT_DAYS_LOOKAHEAD
|
28
|
+
@feed_lookahead = DEFAULT_FEED_LOOKAHEAD
|
22
29
|
@event_sources = []
|
23
30
|
|
24
31
|
@theme_paths = [
|
@@ -33,16 +40,20 @@ module Almanack
|
|
33
40
|
root || raise(ThemeNotFound, "Could not find theme #{theme} in #{paths}")
|
34
41
|
end
|
35
42
|
|
43
|
+
def add_event_source(source)
|
44
|
+
@event_sources << source
|
45
|
+
end
|
46
|
+
|
36
47
|
def add_ical_feed(url)
|
37
|
-
|
48
|
+
add_event_source EventSource::IcalFeed.new(url, connection: connection)
|
38
49
|
end
|
39
50
|
|
40
51
|
def add_events(events)
|
41
|
-
|
52
|
+
add_event_source EventSource::Static.new(events)
|
42
53
|
end
|
43
54
|
|
44
55
|
def add_meetup_group(options)
|
45
|
-
|
56
|
+
add_event_source EventSource::MeetupGroup.new(options.merge(connection: connection))
|
46
57
|
end
|
47
58
|
end
|
48
59
|
end
|
data/lib/almanack/event.rb
CHANGED
@@ -3,21 +3,72 @@ require 'ostruct'
|
|
3
3
|
module Almanack
|
4
4
|
class Event < OpenStruct
|
5
5
|
def formatted_date
|
6
|
-
formatted = "#{formatted_day(
|
6
|
+
formatted = "#{formatted_day(start_time)} at #{formatted_time(start_time)}"
|
7
7
|
|
8
|
-
if
|
8
|
+
if end_time
|
9
9
|
formatted << " to "
|
10
|
-
formatted << "#{formatted_day(
|
11
|
-
formatted << formatted_time(
|
10
|
+
formatted << "#{formatted_day(end_time)} at " unless ends_on_same_day?
|
11
|
+
formatted << formatted_time(end_time)
|
12
12
|
end
|
13
13
|
|
14
14
|
formatted
|
15
15
|
end
|
16
16
|
|
17
|
+
# Deprecated in favour of start_time
|
18
|
+
def start_date
|
19
|
+
deprecated :start_date, newer_method: :start_time
|
20
|
+
end
|
21
|
+
|
22
|
+
def start_time
|
23
|
+
read_attribute :start_time, fallback: :start_date
|
24
|
+
end
|
25
|
+
|
26
|
+
# Deprecated in favour of end_time
|
27
|
+
def end_date
|
28
|
+
deprecated :end_date, newer_method: :end_time
|
29
|
+
end
|
30
|
+
|
31
|
+
def end_time
|
32
|
+
read_attribute :end_time, fallback: :end_date
|
33
|
+
end
|
34
|
+
|
35
|
+
def serialized
|
36
|
+
each_pair.with_object({}) do |(attr, _), hash|
|
37
|
+
hash[attr] = serialize_attribute(attr)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
17
41
|
private
|
18
42
|
|
43
|
+
def serialize_attribute(attribute)
|
44
|
+
value = send(attribute)
|
45
|
+
value.is_a?(Time) ? value.iso8601 : value
|
46
|
+
end
|
47
|
+
|
48
|
+
def deprecated(older_method, options = {})
|
49
|
+
newer_method = options.delete(:newer_method)
|
50
|
+
value = read_attribute(newer_method, fallback: older_method)
|
51
|
+
warn "Event method #{older_method} is deprecated; use #{newer_method} instead"
|
52
|
+
value
|
53
|
+
end
|
54
|
+
|
55
|
+
def read_attribute(newer_method, options = {})
|
56
|
+
older_method = options.delete(:fallback)
|
57
|
+
newer_value = self[newer_method]
|
58
|
+
fallback_value = self[older_method]
|
59
|
+
|
60
|
+
if fallback_value && newer_value
|
61
|
+
raise "Both #{older_method} and #{newer_method} properties are set, please use #{newer_method} only instead"
|
62
|
+
elsif newer_value
|
63
|
+
newer_value
|
64
|
+
elsif fallback_value
|
65
|
+
warn "Deprecated event property #{older_method} is set; set #{newer_method} property instead"
|
66
|
+
fallback_value
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
19
70
|
def ends_on_same_day?
|
20
|
-
[
|
71
|
+
[start_time.year, start_time.yday] == [end_time.year, end_time.yday]
|
21
72
|
end
|
22
73
|
|
23
74
|
def formatted_time(time)
|
@@ -12,6 +12,10 @@ module Almanack
|
|
12
12
|
end
|
13
13
|
end
|
14
14
|
|
15
|
+
def serialized_between(date_range)
|
16
|
+
{ events: events_between(date_range).map(&:serialized) }
|
17
|
+
end
|
18
|
+
|
15
19
|
private
|
16
20
|
|
17
21
|
def each_ical_event(&block)
|
@@ -38,8 +42,8 @@ module Almanack
|
|
38
42
|
def event_from(occurrence)
|
39
43
|
Event.new(
|
40
44
|
title: occurrence.summary,
|
41
|
-
|
42
|
-
|
45
|
+
start_time: occurrence.dtstart,
|
46
|
+
end_time: occurrence.dtend,
|
43
47
|
description: occurrence.description,
|
44
48
|
location: occurrence.location
|
45
49
|
)
|
@@ -3,19 +3,40 @@ module Almanack
|
|
3
3
|
class MeetupGroup
|
4
4
|
def initialize(options = {})
|
5
5
|
@request_options = options
|
6
|
+
@group_properties = {}
|
6
7
|
end
|
7
8
|
|
8
9
|
def events_between(date_range)
|
9
10
|
events.select do |event|
|
10
|
-
event.
|
11
|
+
event.start_time >= date_range.min && event.start_time <= date_range.max
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
15
|
+
def serialized_between(date_range)
|
16
|
+
# TODO `events` must be called before @group_properties is accessed
|
17
|
+
serialized_events = events_between(date_range).map(&:serialized)
|
18
|
+
@group_properties.merge(events: serialized_events)
|
19
|
+
end
|
20
|
+
|
14
21
|
private
|
15
22
|
|
16
23
|
def events
|
17
|
-
|
18
|
-
|
24
|
+
results = MeetupAPIRequest.new(@request_options.clone).results
|
25
|
+
record_group_details_from results
|
26
|
+
results.map { |result| event_from(result) }
|
27
|
+
end
|
28
|
+
|
29
|
+
def record_group_details_from(results)
|
30
|
+
first_result = results.first
|
31
|
+
return if !first_result
|
32
|
+
|
33
|
+
group = first_result['group']
|
34
|
+
return if !group
|
35
|
+
|
36
|
+
@group_properties = {
|
37
|
+
name: group['name'],
|
38
|
+
url: "http://www.meetup.com/" + group['urlname']
|
39
|
+
}
|
19
40
|
end
|
20
41
|
|
21
42
|
def event_from(result)
|
@@ -29,8 +50,8 @@ module Almanack
|
|
29
50
|
|
30
51
|
Event.new(
|
31
52
|
title: event_name,
|
32
|
-
|
33
|
-
|
53
|
+
start_time: start_time,
|
54
|
+
end_time: end_time,
|
34
55
|
description: result['description'],
|
35
56
|
location: location_from_venue(result['venue']),
|
36
57
|
url: result['event_url']
|
@@ -7,10 +7,14 @@ module Almanack
|
|
7
7
|
|
8
8
|
def events_between(date_range)
|
9
9
|
events.select do |event|
|
10
|
-
event.
|
10
|
+
event.start_time >= date_range.min && event.start_time <= date_range.max
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
+
def serialized_between(date_range)
|
15
|
+
{ events: events_between(date_range).map(&:serialized) }
|
16
|
+
end
|
17
|
+
|
14
18
|
private
|
15
19
|
|
16
20
|
def events
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Almanack
|
2
|
+
module Representation
|
3
|
+
class IcalFeed
|
4
|
+
attr_reader :calendar
|
5
|
+
|
6
|
+
def initialize(calendar)
|
7
|
+
@calendar = calendar
|
8
|
+
end
|
9
|
+
|
10
|
+
def ical
|
11
|
+
@ical ||= ical_calendar
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_s
|
15
|
+
ical.to_s
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.from(calendar)
|
19
|
+
self.new(calendar)
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def events
|
25
|
+
calendar.events_between(now..lookahead)
|
26
|
+
end
|
27
|
+
|
28
|
+
def ical_calendar
|
29
|
+
events.each_with_object(RiCal.Calendar) do |event, calendar_component|
|
30
|
+
calendar_component.add_subcomponent ical_event_for(event)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def ical_event_for(event)
|
35
|
+
ical_event = RiCal.Event
|
36
|
+
ical_event.summary = event.title
|
37
|
+
ical_event.dtstart = event.start_time.utc
|
38
|
+
ical_event.dtend = (event.end_time || event.start_time + default_event_duration ).utc
|
39
|
+
ical_event.description = event.description if event.description
|
40
|
+
ical_event.location = event.location if event.location
|
41
|
+
ical_event
|
42
|
+
end
|
43
|
+
|
44
|
+
def lookahead
|
45
|
+
now + calendar.feed_lookahead * ONE_DAY
|
46
|
+
end
|
47
|
+
|
48
|
+
def default_event_duration
|
49
|
+
# Three hours is the duration for events missing end dates, a
|
50
|
+
# recommendation suggested by Meetup.com.
|
51
|
+
3 * ONE_HOUR
|
52
|
+
end
|
53
|
+
|
54
|
+
def now
|
55
|
+
@now ||= Time.now
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Almanack
|
2
|
+
module Representation
|
3
|
+
class JSONFeed
|
4
|
+
attr_reader :calendar
|
5
|
+
|
6
|
+
def initialize(calendar)
|
7
|
+
@calendar = calendar
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_s
|
11
|
+
json_friendly = SerializedTransformation.new(serialized)
|
12
|
+
json_friendly.key { |key| camelize(key.to_s) }
|
13
|
+
json_friendly.apply.to_json
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.from(calendar)
|
17
|
+
self.new(calendar)
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def camelize(string)
|
23
|
+
string.split('_').map.with_index do |part, index|
|
24
|
+
index.zero? ? part : part.capitalize
|
25
|
+
end.join
|
26
|
+
end
|
27
|
+
|
28
|
+
def date_range
|
29
|
+
now..lookahead
|
30
|
+
end
|
31
|
+
|
32
|
+
def serialized
|
33
|
+
{ event_sources: serialized_event_sources }
|
34
|
+
end
|
35
|
+
|
36
|
+
def serialized_event_sources
|
37
|
+
event_sources.map do |source|
|
38
|
+
source.serialized_between(date_range)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def event_sources
|
43
|
+
calendar.event_sources
|
44
|
+
end
|
45
|
+
|
46
|
+
def lookahead
|
47
|
+
now + calendar.feed_lookahead * ONE_DAY
|
48
|
+
end
|
49
|
+
|
50
|
+
def now
|
51
|
+
@now ||= Time.now
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module Almanack
|
2
|
+
class SerializedTransformation
|
3
|
+
|
4
|
+
def initialize(subject)
|
5
|
+
@subject = subject
|
6
|
+
@transformations = {}
|
7
|
+
end
|
8
|
+
|
9
|
+
def key(&block)
|
10
|
+
@transformations[:key] = block
|
11
|
+
end
|
12
|
+
|
13
|
+
def value(&block)
|
14
|
+
@transformations[:value] = block
|
15
|
+
end
|
16
|
+
|
17
|
+
def apply
|
18
|
+
recurse(cloned)
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def cloned
|
24
|
+
@subject.dup
|
25
|
+
end
|
26
|
+
|
27
|
+
def recursable?(node)
|
28
|
+
node.is_a?(Array) || node.is_a?(Hash)
|
29
|
+
end
|
30
|
+
|
31
|
+
def no_change
|
32
|
+
-> (obj) { obj }
|
33
|
+
end
|
34
|
+
|
35
|
+
def transformation(type, entity)
|
36
|
+
(@transformations[type] || no_change).call(entity)
|
37
|
+
end
|
38
|
+
|
39
|
+
def recurse(entity)
|
40
|
+
cloned = case entity
|
41
|
+
when Array
|
42
|
+
entity.map { |child| recurse(child) }
|
43
|
+
when Hash
|
44
|
+
entity.each_with_object({}) do |(key, value), hash|
|
45
|
+
transformed_key = recursable?(key) ? recurse(key) : transformation(:key, key)
|
46
|
+
hash[transformed_key] = recurse(value)
|
47
|
+
end
|
48
|
+
else
|
49
|
+
transformation(:value, entity)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Almanack
|
2
|
+
module ServerContext
|
3
|
+
module Environment
|
4
|
+
def basename(file)
|
5
|
+
Pathname(file).split.last.to_s.split(".", 2).first
|
6
|
+
end
|
7
|
+
|
8
|
+
def locate_asset(name, within: path)
|
9
|
+
name = basename(name)
|
10
|
+
path = settings.root.join(within)
|
11
|
+
available = Pathname.glob(path.join("*"))
|
12
|
+
asset = available.find { |path| basename(path) == name }
|
13
|
+
raise "Could not find stylesheet #{name} inside #{available}" if asset.nil?
|
14
|
+
asset
|
15
|
+
end
|
16
|
+
|
17
|
+
def auto_render_template(asset)
|
18
|
+
renderer = asset.extname.split(".").last
|
19
|
+
content = asset.read
|
20
|
+
respond_to?(renderer) ? send(renderer, content) : content
|
21
|
+
end
|
22
|
+
|
23
|
+
def auto_render_asset(*args)
|
24
|
+
auto_render_template locate_asset(*args)
|
25
|
+
end
|
26
|
+
|
27
|
+
def theme_stylesheet_path
|
28
|
+
settings.root.join('stylesheets')
|
29
|
+
end
|
30
|
+
|
31
|
+
def register_sass_loadpaths!
|
32
|
+
if !Sass.load_paths.include?(theme_stylesheet_path)
|
33
|
+
Sass.load_paths << theme_stylesheet_path
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -1,36 +1,40 @@
|
|
1
|
-
Almanack
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Almanack
|
2
|
+
module ServerContext
|
3
|
+
module Helpers
|
4
|
+
# @return The URL to your consolidated iCal feed.
|
5
|
+
def feed_url
|
6
|
+
"webcal://#{request.host}:#{request.port}/#{settings.feed_path}"
|
7
|
+
end
|
6
8
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
9
|
+
# @return The URL to Almanack's project homepage.
|
10
|
+
def almanack_project_url
|
11
|
+
Almanack::HOMEPAGE
|
12
|
+
end
|
11
13
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
# @return The URL to Almanack's issues page.
|
15
|
+
def almanack_issues_url
|
16
|
+
Almanack::ISSUES
|
17
|
+
end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
# @return The current time.
|
20
|
+
def now
|
21
|
+
Time.now
|
22
|
+
end
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
# @return The calendar.
|
25
|
+
def calendar
|
26
|
+
@calendar ||= Almanack.calendar
|
27
|
+
end
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
29
|
+
# @return The title of the page.
|
30
|
+
def page_title(separator: " – ")
|
31
|
+
[@title, calendar.title].compact.join(separator)
|
32
|
+
end
|
31
33
|
|
32
|
-
|
33
|
-
|
34
|
-
|
34
|
+
# Use to set the title of the page.
|
35
|
+
def title(value)
|
36
|
+
@title = value
|
37
|
+
end
|
38
|
+
end
|
35
39
|
end
|
36
40
|
end
|