working_class 0.1.1

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.
@@ -0,0 +1,195 @@
1
+ # WorkingClass
2
+
3
+ WorkingClass is an human readable syntax to write tasklists.
4
+ Besides being easy to read it's fully parseable, so you can work with the tasks
5
+ in Ruby.
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'working_class'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ ```sh
18
+ $ bundle
19
+ ```
20
+
21
+ Or install it yourself as:
22
+
23
+ ```sh
24
+ $ gem install working_class
25
+ ```
26
+
27
+ ## Usage
28
+
29
+ ### The Syntax
30
+
31
+ Keep in mind that all dates are formatted like this: `DD.MM.YYYY` or `D.M.YY`
32
+
33
+ ```
34
+ Tasklist Name
35
+ ---
36
+ [ ] My first task
37
+ [X] A finished task
38
+ [ ]{1.1.15} A task with a due date
39
+ [ ]{1.1.15}(31.1.15 12:00) A task with a date and a reminder
40
+ [ ]{1.1.15}(-1 12:00) A task with a date and a »relative« reminder
41
+ [ ]{1.1.15}(12:00) A task that will remind me at 12:00 1.1.15
42
+ ```
43
+
44
+ You see it's pretty easy to write tasks like this.
45
+
46
+ At the moment the order of date and reminder is mandatory.
47
+
48
+ So you **can't** write:
49
+
50
+ ```
51
+ My Tasklist
52
+ ---
53
+ [ ](REMINDER){DATE} My Task
54
+ ```
55
+
56
+
57
+ #### Tasklist Names
58
+
59
+ A tasklist name is written like this:
60
+
61
+ The `---` is important, don't forget it.
62
+
63
+ Every tasklist should have a name.
64
+
65
+ ```
66
+ Tasklist Name
67
+ ---
68
+
69
+ ```
70
+
71
+ #### Unfinished Tasks
72
+
73
+ Both tasks are equal, they are both not finished.
74
+
75
+ We recommend the `[ ]`, it looks much nicer.
76
+
77
+ ```
78
+ Shopping List
79
+ ---
80
+ [ ] Jeans
81
+ [] T-Shirts
82
+ ```
83
+
84
+ #### Finished Tasks
85
+
86
+ ```
87
+ Groceries List
88
+ ---
89
+ [X] Milk
90
+ [x] Bread
91
+ ```
92
+
93
+ To write a finished task you have to write a `[X]` or `[x]`. It's not important
94
+ whether you write a small x or a capital X, both characters are recognized as
95
+ a finished task.
96
+
97
+
98
+ #### Tasks with a Date
99
+
100
+ It doesn't matter if you write your dates DD.MM.YY or D.M.YY or DD.MM.YYYY
101
+ WorkingClass accepts all of those formats, as long as it is a valid date.
102
+
103
+
104
+ ```
105
+ The Party List
106
+ ---
107
+ [X]{6.2.2015} Birthday Party
108
+ [X]{13.2.15} Another Birthday Party
109
+ ```
110
+
111
+ #### Tasks with a Reminder
112
+
113
+ Every task can have a reminder.
114
+ You have several options when adding a reminder.
115
+
116
+ All times are 24h.
117
+
118
+ You can write a full date without a time and the parser will add the default
119
+ time (9:00) automatically.
120
+
121
+ ```
122
+ An even more awesome Party List
123
+ ---
124
+ [ ](31.1.15) This time of year
125
+ ```
126
+
127
+ If you already specified a date for the task you can use a relative reminder
128
+ by writing `-2`, this specifies that you want to be reminded 2 days earlier.
129
+ The parser will add the default time, if you didn't add one.
130
+
131
+ ```
132
+ The after party
133
+ ---
134
+ [ ]{2.1.15}(-2) You will have to clean up everthing.
135
+ ```
136
+
137
+ **Important:** This only works if your task has already a date.
138
+
139
+ This will not work:
140
+
141
+ ```
142
+ The after party
143
+ ---
144
+ [ ](-2) You will have to clean up everthing.
145
+ ```
146
+
147
+ So enough of that, what about the times. You can easily add a time to your
148
+ reminder
149
+
150
+ ```
151
+ My Finals
152
+ ---
153
+ [ ]{26.1.15}(15:00) English
154
+ ```
155
+
156
+ If you don't specify a relative or absolute date you will be reminded at 15:00
157
+ on the same day.
158
+
159
+
160
+ You can also combine absolute or relative dates with a time
161
+
162
+ ```
163
+ My Finals
164
+ ---
165
+ [ ]{26.1.15}(24.1.15 9:00) Don't panic.
166
+ [ ]{26.1.15}(-1 15:00) English
167
+ ```
168
+
169
+ ### The Parser
170
+
171
+ Check out the [full documentation](http://www.rubydoc.info/github/TimKaechele/WorkingClass/master)
172
+
173
+ ```ruby
174
+ require 'working_class'
175
+
176
+ string = """
177
+ My Finals
178
+ ---
179
+ [ ]{26.1.15}(15:00) English
180
+ """
181
+
182
+ WorkingClass.load(string) # => WorkingClass::Tasklist
183
+
184
+ # or if you have a file
185
+ WorkingClass.load_file('./examples/example_1.txt') # => WorkingClass::Tasklist
186
+
187
+ ```
188
+
189
+ ## Contributing
190
+
191
+ 1. Fork it ( https://github.com/TimKaechele/workingclass/fork )
192
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
193
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
194
+ 4. Push to the branch (`git push origin my-new-feature`)
195
+ 5. Create a new Pull Request
@@ -0,0 +1,11 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require "rake/testtask"
4
+
5
+ Rake::TestTask.new do |task|
6
+ task.libs << %w(test lib)
7
+ task.pattern = "test/*_test.rb"
8
+ end
9
+
10
+ task :default => :test
11
+
@@ -0,0 +1,29 @@
1
+ require 'working_class/version'
2
+ require 'working_class/parser'
3
+ require 'working_class/task'
4
+ require 'working_class/tasklist'
5
+
6
+ # WorkingClass Module
7
+ #
8
+ module WorkingClass
9
+
10
+ # Loads the file from the path and returns a Tasklist
11
+ #
12
+ # @param path [String] the filepath
13
+ # @return [WorkingClass::Tasklist] the parsed Tasklist
14
+ #
15
+ def self.load_file(path)
16
+ string = File.open(path, 'r').read()
17
+ self.load(string)
18
+ end
19
+
20
+ # Parses the given string and returns a Tasklist
21
+ #
22
+ # @param string [String] the WorkingClass tasklist syntax string
23
+ # @return [WorkingClass::Tasklist] the parsed Tasklist
24
+ #
25
+ def self.load(string)
26
+ Parser.new(string).to_tasklist
27
+ end
28
+
29
+ end
@@ -0,0 +1,165 @@
1
+ require 'date'
2
+
3
+ module WorkingClass
4
+ # The actual syntax parser
5
+ #
6
+ class Parser
7
+
8
+ # Initializes a new Parser object with a given WorkingClass syntax string
9
+ #
10
+ # @param string [String] the raw string with the WorkingClass syntax
11
+ # @return [WorkingClass::Parser] a parser instance
12
+ #
13
+ def initialize(string)
14
+ @raw_string = string
15
+ @date_regex = /(?:\d{1,2}\.){2}\d{2,4}/
16
+ @time_regex = /\d{1,2}:\d{1,2}/
17
+ end
18
+
19
+ # Parses the syntax and returns an Hash with the result
20
+ #
21
+ # @return [Hash] a Hash representation of the parsed tasklist
22
+ #
23
+ def to_h
24
+ {
25
+ name: tasklist_name,
26
+ tasks_count: tasks.length,
27
+ tasks: tasks
28
+ }
29
+ end
30
+
31
+ # Parses the syntax and returns a WorkingClass::Tasklist instance
32
+ #
33
+ # @return [WorkingClass::Tasklist] a Tasklist instance of the parsed tasklist
34
+ #
35
+ def to_tasklist
36
+ tasks = Array.new
37
+ raw = self.to_h
38
+ raw[:tasks].each do |t|
39
+ task = Task.new(t[:name], t)
40
+ tasks << task
41
+ end
42
+ Tasklist.new raw[:name], tasks
43
+ end
44
+
45
+ private
46
+ def tasklist_name_regex
47
+ /(.*)\n---/
48
+ end
49
+
50
+ def task_regex
51
+ /\[(.*)\] *(?:\{(.*)\})? *(?:\((.*)\)){0,1} *(.*)/
52
+ end
53
+
54
+ def tasklist_name
55
+ @raw_string.match(tasklist_name_regex).captures.first
56
+ end
57
+
58
+ def tasks
59
+ tasks = []
60
+ @raw_string.scan(task_regex).each do |match|
61
+ task = Hash.new
62
+ task[:is_finished] = extract_is_finished(match[0])
63
+ task[:date] = extract_date(match[1])
64
+ task[:reminder] = extract_reminder(task[:date], match[2])
65
+ task[:name] = extract_name(match[3])
66
+ tasks << task
67
+ end
68
+ tasks
69
+ end
70
+
71
+ def extract_is_finished(match)
72
+ match = match.strip
73
+ if match == "x" or match == "X"
74
+ true
75
+ else
76
+ false
77
+ end
78
+ end
79
+
80
+ def extract_date(match)
81
+ if match.nil?
82
+ return nil
83
+ end
84
+
85
+ if match.scan(@date_regex)
86
+ # @todo rescue the right exception
87
+ begin
88
+ normalize_date(match)
89
+ rescue
90
+ nil
91
+ end
92
+ else
93
+ nil
94
+ end
95
+ end
96
+
97
+
98
+ def extract_name(match)
99
+ if !match.nil?
100
+ match.strip
101
+ else
102
+ ""
103
+ end
104
+ end
105
+
106
+ def normalize_date(date_string)
107
+ parts = date_string.split(".")
108
+ if parts[2].length == 2
109
+ parts[2] = "20" + parts[2]
110
+ end
111
+ parts = parts.map { |p| p.to_i }
112
+ Date.new(parts[2], parts[1], parts[0])
113
+ end
114
+
115
+ # Shame Shame
116
+ # @todo REFACTOR!!!!!1eleven!1
117
+ def extract_reminder(task_date, match)
118
+ if match.nil?
119
+ return nil
120
+ end
121
+
122
+ if match.empty? and !task_date.nil?
123
+ return DateTime.parse(task_date.to_s + " 9:00")
124
+ elsif match.empty? and task_date.nil?
125
+ return nil
126
+ end
127
+
128
+ date = nil
129
+ time = nil
130
+
131
+ absolute_date = match.scan(/(#{@date_regex})/)
132
+ relative_date = match.scan(/(-\d{1,2})/)
133
+ match_time = match.scan(/(#{@time_regex})/)
134
+
135
+ if absolute_date.empty? and (!relative_date.empty? and !task_date.nil?)
136
+ date = task_date + relative_date.flatten.first.to_i
137
+ elsif !absolute_date.empty?
138
+ # @todo rescue the right exception
139
+ begin
140
+ date = normalize_date(absolute_date.flatten.first)
141
+ rescue
142
+ date = nil
143
+ end
144
+ end
145
+
146
+ if !match_time.empty?
147
+ time = match_time
148
+ end
149
+
150
+ if !date.nil? and !time.nil?
151
+ DateTime.parse(date.to_s + " #{time}")
152
+ elsif (date.nil? and !task_date.nil?) and !time.nil?
153
+ DateTime.parse(task_date.to_s + " #{time}")
154
+ elsif !date.nil? and time.nil?
155
+ DateTime.parse(date.to_s + " 9:00")
156
+ else
157
+ return nil
158
+ end
159
+
160
+ end
161
+
162
+
163
+ end
164
+ end
165
+
@@ -0,0 +1,82 @@
1
+ module WorkingClass
2
+
3
+ # A basic represantation of a Task
4
+ #
5
+ # @attr_reader name [String] the task name
6
+ # @attr_reader is_finished [Boolean] true if the task is finished
7
+ # @attr_reader date [Date] the day the task is due
8
+ # @attr_reader reminder [DateTime] a DateTime with a reminder
9
+ class Task
10
+
11
+ attr_reader :name
12
+ attr_reader :date
13
+ attr_reader :reminder
14
+ attr_reader :is_finished
15
+
16
+ alias :is_finished? :is_finished
17
+ alias :finished? :is_finished
18
+
19
+ # Initializes a new task object with a name, and options
20
+ #
21
+ # @param name [String] the task name
22
+ # @param options [Hash] an options hash
23
+ #
24
+ # @option options [Boolean] :is_finished (false) true if the task is finished
25
+ # @option options [Date] :date (nil) the date when the task is due
26
+ # @option options [DateTime] :reminder (nil )a DateTime with a reminder
27
+ #
28
+ def initialize name, options = {}
29
+ options = {is_finished: false, date: nil, reminder: nil}.merge(options)
30
+ @name = name
31
+ @is_finished = options[:is_finished]
32
+ @date = options[:date]
33
+ @reminder = options[:reminder]
34
+ end
35
+
36
+ # Returns true if the task is upcoming
37
+ #
38
+ # A Task without a date is always upcoming.
39
+ # @todo add example
40
+ #
41
+ # A finished task is never upcoming.
42
+ # @todo add example
43
+ #
44
+ # @return [Boolean] true if the task is upcoming
45
+ def is_upcoming
46
+ if @is_finished
47
+ false
48
+ elsif !@date.nil?
49
+ Date.today <= @date
50
+ else
51
+ true
52
+ end
53
+ end
54
+
55
+ alias :is_upcoming? :is_upcoming
56
+ alias :upcoming? :is_upcoming
57
+
58
+ # Returns true if the task is due tomorrow
59
+ #
60
+ # A Task without a date is always due tomorrow.
61
+ # @todo add example
62
+ #
63
+ # A finished task is never due tomorrow.
64
+ # @todo add example
65
+ #
66
+ # @return [Boolean] true if the task is due tomorrow
67
+ def is_tomorrow
68
+ if @is_finished
69
+ false
70
+ elsif date.nil?
71
+ true
72
+ else
73
+ Date.today + 1 == @date
74
+ end
75
+ end
76
+
77
+ alias :is_tomorrow? :is_tomorrow
78
+ alias :tomorrow? :is_tomorrow
79
+
80
+
81
+ end
82
+ end