calendarium-romanum-remote 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1922aae05ab92482cef8245ff0af153704256dcf
4
+ data.tar.gz: 37b09a459b50be3e1e8a85d5df6a55c903bb87dc
5
+ SHA512:
6
+ metadata.gz: a9d35cac14c7720322963a0470e7025d25a3ca834ddfe7d822459ff89d0e210d4f3125c0c91013215e0faab7d466f27ecfcf01f41add018aa4678a900a82f8b8
7
+ data.tar.gz: f0306395509292979378465024b9b38bc6378acd38c195de2aa89790edcfb92df0d02d25ae49d710c27c9a28c747d2ff8344680473aae43e5c8e51ca17a2d7a9
@@ -0,0 +1,2 @@
1
+ require 'calendarium-romanum'
2
+ require_relative 'calendarium-romanum/remote'
@@ -0,0 +1,17 @@
1
+ module CalendariumRomanum
2
+ module Remote; end
3
+ end
4
+
5
+ require 'net/http'
6
+ require 'json'
7
+
8
+ %w(
9
+ version
10
+ calendar
11
+ drivers
12
+ drivers/net_http_driver
13
+ errors
14
+ v0/deserializer
15
+ ).each do |path|
16
+ require_relative File.join('remote', path)
17
+ end
@@ -0,0 +1,75 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ # Mostly API-compatible with CalendariumRomanum::Calendar
4
+ # (only constructor differs).
5
+ # Instead of computing calendar data, obtains them
6
+ # from a remote calendar API
7
+ # https://github.com/igneus/church-calendar-api
8
+ class Calendar
9
+ extend Forwardable
10
+
11
+ def initialize(year, calendar_uri, api_version: :v0, driver: :net_http)
12
+ @year = year
13
+ @calendar_uri = calendar_uri
14
+ @driver =
15
+ if driver.is_a? Symbol
16
+ # built-in driver specified by a Symbol
17
+ Drivers.get(api_version, driver)
18
+ else
19
+ # driver instance
20
+ driver
21
+ end
22
+ @deserializer = V0::Deserializer.new
23
+
24
+ # only for most fundamental computations made locally
25
+ @temporale = Temporale.new(year)
26
+ # only for API compatibility
27
+ @sanctorale = nil
28
+ end
29
+
30
+ attr_reader :year
31
+ attr_reader :temporale
32
+ attr_reader :sanctorale
33
+ attr_reader :calendar_uri
34
+
35
+ def_delegators :@temporale, :range_check, :season
36
+
37
+ def day(*args)
38
+ # TODO code copied from CalendariumRomanum::Calendar -
39
+ # extract to a separate method
40
+ if args.size == 2
41
+ date = Date.new(@year, *args)
42
+ unless @temporale.date_range.include? date
43
+ date = Date.new(@year + 1, *args)
44
+ end
45
+ else
46
+ date = CalendariumRomanum::Calendar.mk_date *args
47
+ #range_check date
48
+ end
49
+
50
+ serialized = @driver.get date, @calendar_uri
51
+ @deserializer.call serialized
52
+ end
53
+
54
+ def lectionary
55
+ year_spec['lectionary'].to_sym
56
+ end
57
+
58
+ def ferial_lectionary
59
+ year_spec['ferial_lectionary'].to_i
60
+ end
61
+
62
+ def ==(obj)
63
+ self.class == obj.class &&
64
+ self.year == obj.year &&
65
+ self.calendar_uri == obj.calendar_uri
66
+ end
67
+
68
+ private
69
+
70
+ def year_spec
71
+ @year_spec ||= JSON.parse(@driver.year(@year, @calendar_uri))
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,17 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ module Drivers
4
+ def self.get(api_version, driver_id)
5
+ if api_version == :v0
6
+ if driver_id == :net_http
7
+ return NetHttpDriver.new
8
+ else
9
+ raise ArgumentError.new("Unsupported driver #{driver_id}")
10
+ end
11
+ else
12
+ raise ArgumentError.new("Unsupported API version #{api_version.inspect}")
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,52 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ module Drivers
4
+ # Communicates with the remote API using Ruby standard library
5
+ class NetHttpDriver
6
+ def get(date, calendar_uri)
7
+ uri_str =
8
+ calendar_uri +
9
+ (calendar_uri.end_with?('/') ? '' : '/') +
10
+ "#{date.year}/#{date.month}/#{date.day}"
11
+ uri = URI(uri_str)
12
+
13
+ get_request uri
14
+ end
15
+
16
+ def year(year, calendar_uri)
17
+ uri_str =
18
+ calendar_uri +
19
+ (calendar_uri.end_with?('/') ? '' : '/') +
20
+ year.to_s
21
+ uri = URI(uri_str)
22
+
23
+ get_request uri
24
+ end
25
+
26
+ private
27
+
28
+ def get_request(uri)
29
+ begin
30
+ response = Net::HTTP.get_response uri
31
+ rescue SocketError, Errno::ECONNREFUSED => err
32
+ raise ServerNotFoundError.new err.message
33
+ rescue Timeout::Error, Errno::EINVAL,
34
+ Errno::ECONNRESET, EOFError,
35
+ Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError,
36
+ Net::ProtocolError => err
37
+ raise TransportError.new err.message
38
+ end
39
+
40
+ if response.code == '200'
41
+ return response.body
42
+ elsif response.code == '400'
43
+ json = JSON.parse(response.body)
44
+ raise BadRequestError.new(json['error'])
45
+ else
46
+ raise Error.new("Unexpected status #{response.code.inspect}")
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,15 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ # parent of the gem's error classes,
4
+ # never itself instantiated
5
+ class Error < ::RuntimeError; end
6
+
7
+ # server not found
8
+ class ServerNotFoundError < Error; end
9
+
10
+ # server refuses submitted input
11
+ class BadRequestError < Error; end
12
+
13
+ class TransportError < Error; end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ module V0
4
+ class Deserializer
5
+ def call(day_str)
6
+ parsed = JSON.parse day_str
7
+
8
+ season_sym = parsed['season'].to_sym
9
+
10
+ CalendariumRomanum::Day.new(
11
+ date: Date.parse(parsed['date']),
12
+ season: CalendariumRomanum::Seasons.all.find {|s| s.symbol == season_sym},
13
+ season_week: parsed['season_week'],
14
+ celebrations: parsed['celebrations'].collect do |c|
15
+ colour_sym = c['colour'].to_sym
16
+
17
+ CalendariumRomanum::Celebration.new(
18
+ c['title'],
19
+ CalendariumRomanum::Ranks[c['rank_num']],
20
+ CalendariumRomanum::Colours.all.find {|c| c.symbol == colour_sym }
21
+ )
22
+ end
23
+ )
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,6 @@
1
+ module CalendariumRomanum
2
+ module Remote
3
+ VERSION = '0.1.0'
4
+ RELEASE_DATE = Date.new(2017, 9, 2)
5
+ end
6
+ end
@@ -0,0 +1,104 @@
1
+ require_relative 'spec_helper'
2
+
3
+ describe CalendariumRomanum::Remote::Calendar do
4
+ CR = CalendariumRomanum
5
+ CRR = CalendariumRomanum::Remote
6
+
7
+ let(:year) { 2016 }
8
+ let(:calendar) { described_class.new year, REMOTE_CALENDAR_URI }
9
+
10
+ describe '#year' do
11
+ it 'returns the year passed to the constructor' do
12
+ y = 2200
13
+ cal = described_class.new y, REMOTE_CALENDAR_URI
14
+ expect(cal.year).to eq y
15
+ end
16
+ end
17
+
18
+ describe '#lectionary' do
19
+ it { expect(calendar.lectionary).to be :A }
20
+ end
21
+
22
+ describe '#ferial_lectionary' do
23
+ it { expect(calendar.ferial_lectionary).to be 1 }
24
+ end
25
+
26
+ describe '#==' do
27
+ describe 'same year and URI' do
28
+ let(:c) { described_class.new year, REMOTE_CALENDAR_URI }
29
+
30
+ it 'is same' do
31
+ expect(c == calendar).to be true
32
+ end
33
+ end
34
+
35
+ describe 'year differs' do
36
+ let(:c) { described_class.new year + 1, REMOTE_CALENDAR_URI }
37
+
38
+ it 'is different' do
39
+ expect(c == calendar).to be false
40
+ end
41
+ end
42
+
43
+ describe 'URI differs' do
44
+ let(:c) { described_class.new year, 'http://other.uri' }
45
+
46
+ it 'is different' do
47
+ expect(c == calendar).to be false
48
+ end
49
+ end
50
+ end
51
+
52
+ describe 'API-compatibility with Calendar' do
53
+ cls = CR::Calendar
54
+
55
+ describe 'instance methods' do
56
+ let(:year) { 2000 }
57
+
58
+ let(:origin) { CR::Calendar.new(year) }
59
+ let(:mirror) { CRR::Calendar.new(year, REMOTE_CALENDAR_URI) }
60
+
61
+ cls.public_instance_methods.each do |method|
62
+ describe method do
63
+ it 'is defined by origin' do
64
+ expect(origin).to respond_to method
65
+ end
66
+
67
+ it 'is defined by mirror' do
68
+ expect(mirror).to respond_to method
69
+ end
70
+
71
+ it 'definitions match' do
72
+ morig = origin.method method
73
+ mmirr = mirror.method method
74
+
75
+ expect(mmirr.arity).to eq morig.arity
76
+ expect(mmirr.parameters).to eq morig.parameters
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+
83
+ describe 'returns the same data as Calendar' do
84
+ let(:year) { 2016 }
85
+
86
+ # classical calendar
87
+ let(:sanctorale) { CR::Data::GENERAL_ROMAN_ENGLISH.load }
88
+ let(:calendar) { CR::Calendar.new(year, sanctorale) }
89
+
90
+ # remote calendar with the same settings
91
+ let(:uri) { REMOTE_CALENDAR_URI }
92
+ let(:remote) { described_class.new(year, uri) }
93
+
94
+ days = (CR::Temporale::Dates.first_advent_sunday(2016) ... CR::Temporale::Dates.first_advent_sunday(2017))
95
+ days.each do |date|
96
+ it date do
97
+ c = calendar.day date
98
+ r = remote.day date
99
+
100
+ expect(r).to eq c
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec_helper'
2
+
3
+ describe CalendariumRomanum::Remote::Drivers do
4
+ CRRem = CalendariumRomanum::Remote
5
+
6
+ let(:base_uri) { REMOTE_CALENDAR_URI }
7
+ let(:date) { Date.new 2000, 1, 1 }
8
+
9
+ shared_examples 'any driver' do
10
+ describe 'success' do
11
+ it 'returns a string' do
12
+ r = driver.get date, base_uri
13
+ expect(r).to be_a String
14
+ expect(r).not_to be_empty
15
+ end
16
+
17
+ it 'returns a valid JSON' do
18
+ r = driver.get date, base_uri
19
+ expect do
20
+ JSON.parse(r)
21
+ end.not_to raise_exception
22
+ end
23
+ end
24
+
25
+ describe 'server not found' do
26
+ let(:base_uri) { 'http://unknown.server.xyz' }
27
+
28
+ it 'fails' do
29
+ expect do
30
+ driver.get date, base_uri
31
+ end.to raise_exception CRRem::ServerNotFoundError
32
+ end
33
+ end
34
+
35
+ describe 'connection refused' do
36
+ let(:base_uri) { 'http://localhost:33333' }
37
+
38
+ it 'fails' do
39
+ expect do
40
+ driver.get date, base_uri
41
+ end.to raise_exception CRRem::ServerNotFoundError
42
+ end
43
+ end
44
+
45
+ describe 'calendar not found' do
46
+ let(:base_uri) { REMOTE_CALENDAR_URI.sub(/(?<=calendars\/).*$/, 'unknown-calendar') }
47
+
48
+ it 'fails' do
49
+ expect do
50
+ driver.get date, base_uri
51
+ end.to raise_exception CRRem::BadRequestError
52
+ end
53
+ end
54
+
55
+ describe 'invalid date' do
56
+ # this won't ever happen, but the driver must be prepared
57
+ # to handle bad request scenarios
58
+ let(:date) { double(year: 2000, month: 13, day: 40) }
59
+
60
+ it 'fails' do
61
+ expect do
62
+ driver.get date, base_uri
63
+ end.to raise_exception CRRem::BadRequestError
64
+ end
65
+ end
66
+ end
67
+
68
+ describe CalendariumRomanum::Remote::Drivers::NetHttpDriver do
69
+ let(:driver) { described_class.new }
70
+
71
+ it_behaves_like 'any driver'
72
+
73
+ # implementation-specific error handling
74
+ describe 'network error' do
75
+ [
76
+ Timeout::Error,
77
+ Errno::EINVAL,
78
+ Errno::ECONNRESET,
79
+ EOFError,
80
+ Net::HTTPBadResponse,
81
+ Net::HTTPHeaderSyntaxError,
82
+ Net::ProtocolError
83
+ ].each do |etype|
84
+ it 'fails' do
85
+ allow(Net::HTTP).to receive(:get_response).and_raise(etype)
86
+
87
+ expect do
88
+ driver.get date, base_uri
89
+ end.to raise_exception CRRem::TransportError
90
+ end
91
+ end
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,52 @@
1
+ require 'spec_helper'
2
+
3
+ class MarkdownDocument
4
+ def initialize(str)
5
+ @str = str
6
+ end
7
+
8
+ def each_ruby_example
9
+ example = nil
10
+ line = nil
11
+ @str.each_line.with_index(1) do |l, i|
12
+ if example.nil?
13
+ if example_beginning?(l)
14
+ example = ''
15
+ line = i + 1
16
+ end
17
+ elsif example_end?(l)
18
+ yield example, line
19
+ example = nil
20
+ else
21
+ example += l
22
+ end
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def example_beginning?(line)
29
+ line =~ /^```ruby/
30
+ end
31
+
32
+ def example_end?(line)
33
+ line =~ /```/
34
+ end
35
+ end
36
+
37
+ %w(README.md).each do |path|
38
+ describe path do
39
+ readme_path = File.expand_path('../../' + path, __FILE__)
40
+ readme = File.read readme_path
41
+ doc = MarkdownDocument.new readme
42
+
43
+ doc.each_ruby_example do |code, line|
44
+ describe "example L#{line}" do
45
+ it 'executes without failure' do
46
+ cls = Class.new
47
+ cls.class_eval(code, readme_path, line)
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,105 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # The generated `.rspec` file contains `--require spec_helper` which will cause
4
+ # this file to always be loaded, without a need to explicitly require it in any
5
+ # files.
6
+ #
7
+ # Given that it is always loaded, you are encouraged to keep this file as
8
+ # light-weight as possible. Requiring heavyweight dependencies from this file
9
+ # will add to the boot time of your test suite on EVERY test run, even for an
10
+ # individual file that may not need all of that loaded. Instead, consider making
11
+ # a separate helper file that requires the additional dependencies and performs
12
+ # the additional setup, and require it from the spec files that actually need
13
+ # it.
14
+ #
15
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
16
+ RSpec.configure do |config|
17
+ # rspec-expectations config goes here. You can use an alternate
18
+ # assertion/expectation library such as wrong or the stdlib/minitest
19
+ # assertions if you prefer.
20
+ config.expect_with :rspec do |expectations|
21
+ # This option will default to `true` in RSpec 4. It makes the `description`
22
+ # and `failure_message` of custom matchers include text for helper methods
23
+ # defined using `chain`, e.g.:
24
+ # be_bigger_than(2).and_smaller_than(4).description
25
+ # # => "be bigger than 2 and smaller than 4"
26
+ # ...rather than:
27
+ # # => "be bigger than 2"
28
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
29
+ end
30
+
31
+ # rspec-mocks config goes here. You can use an alternate test double
32
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
33
+ config.mock_with :rspec do |mocks|
34
+ # Prevents you from mocking or stubbing a method that does not exist on
35
+ # a real object. This is generally recommended, and will default to
36
+ # `true` in RSpec 4.
37
+ mocks.verify_partial_doubles = true
38
+ end
39
+
40
+ # This option will default to `:apply_to_host_groups` in RSpec 4 (and will
41
+ # have no way to turn it off -- the option exists only for backwards
42
+ # compatibility in RSpec 3). It causes shared context metadata to be
43
+ # inherited by the metadata hash of host groups and examples, rather than
44
+ # triggering implicit auto-inclusion in groups with matching metadata.
45
+ config.shared_context_metadata_behavior = :apply_to_host_groups
46
+
47
+ # The settings below are suggested to provide a good initial experience
48
+ # with RSpec, but feel free to customize to your heart's content.
49
+ =begin
50
+ # This allows you to limit a spec run to individual examples or groups
51
+ # you care about by tagging them with `:focus` metadata. When nothing
52
+ # is tagged with `:focus`, all examples get run. RSpec also provides
53
+ # aliases for `it`, `describe`, and `context` that include `:focus`
54
+ # metadata: `fit`, `fdescribe` and `fcontext`, respectively.
55
+ config.filter_run_when_matching :focus
56
+
57
+ # Allows RSpec to persist some state between runs in order to support
58
+ # the `--only-failures` and `--next-failure` CLI options. We recommend
59
+ # you configure your source control system to ignore this file.
60
+ config.example_status_persistence_file_path = "spec/examples.txt"
61
+
62
+ # Limits the available syntax to the non-monkey patched syntax that is
63
+ # recommended. For more details, see:
64
+ # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/
65
+ # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/
66
+ # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode
67
+ config.disable_monkey_patching!
68
+
69
+ # This setting enables warnings. It's recommended, but in some cases may
70
+ # be too noisy due to issues in dependencies.
71
+ config.warnings = true
72
+
73
+ # Many RSpec users commonly either run the entire suite or an individual
74
+ # file, and it's useful to allow more verbose output when running an
75
+ # individual spec file.
76
+ if config.files_to_run.one?
77
+ # Use the documentation formatter for detailed output,
78
+ # unless a formatter has already been configured
79
+ # (e.g. via a command-line flag).
80
+ config.default_formatter = "doc"
81
+ end
82
+
83
+ # Print the 10 slowest examples and example groups at the
84
+ # end of the spec run, to help surface which specs are running
85
+ # particularly slow.
86
+ config.profile_examples = 10
87
+
88
+ # Run specs in random order to surface order dependencies. If you find an
89
+ # order dependency and want to debug it, you can fix the order by providing
90
+ # the seed, which is printed after each run.
91
+ # --seed 1234
92
+ config.order = :random
93
+
94
+ # Seed global randomization in this process using the `--seed` CLI option.
95
+ # Setting this allows you to use `--seed` to deterministically reproduce
96
+ # test failures related to randomization by passing the same `--seed` value
97
+ # as the one that triggered the failure.
98
+ Kernel.srand config.seed
99
+ =end
100
+ end
101
+
102
+ require_relative '../lib/calendarium-romanum-remote'
103
+
104
+ REMOTE_CALENDAR_URI =
105
+ ENV['REMOTE_CALENDAR'] || 'http://calapi.inadiutorium.cz/api/v0/en/calendars/general-en/'
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: calendarium-romanum-remote
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jakub Pavlík
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-09-02 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: calendarium-romanum
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.4.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.4.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '12.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '12.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.6'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.6'
55
+ description: obtains calendar data from an API
56
+ email: jkb.pavlik@gmail.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/calendarium-romanum-remote.rb
62
+ - lib/calendarium-romanum/remote.rb
63
+ - lib/calendarium-romanum/remote/calendar.rb
64
+ - lib/calendarium-romanum/remote/drivers.rb
65
+ - lib/calendarium-romanum/remote/drivers/net_http_driver.rb
66
+ - lib/calendarium-romanum/remote/errors.rb
67
+ - lib/calendarium-romanum/remote/v0/deserializer.rb
68
+ - lib/calendarium-romanum/remote/version.rb
69
+ - spec/calendar_spec.rb
70
+ - spec/drivers_spec.rb
71
+ - spec/readme_spec.rb
72
+ - spec/spec_helper.rb
73
+ homepage: http://github.com/igneus/calendarium-romanum-remote
74
+ licenses:
75
+ - LGPL-3.0
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.5.1
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: remote calendar extension for calendarium-romanum
98
+ test_files: []