plansheet 0.12.0 → 0.15.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 +4 -4
- data/.rubocop.yml +6 -2
- data/Gemfile.lock +1 -1
- data/exe/plansheet +6 -7
- data/lib/plansheet/pool.rb +53 -0
- data/lib/plansheet/project/stringify.rb +49 -0
- data/lib/plansheet/project/yaml.rb +167 -0
- data/lib/plansheet/project.rb +16 -189
- data/lib/plansheet/version.rb +1 -1
- data/lib/plansheet.rb +2 -19
- metadata +5 -3
- data/.rubocop_todo.yml +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f121593269dc11059fe547d8b72d58c2ad1a6685ae4826ac39daaff8b33f7c9a
|
4
|
+
data.tar.gz: 4020dab046e837f02b64cfc975408240d472b04a26bc6f40e7e552d915d77336
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ae82c5c2df93d55e520262754cc1c6e8b1b80a9a2fe230c09d00bd7ed4a38790c71b0dfcde5ac178e38112a26284219f21a8d99e48212601bbbcfae98607c0d1
|
7
|
+
data.tar.gz: 6ca757244abd515dcd86bbedb5a31590a53fbc8720e35167549bfe326c1f96673acef49b0dd1044a0e92f43253ef1442d126872859cebfa0f1185bb06612da59
|
data/.rubocop.yml
CHANGED
@@ -1,5 +1,3 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
1
|
AllCops:
|
4
2
|
TargetRubyVersion: 2.6
|
5
3
|
NewCops: enable
|
@@ -15,3 +13,9 @@ Style/StringLiteralsInInterpolation:
|
|
15
13
|
|
16
14
|
Layout/LineLength:
|
17
15
|
Max: 120
|
16
|
+
|
17
|
+
Metrics:
|
18
|
+
Enabled: false
|
19
|
+
|
20
|
+
Style/Documentation:
|
21
|
+
Enabled: false
|
data/Gemfile.lock
CHANGED
data/exe/plansheet
CHANGED
@@ -25,18 +25,17 @@ options = {}
|
|
25
25
|
parser.parse!(into: options)
|
26
26
|
|
27
27
|
config = Plansheet.load_config
|
28
|
+
pool = Plansheet::Pool.new({ projects_dir: config["projects_dir"] })
|
28
29
|
|
29
30
|
if options[:sheet] || options.empty?
|
30
|
-
project_arr = Plansheet.load_projects_dir config["projects_dir"]
|
31
|
-
|
32
31
|
Dir.mkdir config["output_dir"] unless Dir.exist? config["output_dir"]
|
33
|
-
|
34
|
-
Plansheet::Sheet.new("#{config["output_dir"]}/projects.md", project_arr)
|
32
|
+
Plansheet::Sheet.new("#{config["output_dir"]}/projects.md", pool.projects)
|
35
33
|
elsif options[:sort]
|
36
|
-
|
34
|
+
# Pool sorts projects, this now just matches old behaviour
|
35
|
+
pool.write_projects
|
37
36
|
elsif options[:cli]
|
38
|
-
|
39
|
-
project_arr.
|
37
|
+
# TODO: add a project filter method
|
38
|
+
project_arr = pool.projects
|
40
39
|
project_arr.delete_if { |x| x.status == "dropped" || x.status == "done" }
|
41
40
|
project_arr.select! { |x| x.location == options[:location_filter] } if options[:location_filter]
|
42
41
|
project_arr.each do |proj|
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plansheet
|
4
|
+
# The "pool" is the aggregated collection of projects, calendar events, etc.
|
5
|
+
class Pool
|
6
|
+
attr_accessor :projects
|
7
|
+
|
8
|
+
def initialize(config)
|
9
|
+
@projects_dir = config[:projects_dir]
|
10
|
+
# @completed_projects_dir = config(:completed_projects_dir)
|
11
|
+
|
12
|
+
load_projects_dir(@projects_dir)
|
13
|
+
# TODO: Slurp all files
|
14
|
+
sort_projects
|
15
|
+
end
|
16
|
+
|
17
|
+
def sort_projects
|
18
|
+
@projects.sort!
|
19
|
+
end
|
20
|
+
|
21
|
+
def project_namespaces
|
22
|
+
@projects.collect(&:namespace).uniq.sort
|
23
|
+
end
|
24
|
+
|
25
|
+
def projects_in_namespace(namespace)
|
26
|
+
@projects.select { |x| x.namespace == namespace }
|
27
|
+
end
|
28
|
+
|
29
|
+
def write_projects
|
30
|
+
# TODO: This leaves potential for duplicate projects where empty files
|
31
|
+
# are involved once completed project directories are a thing - will need
|
32
|
+
# to keep a list of project files to delete
|
33
|
+
project_namespaces.each do |ns|
|
34
|
+
# TODO: move this to ProjectYAMLFile
|
35
|
+
#
|
36
|
+
f = "#{@projects_dir}/#{ns}.yml"
|
37
|
+
pyf = ProjectYAMLFile.new f
|
38
|
+
pyf.projects = projects_in_namespace(ns)
|
39
|
+
pyf.write
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def load_projects_dir(dir)
|
44
|
+
project_arr = []
|
45
|
+
projects = Dir.glob("*yml", base: dir)
|
46
|
+
projects.each do |l|
|
47
|
+
project_arr << ProjectYAMLFile.new(File.join(dir, l)).load_file
|
48
|
+
end
|
49
|
+
|
50
|
+
@projects = project_arr.flatten!
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Plansheet
|
4
|
+
class Project
|
5
|
+
def to_s
|
6
|
+
str = String.new
|
7
|
+
str << "# "
|
8
|
+
str << "#{@namespace} - " if @namespace
|
9
|
+
str << "#{@name}\n"
|
10
|
+
STRING_PROPERTIES.each do |o|
|
11
|
+
str << stringify_string_property(o)
|
12
|
+
end
|
13
|
+
DATE_PROPERTIES.each do |o|
|
14
|
+
str << stringify_string_property(o)
|
15
|
+
end
|
16
|
+
ARRAY_PROPERTIES.each do |o|
|
17
|
+
str << stringify_array_property(o)
|
18
|
+
end
|
19
|
+
str
|
20
|
+
end
|
21
|
+
|
22
|
+
def stringify_string_property(prop)
|
23
|
+
if instance_variable_defined? "@#{prop}"
|
24
|
+
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
25
|
+
else
|
26
|
+
""
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def stringify_date_property(prop)
|
31
|
+
if instance_variable_defined? "@#{prop}"
|
32
|
+
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
33
|
+
else
|
34
|
+
""
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def stringify_array_property(prop)
|
39
|
+
str = String.new
|
40
|
+
if instance_variable_defined? "@#{prop}"
|
41
|
+
str << "#{prop}:\n"
|
42
|
+
instance_variable_get("@#{prop}").each do |t|
|
43
|
+
str << "- #{t}\n"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
str
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,167 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "yaml"
|
4
|
+
require "date"
|
5
|
+
require "pathname"
|
6
|
+
|
7
|
+
module Plansheet
|
8
|
+
# Once there's some stability in plansheet and dc-kwalify, will pre-load this
|
9
|
+
# to save the later YAML.load
|
10
|
+
PROJECT_YAML_SCHEMA = <<~YAML
|
11
|
+
desc: dc-tasks project schema
|
12
|
+
type: seq
|
13
|
+
sequence:
|
14
|
+
- type: map
|
15
|
+
mapping:
|
16
|
+
"project":
|
17
|
+
desc: Project name
|
18
|
+
type: str
|
19
|
+
"namespace":
|
20
|
+
desc: Project name
|
21
|
+
type: str
|
22
|
+
"priority":
|
23
|
+
desc: Project priority
|
24
|
+
type: str
|
25
|
+
enum:
|
26
|
+
- high
|
27
|
+
- medium
|
28
|
+
- low
|
29
|
+
"status":
|
30
|
+
desc: The current status of the project
|
31
|
+
type: str
|
32
|
+
enum:
|
33
|
+
- wip # project is a work-in-progress
|
34
|
+
- ready # project has tasks, ready to go
|
35
|
+
- waiting # project in waiting on some external person/event
|
36
|
+
- blocked # project is blocked by another project, but otherwise ready/wip
|
37
|
+
- planning # project in planning phase (set manually)
|
38
|
+
- idea # project is little more than an idea
|
39
|
+
- dropped # project has been explicitly dropped, but
|
40
|
+
# want to keep around for reference, etc
|
41
|
+
- done # project is finished, but want to keep around
|
42
|
+
# for reference, etc.
|
43
|
+
"location":
|
44
|
+
desc: Location
|
45
|
+
type: str
|
46
|
+
"notes":
|
47
|
+
desc: Free-form notes string
|
48
|
+
type: str
|
49
|
+
"time_estimate":
|
50
|
+
desc: The estimated amount of time before a project is completed
|
51
|
+
type: str
|
52
|
+
"frequency":
|
53
|
+
desc: The amount of time before a recurring project moves to ready status again from when it was last done (WIP)
|
54
|
+
type: str
|
55
|
+
pattern: /\\d+[dwDW]/
|
56
|
+
"lead_time":
|
57
|
+
desc: The amount of time before a recurring project is "due" moved to ready where the project (sort of a deferral mechanism) (WIP)
|
58
|
+
type: str
|
59
|
+
pattern: /\\d+[dwDW]/
|
60
|
+
"due":
|
61
|
+
desc: Due date of the task
|
62
|
+
type: date
|
63
|
+
"defer":
|
64
|
+
desc: Defer task until this day
|
65
|
+
type: date
|
66
|
+
"completed_on":
|
67
|
+
desc: When the (non-recurring) project was completed
|
68
|
+
type: date
|
69
|
+
"created_on":
|
70
|
+
desc: When the project was created
|
71
|
+
type: date
|
72
|
+
"starts_on":
|
73
|
+
desc: For ICS (WIP)
|
74
|
+
type: date
|
75
|
+
"last_reviewed":
|
76
|
+
desc: When the project was last reviewed (WIP)
|
77
|
+
type: date
|
78
|
+
"last_done":
|
79
|
+
desc: When the recurring project was last completed (WIP)
|
80
|
+
type: date
|
81
|
+
"dependencies":
|
82
|
+
desc: The names of projects that need to be completed before this project can be started/completed
|
83
|
+
type: seq
|
84
|
+
sequence:
|
85
|
+
- type: str
|
86
|
+
"externals":
|
87
|
+
desc: List of external commitments, ie who else cares about project completion?
|
88
|
+
type: seq
|
89
|
+
sequence:
|
90
|
+
- type: str
|
91
|
+
"urls":
|
92
|
+
desc: List of URLs that may be pertinent
|
93
|
+
type: seq
|
94
|
+
sequence:
|
95
|
+
- type: str
|
96
|
+
"tasks":
|
97
|
+
desc: List of tasks to do
|
98
|
+
type: seq
|
99
|
+
sequence:
|
100
|
+
- type: str
|
101
|
+
"done":
|
102
|
+
desc: List of tasks which have been completed
|
103
|
+
type: seq
|
104
|
+
sequence:
|
105
|
+
- type: str
|
106
|
+
"tags":
|
107
|
+
desc: List of tags (WIP)
|
108
|
+
type: seq
|
109
|
+
sequence:
|
110
|
+
- type: str
|
111
|
+
YAML
|
112
|
+
PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
|
113
|
+
|
114
|
+
class ProjectYAMLFile
|
115
|
+
attr_accessor :projects
|
116
|
+
|
117
|
+
def initialize(path)
|
118
|
+
@path = path
|
119
|
+
# TODO: this won't GC, inline validation instead?
|
120
|
+
end
|
121
|
+
|
122
|
+
def load_file
|
123
|
+
# Handle pre-Ruby 3.1 psych versions (this is brittle)
|
124
|
+
@raw = if Psych::VERSION.split(".")[0].to_i >= 4
|
125
|
+
YAML.load_file(@path, permitted_classes: [Date])
|
126
|
+
else
|
127
|
+
YAML.load_file(@path)
|
128
|
+
end
|
129
|
+
|
130
|
+
validate_schema
|
131
|
+
@raw ||= []
|
132
|
+
@projects = @raw.map do |proj|
|
133
|
+
proj["namespace"] = namespace
|
134
|
+
Project.new proj
|
135
|
+
end
|
136
|
+
@projects
|
137
|
+
end
|
138
|
+
|
139
|
+
def namespace
|
140
|
+
# TODO: yikes
|
141
|
+
::Pathname.new(@path).basename.to_s.gsub(/\.yml$/, "")
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_schema
|
145
|
+
validator = Kwalify::Validator.new(Plansheet::PROJECT_SCHEMA)
|
146
|
+
errors = validator.validate(@raw)
|
147
|
+
# Check YAML validity
|
148
|
+
return unless errors && !errors.empty?
|
149
|
+
|
150
|
+
$stderr.write "Schema errors in #{@path}:\n"
|
151
|
+
errors.each { |err| puts "- [#{err.path}] #{err.message}" }
|
152
|
+
abort
|
153
|
+
end
|
154
|
+
|
155
|
+
def sort!
|
156
|
+
@projects.sort!
|
157
|
+
end
|
158
|
+
|
159
|
+
def write
|
160
|
+
File.write @path, yaml_dump
|
161
|
+
end
|
162
|
+
|
163
|
+
def yaml_dump
|
164
|
+
YAML.dump(@projects.map { |x| x.to_h.except("namespace") })
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
data/lib/plansheet/project.rb
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
|
3
3
|
require "yaml"
|
4
4
|
require "date"
|
5
|
+
require_relative "project/yaml"
|
6
|
+
require_relative "project/stringify"
|
5
7
|
|
6
8
|
module Plansheet
|
7
9
|
PROJECT_STATUS_PRIORITY = {
|
@@ -21,107 +23,6 @@ module Plansheet
|
|
21
23
|
"low" => 3
|
22
24
|
}.freeze
|
23
25
|
|
24
|
-
# Once there's some stability in plansheet and dc-kwalify, will pre-load this
|
25
|
-
# to save the later YAML.load
|
26
|
-
PROJECT_YAML_SCHEMA = <<~YAML
|
27
|
-
desc: dc-tasks project schema
|
28
|
-
type: seq
|
29
|
-
sequence:
|
30
|
-
- type: map
|
31
|
-
mapping:
|
32
|
-
"project":
|
33
|
-
desc: Project name
|
34
|
-
type: str
|
35
|
-
required: yes
|
36
|
-
"priority":
|
37
|
-
desc: Project priority
|
38
|
-
type: str
|
39
|
-
enum:
|
40
|
-
- high
|
41
|
-
- medium
|
42
|
-
- low
|
43
|
-
"status":
|
44
|
-
desc: The current status of the project
|
45
|
-
type: str
|
46
|
-
enum:
|
47
|
-
- wip # project is a work-in-progress
|
48
|
-
- ready # project has tasks, ready to go
|
49
|
-
- waiting # project in waiting on some external person/event
|
50
|
-
- blocked # project is blocked by another project, but otherwise ready/wip
|
51
|
-
- planning # project in planning phase (set manually)
|
52
|
-
- idea # project is little more than an idea
|
53
|
-
- dropped # project has been explicitly dropped, but
|
54
|
-
# want to keep around for reference, etc
|
55
|
-
- done # project is finished, but want to keep around
|
56
|
-
# for reference, etc.
|
57
|
-
"location":
|
58
|
-
desc: Location
|
59
|
-
type: str
|
60
|
-
"notes":
|
61
|
-
desc: Free-form notes string
|
62
|
-
type: str
|
63
|
-
"time_estimate":
|
64
|
-
desc: The estimated amount of time before a project is completed
|
65
|
-
type: str
|
66
|
-
"frequency":
|
67
|
-
desc: The amount of time before a recurring project moves to ready status again from when it was last done (WIP)
|
68
|
-
type: str
|
69
|
-
pattern: /\\d+[dwDW]/
|
70
|
-
"lead_time":
|
71
|
-
desc: The amount of time before a recurring project is "due" moved to ready where the project (sort of a deferral mechanism) (WIP)
|
72
|
-
type: str
|
73
|
-
pattern: /\\d+[dwDW]/
|
74
|
-
"due":
|
75
|
-
desc: Due date of the task
|
76
|
-
type: date
|
77
|
-
"defer":
|
78
|
-
desc: Defer task until this day
|
79
|
-
type: date
|
80
|
-
"created_on":
|
81
|
-
desc: When the project was created
|
82
|
-
type: date
|
83
|
-
"starts_on":
|
84
|
-
desc: For ICS (WIP)
|
85
|
-
type: date
|
86
|
-
"last_reviewed":
|
87
|
-
desc: When the project was last reviewed (WIP)
|
88
|
-
type: date
|
89
|
-
"last_done":
|
90
|
-
desc: When the recurring project was last completed (WIP)
|
91
|
-
type: date
|
92
|
-
"dependencies":
|
93
|
-
desc: The names of projects that need to be completed before this project can be started/completed
|
94
|
-
type: seq
|
95
|
-
sequence:
|
96
|
-
- type: str
|
97
|
-
"externals":
|
98
|
-
desc: List of external commitments, ie who else cares about project completion?
|
99
|
-
type: seq
|
100
|
-
sequence:
|
101
|
-
- type: str
|
102
|
-
"urls":
|
103
|
-
desc: List of URLs that may be pertinent
|
104
|
-
type: seq
|
105
|
-
sequence:
|
106
|
-
- type: str
|
107
|
-
"tasks":
|
108
|
-
desc: List of tasks to do
|
109
|
-
type: seq
|
110
|
-
sequence:
|
111
|
-
- type: str
|
112
|
-
"done":
|
113
|
-
desc: List of tasks which have been completed
|
114
|
-
type: seq
|
115
|
-
sequence:
|
116
|
-
- type: str
|
117
|
-
"tags":
|
118
|
-
desc: List of tags (WIP)
|
119
|
-
type: seq
|
120
|
-
sequence:
|
121
|
-
- type: str
|
122
|
-
YAML
|
123
|
-
PROJECT_SCHEMA = YAML.safe_load(PROJECT_YAML_SCHEMA)
|
124
|
-
|
125
26
|
def self.parse_date_duration(str)
|
126
27
|
return Regexp.last_match(1).to_i if str.strip.match(/(\d+)[dD]/)
|
127
28
|
return (Regexp.last_match(1).to_i * 7) if str.strip.match(/(\d+)[wW]/)
|
@@ -147,16 +48,19 @@ module Plansheet
|
|
147
48
|
status
|
148
49
|
].map { |x| "compare_#{x}".to_sym }.freeze
|
149
50
|
# NOTE: The order of these affects presentation!
|
51
|
+
# namespace is derived from file name
|
150
52
|
STRING_PROPERTIES = %w[priority status location notes time_estimate frequency lead_time].freeze
|
151
|
-
DATE_PROPERTIES = %w[due defer created_on starts_on last_done last_reviewed].freeze
|
53
|
+
DATE_PROPERTIES = %w[due defer completed_on created_on starts_on last_done last_reviewed].freeze
|
152
54
|
ARRAY_PROPERTIES = %w[dependencies externals urls tasks done tags].freeze
|
153
55
|
|
154
56
|
ALL_PROPERTIES = STRING_PROPERTIES + DATE_PROPERTIES + ARRAY_PROPERTIES
|
155
57
|
|
156
58
|
attr_reader :name, *ALL_PROPERTIES
|
59
|
+
attr_accessor :namespace
|
157
60
|
|
158
61
|
def initialize(options)
|
159
62
|
@name = options["project"]
|
63
|
+
@namespace = options["namespace"]
|
160
64
|
|
161
65
|
ALL_PROPERTIES.each do |o|
|
162
66
|
instance_variable_set("@#{o}", options[o]) if options[o]
|
@@ -240,8 +144,8 @@ module Plansheet
|
|
240
144
|
def status
|
241
145
|
return @status if @status
|
242
146
|
return recurring_status if recurring?
|
243
|
-
return task_based_status if
|
244
|
-
|
147
|
+
return task_based_status if @tasks || @done
|
148
|
+
return "done" if @completed_on && @tasks.nil?
|
245
149
|
|
246
150
|
"idea"
|
247
151
|
end
|
@@ -260,18 +164,19 @@ module Plansheet
|
|
260
164
|
|
261
165
|
def recurring_status
|
262
166
|
# add frequency to last_done
|
263
|
-
|
264
|
-
# This recurring project is being done for the first time
|
265
|
-
task_based_status
|
266
|
-
else
|
167
|
+
if @last_done
|
267
168
|
# This project has been done once before
|
268
169
|
subsequent_recurring_status
|
170
|
+
else
|
171
|
+
# This recurring project is being done for the first time
|
172
|
+
task_based_status
|
269
173
|
end
|
270
174
|
end
|
271
175
|
|
272
176
|
def subsequent_recurring_status
|
273
177
|
return "done" if @lead_time && defer > Date.today
|
274
178
|
return "done" if due > Date.today
|
179
|
+
|
275
180
|
task_based_status
|
276
181
|
end
|
277
182
|
|
@@ -284,6 +189,7 @@ module Plansheet
|
|
284
189
|
def due
|
285
190
|
return @due if @due
|
286
191
|
return recurring_due_date if recurring?
|
192
|
+
|
287
193
|
nil
|
288
194
|
end
|
289
195
|
|
@@ -298,6 +204,7 @@ module Plansheet
|
|
298
204
|
def defer
|
299
205
|
return @defer if @defer
|
300
206
|
return lead_time_deferral if @lead_time && due
|
207
|
+
|
301
208
|
nil
|
302
209
|
end
|
303
210
|
|
@@ -314,50 +221,8 @@ module Plansheet
|
|
314
221
|
status == "dropped" || status == "done"
|
315
222
|
end
|
316
223
|
|
317
|
-
def to_s
|
318
|
-
str = String.new
|
319
|
-
str << "# #{@name}\n"
|
320
|
-
STRING_PROPERTIES.each do |o|
|
321
|
-
str << stringify_string_property(o)
|
322
|
-
end
|
323
|
-
DATE_PROPERTIES.each do |o|
|
324
|
-
str << stringify_string_property(o)
|
325
|
-
end
|
326
|
-
ARRAY_PROPERTIES.each do |o|
|
327
|
-
str << stringify_array_property(o)
|
328
|
-
end
|
329
|
-
str
|
330
|
-
end
|
331
|
-
|
332
|
-
def stringify_string_property(prop)
|
333
|
-
if instance_variable_defined? "@#{prop}"
|
334
|
-
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
335
|
-
else
|
336
|
-
""
|
337
|
-
end
|
338
|
-
end
|
339
|
-
|
340
|
-
def stringify_date_property(prop)
|
341
|
-
if instance_variable_defined? "@#{prop}"
|
342
|
-
"#{prop}: #{instance_variable_get("@#{prop}")}\n"
|
343
|
-
else
|
344
|
-
""
|
345
|
-
end
|
346
|
-
end
|
347
|
-
|
348
|
-
def stringify_array_property(prop)
|
349
|
-
str = String.new
|
350
|
-
if instance_variable_defined? "@#{prop}"
|
351
|
-
str << "#{prop}:\n"
|
352
|
-
instance_variable_get("@#{prop}").each do |t|
|
353
|
-
str << "- #{t}\n"
|
354
|
-
end
|
355
|
-
end
|
356
|
-
str
|
357
|
-
end
|
358
|
-
|
359
224
|
def to_h
|
360
|
-
h = { "project" => @name }
|
225
|
+
h = { "project" => @name, "namespace" => @namespace }
|
361
226
|
ALL_PROPERTIES.each do |prop|
|
362
227
|
h[prop] = instance_variable_get("@#{prop}") if instance_variable_defined?("@#{prop}")
|
363
228
|
end
|
@@ -366,42 +231,4 @@ module Plansheet
|
|
366
231
|
h
|
367
232
|
end
|
368
233
|
end
|
369
|
-
|
370
|
-
class ProjectYAMLFile
|
371
|
-
attr_reader :projects
|
372
|
-
|
373
|
-
def initialize(path)
|
374
|
-
@path = path
|
375
|
-
# TODO: this won't GC, inline validation instead?
|
376
|
-
|
377
|
-
# Handle pre-Ruby 3.1 psych versions (this is brittle)
|
378
|
-
@raw = if Psych::VERSION.split(".")[0].to_i >= 4
|
379
|
-
YAML.load_file(path, permitted_classes: [Date])
|
380
|
-
else
|
381
|
-
YAML.load_file(path)
|
382
|
-
end
|
383
|
-
|
384
|
-
validate_schema
|
385
|
-
@projects = @raw.map { |proj| Project.new proj }
|
386
|
-
end
|
387
|
-
|
388
|
-
def validate_schema
|
389
|
-
validator = Kwalify::Validator.new(Plansheet::PROJECT_SCHEMA)
|
390
|
-
errors = validator.validate(@raw)
|
391
|
-
# Check YAML validity
|
392
|
-
return unless errors && !errors.empty?
|
393
|
-
|
394
|
-
$stderr.write "Schema errors in #{@path}:\n"
|
395
|
-
errors.each { |err| puts "- [#{err.path}] #{err.message}" }
|
396
|
-
abort
|
397
|
-
end
|
398
|
-
|
399
|
-
def sort!
|
400
|
-
@projects.sort!
|
401
|
-
end
|
402
|
-
|
403
|
-
def yaml_dump
|
404
|
-
YAML.dump(@projects.map(&:to_h))
|
405
|
-
end
|
406
|
-
end
|
407
234
|
end
|
data/lib/plansheet/version.rb
CHANGED
data/lib/plansheet.rb
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
require_relative "plansheet/version"
|
4
4
|
require_relative "plansheet/project"
|
5
|
+
require_relative "plansheet/pool"
|
5
6
|
require_relative "plansheet/sheet"
|
6
7
|
require "yaml"
|
7
8
|
require "kwalify"
|
@@ -9,28 +10,10 @@ require "kwalify"
|
|
9
10
|
module Plansheet
|
10
11
|
class Error < StandardError; end
|
11
12
|
|
13
|
+
# TODO: config schema validation
|
12
14
|
def self.load_config
|
13
15
|
YAML.load_file "#{Dir.home}/.plansheet.yml"
|
14
16
|
rescue StandardError
|
15
17
|
abort "unable to load plansheet config file"
|
16
18
|
end
|
17
|
-
|
18
|
-
def self.resort_projects_in_dir(dir)
|
19
|
-
project_files = Dir.glob("#{dir}/*yml")
|
20
|
-
project_files.each do |f|
|
21
|
-
pyf = ProjectYAMLFile.new(f)
|
22
|
-
pyf.sort!
|
23
|
-
File.write(f, pyf.yaml_dump)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def self.load_projects_dir(dir)
|
28
|
-
project_arr = []
|
29
|
-
projects = Dir.glob("*yml", base: dir)
|
30
|
-
projects.each do |l|
|
31
|
-
project_arr << ProjectYAMLFile.new(File.join(dir, l)).projects
|
32
|
-
end
|
33
|
-
|
34
|
-
project_arr.flatten!
|
35
|
-
end
|
36
19
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plansheet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.15.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- David Crosby
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-06-
|
11
|
+
date: 2022-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: dc-kwalify
|
@@ -33,7 +33,6 @@ extensions: []
|
|
33
33
|
extra_rdoc_files: []
|
34
34
|
files:
|
35
35
|
- ".rubocop.yml"
|
36
|
-
- ".rubocop_todo.yml"
|
37
36
|
- CODE_OF_CONDUCT.md
|
38
37
|
- Gemfile
|
39
38
|
- Gemfile.lock
|
@@ -44,7 +43,10 @@ files:
|
|
44
43
|
- examples/backpack.yml
|
45
44
|
- exe/plansheet
|
46
45
|
- lib/plansheet.rb
|
46
|
+
- lib/plansheet/pool.rb
|
47
47
|
- lib/plansheet/project.rb
|
48
|
+
- lib/plansheet/project/stringify.rb
|
49
|
+
- lib/plansheet/project/yaml.rb
|
48
50
|
- lib/plansheet/sheet.rb
|
49
51
|
- lib/plansheet/version.rb
|
50
52
|
homepage: https://dafyddcrosby.com
|
data/.rubocop_todo.yml
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config`
|
3
|
-
# on 2022-06-04 17:25:54 UTC using RuboCop version 1.29.1.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|
8
|
-
|
9
|
-
# Offense count: 1
|
10
|
-
# Configuration parameters: CountComments, CountAsOne.
|
11
|
-
Metrics/ClassLength:
|
12
|
-
Enabled: false
|
13
|
-
|
14
|
-
# Offense count: 1
|
15
|
-
# Configuration parameters: IgnoredMethods.
|
16
|
-
Metrics/CyclomaticComplexity:
|
17
|
-
Enabled: false
|
18
|
-
|
19
|
-
# Offense count: 2
|
20
|
-
# Configuration parameters: CountComments, CountAsOne, ExcludedMethods, IgnoredMethods.
|
21
|
-
Metrics/MethodLength:
|
22
|
-
Enabled: false
|
23
|
-
|
24
|
-
# Offense count: 1
|
25
|
-
# Configuration parameters: IgnoredMethods.
|
26
|
-
Metrics/PerceivedComplexity:
|
27
|
-
Enabled: false
|
28
|
-
|
29
|
-
# Offense count: 2
|
30
|
-
# Configuration parameters: AllowedConstants.
|
31
|
-
Style/Documentation:
|
32
|
-
Exclude:
|
33
|
-
- 'test/**/*'
|
34
|
-
- 'lib/plansheet.rb'
|
35
|
-
- 'lib/plansheet/project.rb'
|