hanreki 1.2.0 → 4.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a867b87334a1a1be5954d74b9059cd465651f1a5
4
- data.tar.gz: 2961b20754273404809b8c9f8f1d2d789edfb879
2
+ SHA256:
3
+ metadata.gz: 944f39bf167421f37e9bb814289d58b77a791695cae547cca3d4afd7acd9d2b5
4
+ data.tar.gz: ca7b46cc65c16e5fe7c61ff6e4739d4a7d8194b64b35a767f312520ca987abfb
5
5
  SHA512:
6
- metadata.gz: a04b6f1fe15222d4de38772121cfe025a0df77008a58a726e09cec72bf0f2b4fa3650ac7ec4e4ef1f4ef1eca17d850bd8889369c920bab1dd332114a15d44709
7
- data.tar.gz: 8579bada5beea05a9b7cf71eea9e2e423a456425430e28034dc77e2c19e3a0f36cb2fba1db85351ac6b097a9bca8ec536c53036b288d96e2f466eb3d6ffed9e7
6
+ metadata.gz: 81b8d3882ee6bb4186b9d3c5e2acc7d43e5914bc9590bc686707526e247d809e43a90a69a3b35712dc509a304df58881be1386db997e48769bf328792d3b50a2
7
+ data.tar.gz: 0b406751aef3e864deba94e983811cf8e174cd75db217ecfc8b31ddc1e06cc339c40affcbdccf5e29f90e67cd753be4d188ea454ac37eaff9b01a56ebbad3aaa
data/README.md CHANGED
@@ -5,6 +5,11 @@
5
5
 
6
6
  Simple schedule manager for [CAMPHOR-](https://camph.net/)
7
7
 
8
+ ## Features
9
+ - Manage public & private schedules
10
+ - Manage events using simple CSV master files
11
+ - Convert master files to iCal and JSON files
12
+
8
13
  ## Installation
9
14
  ### Gem
10
15
  Install hanreki with gem command:
@@ -27,6 +32,23 @@ And then execute:
27
32
  ## Usage
28
33
  Run `hanreki help` or `bundle exec hanreki help`.
29
34
 
35
+ ### Create a master file
36
+ `hanreki blank` generates a master file for the next month.
37
+
38
+ ### Sync
39
+ `hanreki sync` generates iCal and JSON files from master files.
40
+
41
+ ### Edit
42
+ `hanreki edit` opens a editor and validates master files.
43
+ Hanreki chooses the editor in the following order:
44
+
45
+ 1. Editor specified by the environment variable: `CAMPHOR_SCHEDULE_EDITOR`
46
+ 2. Editor specified by the environment variable: `EDITOR`
47
+ 3. `vi`
48
+
49
+ ### Validation
50
+ `hanreki validate` validates master files.
51
+
30
52
  ## Development
31
53
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
32
54
 
data/hanreki.gemspec CHANGED
@@ -24,13 +24,13 @@ Gem::Specification.new do |spec|
24
24
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
25
25
  spec.require_paths = ['lib']
26
26
 
27
- spec.required_ruby_version = '>= 2.1.0'
27
+ spec.required_ruby_version = '>= 2.2.0'
28
28
 
29
- spec.add_dependency 'thor', '~> 0.19'
29
+ spec.add_dependency 'thor', '>= 0.19', '< 2.0'
30
30
  spec.add_dependency 'icalendar', '~> 2.3'
31
31
  spec.add_dependency 'json-schema', '~> 2.7'
32
32
 
33
- spec.add_development_dependency 'bundler', '~> 1.12'
34
- spec.add_development_dependency 'rake', '~> 12.0'
33
+ spec.add_development_dependency 'bundler', '~> 2.1'
34
+ spec.add_development_dependency 'rake', '~> 13.0'
35
35
  spec.add_development_dependency 'rspec', '~> 3.0'
36
36
  end
data/lib/hanreki/cli.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'date'
2
2
  require 'thor'
3
+ require 'hanreki/exceptions'
3
4
  require 'hanreki/schedule'
4
5
  require 'hanreki/time_util'
5
6
  require 'hanreki/version'
@@ -29,23 +30,18 @@ module Hanreki
29
30
  validate
30
31
  end
31
32
 
32
- desc 'sync', 'Sync iCal, JSON, and JSONP'
33
+ desc 'sync', 'Sync iCal and JSON'
33
34
  def sync
34
- schedule = Schedule.new
35
- schedule.load_master_files
35
+ schedule = load_schedule
36
+ schedule.sort_by_date!
36
37
  schedule.out_ical
37
38
  schedule.out_json
38
- schedule.out_jsonp
39
39
  end
40
40
 
41
41
  desc 'validate', 'Validate master files'
42
42
  def validate
43
43
  schedule = Schedule.new
44
- begin
45
- schedule.load_master_files
46
- rescue => e
47
- raise "validation error: #{e}"
48
- end
44
+ schedule = load_schedule
49
45
  end
50
46
 
51
47
  desc 'version', 'Show the version information'
@@ -56,6 +52,24 @@ module Hanreki
56
52
 
57
53
  private
58
54
 
55
+ def load_schedule()
56
+ schedule = Schedule.new
57
+ begin
58
+ schedule.load_master_files
59
+ rescue ValidationError => e
60
+ message = 'validation error'
61
+ if e.event
62
+ message += " at line #{e.event.line_number}" if e.event.line_number
63
+ message += " in #{e.event.filename}" if e.event.filename
64
+ message += "\n#{e.event.row.to_s.strip}" if e.event.row
65
+ end
66
+ message += "\n#{e}"
67
+ puts message
68
+ exit 1
69
+ end
70
+ schedule
71
+ end
72
+
59
73
  # Parse a string (YYYYMM) in options[:date] and returns [YYYY, MM]
60
74
  def parse_month_in_options(options, default = nil)
61
75
  date = options[:date]
data/lib/hanreki/event.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  require 'icalendar'
2
+ require 'hanreki/exceptions'
2
3
  require 'hanreki/time_util'
3
4
 
4
5
  # Event of CAMPHOR- Schedule
5
6
  class Event
6
7
  using ExtendTime
7
8
  DOWS = %w(日 月 火 水 木 金 土)
9
+ attr_accessor :filename, :line_number, :row
8
10
  attr_accessor :start, :end
9
11
  attr_accessor :public_summary, :private_summary
10
12
  attr_accessor :url
@@ -23,29 +25,34 @@ class Event
23
25
  # month: String (e.g. 201510)
24
26
  # line: CSV::Row
25
27
  # (eg. [01, 水, 15:00, 20:00, "HOUSE 開館", "HOUSE 当番 @ivstivs", https://camph.net])
26
- def self.from_master(month, line)
27
- event = Event.new({})
28
+ def self.from_master(filename, line_number, month, line)
29
+ event = Event.new({
30
+ row: line,
31
+ filename: filename,
32
+ line_number: line_number,
33
+ })
28
34
  first_day = Date.parse("#{month}01")
35
+ raise ValidationError.new(event), 'invalid number of columns' unless line.length == 7
29
36
  day, dow, hour_start, hour_end, public_summary, private_summary, url = line.fields.map { |c| c.to_s.strip }
30
37
  # Zero-fill (eg. 1 -> 01, 02 -> 02)
31
38
  day = day.rjust(2, "0")
32
- raise ArgumentError, 'invalid day' unless day.length == 2
33
- raise ArgumentError, 'invalid day' unless (first_day...first_day.next_month).include?(Date.parse("#{month}#{day}"))
39
+ raise ValidationError.new(event), 'invalid day' unless day.length == 2
40
+ raise ValidationError.new(event), 'invalid day' unless (first_day...first_day.next_month).include?(Date.parse("#{month}#{day}"))
34
41
  event.start = Time.parse("#{month}#{day} #{hour_start} +09:00").getlocal("+09:00")
35
42
  event.end = Time.parse("#{month}#{day} #{hour_end} +09:00").getlocal("+09:00")
36
43
  event.public_summary = public_summary unless public_summary.empty?
37
44
  event.private_summary = private_summary unless private_summary.empty?
38
45
  event.url = url unless url.empty?
39
46
  # 曜日はインスタンス変数に保持しないのでここでチェックする
40
- raise ArgumentError, 'invalid day of the week' unless DOWS.include? dow
41
- raise ArgumentError, 'invalid day of the week' unless DOWS[event.start.wday] == dow
47
+ raise ValidationError.new(event), 'invalid day of the week' unless DOWS.include? dow
48
+ raise ValidationError.new(event), 'invalid day of the week' unless DOWS[event.start.wday] == dow
42
49
  event.validate
43
50
  event
44
51
  end
45
52
 
46
53
  def to_h(type, time_type = :time)
47
- fail ArgumentError, 'Invalid type' unless [:public, :private].include?(type)
48
- fail ArgumentError, 'Invalid time_type' unless [:time, :string].include?(time_type)
54
+ fail ValidationError.new(self), 'invalid type' unless [:public, :private].include?(type)
55
+ fail ValidationError.new(self), 'invalid time_type' unless [:time, :string].include?(time_type)
49
56
  hash = {
50
57
  start: @start,
51
58
  end: @end,
@@ -82,35 +89,41 @@ class Event
82
89
  # イベントのバリデーションを行う
83
90
  # バリデーションに成功した場合は true を返し, 失敗した場合は例外を送出する
84
91
  def validate
85
- raise ArgumentError, 'invalid start & end' if @start > @end
92
+ raise ValidationError.new(self), 'invalid start & end' if @start > @end
86
93
 
87
94
  if @public_summary.nil? and @private_summary.nil?
88
- raise ArgumentError, 'both summaries are not set'
95
+ raise ValidationError.new(self), 'both summaries are not set'
89
96
  end
90
97
 
91
98
  if @public_summary == 'Open'
92
99
  if @start == @end
93
- raise ArgumentError, '"open" event should have duration'
100
+ raise ValidationError.new(self), '"open" event should have duration'
101
+ end
102
+ end
103
+
104
+ if @public_summary == 'Online Open'
105
+ if @start == @end
106
+ raise ValidationError.new(self), '"online open" event should have duration'
94
107
  end
95
108
  end
96
109
 
97
110
  if @private_summary == 'Closed'
98
111
  if not @public_summary.nil?
99
- raise ArgumentError, 'invalid public summary for a closed event'
112
+ raise ValidationError.new(self), 'invalid public summary for a closed event'
100
113
  end
101
114
 
102
115
  if not @start.is_midnight_in_jst
103
- raise ArgumentError, 'start must be 0:00 for a closed event'
116
+ raise ValidationError.new(self), 'start must be 0:00 for a closed event'
104
117
  end
105
118
 
106
119
  if not @end.is_midnight_in_jst
107
- raise ArgumentError, 'end must be 0:00 for a closed event'
120
+ raise ValidationError.new(self), 'end must be 0:00 for a closed event'
108
121
  end
109
122
  end
110
123
 
111
124
  unless @url.nil?
112
125
  unless @url.start_with?('http://') or @url.start_with?('https://')
113
- raise ArgumentError, 'invalid url scheme'
126
+ raise ValidationError.new(self), 'invalid url scheme'
114
127
  end
115
128
  end
116
129
 
@@ -0,0 +1,8 @@
1
+ class ValidationError < StandardError
2
+ attr_reader :event
3
+
4
+ def initialize(event)
5
+ super
6
+ @event = event
7
+ end
8
+ end
@@ -19,8 +19,8 @@ class ICalendar
19
19
  end
20
20
 
21
21
  def set_event(event, type)
22
- fail ArgumentError, 'Invalid date' unless event.start && event.end
23
- fail ArgumentError, 'Invalid type' unless [:public, :private].include?(type)
22
+ fail ValidationError.new(event), 'invalid date' unless event.start && event.end
23
+ fail ValidationError.new(event), 'invalid type' unless [:public, :private].include?(type)
24
24
  @calendar.event do |e|
25
25
  e.dtstart = event.start
26
26
  e.dtend = event.end
@@ -13,8 +13,6 @@ class Schedule
13
13
  ICAL_PRIVATE_PATH = 'ical/camphor_private_events.ics'
14
14
  JSON_PUBLIC_PATH = 'json/camphor_public_events.json'
15
15
  JSON_PRIVATE_PATH = 'json/camphor_private_events.json'
16
- JSONP_PUBLIC_PATH = 'jsonp/camphor_public_events.js'
17
- JSONP_PRIVATE_PATH = 'jsonp/camphor_private_events.js'
18
16
  JSON_SCHEMA_PATH = File.expand_path('../schema.json', __FILE__)
19
17
 
20
18
  def initialize
@@ -35,7 +33,7 @@ class Schedule
35
33
  @events = Dir.glob('master/*.csv').flat_map do |f|
36
34
  month = f.match(/master\/(\d+).csv/)[1]
37
35
  CSV.open(f, headers: true) do |csv|
38
- csv.map { |line| Event.from_master(month, line) }
36
+ csv.map { |row| Event.from_master(f, $., month, row) }
39
37
  end
40
38
  end
41
39
  end
@@ -64,16 +62,6 @@ class Schedule
64
62
  end
65
63
  end
66
64
 
67
- # Output private and public JSONP calendar files
68
- def out_jsonp(callback = 'callback')
69
- File.open(JSONP_PUBLIC_PATH, 'w') do |f|
70
- f.write("#{callback}(#{events_to_json(public_events, :public)});")
71
- end
72
- File.open(JSONP_PRIVATE_PATH, 'w') do |f|
73
- f.write("#{callback}(#{events_to_json(private_events, :private)});")
74
- end
75
- end
76
-
77
65
  # Output master file
78
66
  def out_master
79
67
  CSV.open(
@@ -88,6 +76,10 @@ class Schedule
88
76
  File.exists? file_path
89
77
  end
90
78
 
79
+ def sort_by_date!
80
+ @events.sort_by! { |e| [e.start, e.end] }
81
+ end
82
+
91
83
  private
92
84
 
93
85
  def file_path
@@ -1,3 +1,3 @@
1
1
  module Hanreki
2
- VERSION = '1.2.0'
2
+ VERSION = '4.2.0'
3
3
  end
metadata CHANGED
@@ -1,29 +1,35 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanreki
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 4.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - CAMPHOR-
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-05-03 00:00:00.000000000 Z
11
+ date: 2021-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - "~>"
17
+ - - ">="
18
18
  - !ruby/object:Gem::Version
19
19
  version: '0.19'
20
+ - - "<"
21
+ - !ruby/object:Gem::Version
22
+ version: '2.0'
20
23
  type: :runtime
21
24
  prerelease: false
22
25
  version_requirements: !ruby/object:Gem::Requirement
23
26
  requirements:
24
- - - "~>"
27
+ - - ">="
25
28
  - !ruby/object:Gem::Version
26
29
  version: '0.19'
30
+ - - "<"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
27
33
  - !ruby/object:Gem::Dependency
28
34
  name: icalendar
29
35
  requirement: !ruby/object:Gem::Requirement
@@ -58,28 +64,28 @@ dependencies:
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '1.12'
67
+ version: '2.1'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '1.12'
74
+ version: '2.1'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: rake
71
77
  requirement: !ruby/object:Gem::Requirement
72
78
  requirements:
73
79
  - - "~>"
74
80
  - !ruby/object:Gem::Version
75
- version: '12.0'
81
+ version: '13.0'
76
82
  type: :development
77
83
  prerelease: false
78
84
  version_requirements: !ruby/object:Gem::Requirement
79
85
  requirements:
80
86
  - - "~>"
81
87
  - !ruby/object:Gem::Version
82
- version: '12.0'
88
+ version: '13.0'
83
89
  - !ruby/object:Gem::Dependency
84
90
  name: rspec
85
91
  requirement: !ruby/object:Gem::Requirement
@@ -110,6 +116,7 @@ files:
110
116
  - lib/hanreki.rb
111
117
  - lib/hanreki/cli.rb
112
118
  - lib/hanreki/event.rb
119
+ - lib/hanreki/exceptions.rb
113
120
  - lib/hanreki/i_calendar.rb
114
121
  - lib/hanreki/schedule.rb
115
122
  - lib/hanreki/schema.json
@@ -127,15 +134,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
127
134
  requirements:
128
135
  - - ">="
129
136
  - !ruby/object:Gem::Version
130
- version: 2.1.0
137
+ version: 2.2.0
131
138
  required_rubygems_version: !ruby/object:Gem::Requirement
132
139
  requirements:
133
140
  - - ">="
134
141
  - !ruby/object:Gem::Version
135
142
  version: '0'
136
143
  requirements: []
137
- rubyforge_project:
138
- rubygems_version: 2.6.8
144
+ rubygems_version: 3.0.3
139
145
  signing_key:
140
146
  specification_version: 4
141
147
  summary: Simple schedule manager for CAMPHOR-