sifttter-redux 0.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.
@@ -0,0 +1,135 @@
1
+ #| ======================================================
2
+ #| CliManager Module
3
+ #| Singleton to manage common CLI interfacing
4
+ #| ======================================================
5
+ module CliMessage
6
+
7
+ ERROR = 1
8
+ INFO = 2
9
+ SECTION = 3
10
+ WARNING = 4
11
+
12
+ #| ------------------------------------------------------
13
+ #| error method
14
+ #|
15
+ #| Outputs a formatted-red error message.
16
+ #| @param message The message to output
17
+ #| @return Void
18
+ #| ------------------------------------------------------
19
+ def self.error(message, addNewline = true)
20
+ if addNewline
21
+ puts "---> ERROR: #{message}".red
22
+ else
23
+ print "---> ERROR: #{message}".red
24
+ end
25
+
26
+ @@last_message_type = ERROR
27
+ end
28
+
29
+ #| ------------------------------------------------------
30
+ #| finish_message method
31
+ #|
32
+ #| Finishes a previous message by appending "DONE" in the
33
+ #| correct color.
34
+ #| @return Void
35
+ #| ------------------------------------------------------
36
+ def self.finish_message(message)
37
+ case @@last_message_type
38
+ when ERROR
39
+ puts message.red
40
+ when INFO
41
+ puts message.blue
42
+ when SECTION
43
+ puts message.green
44
+ when WARNING
45
+ puts message.yellow
46
+ end
47
+ end
48
+
49
+ #| ------------------------------------------------------
50
+ #| info method
51
+ #|
52
+ #| Outputs a formatted-blue informational message.
53
+ #| @param message The message to output
54
+ #| @return Void
55
+ #| ------------------------------------------------------
56
+ def self.info(message, addNewline = true)
57
+ if addNewline
58
+ puts "---> INFO: #{message}".blue
59
+ else
60
+ print "---> INFO: #{message}".blue
61
+ end
62
+
63
+ @@last_message_type = INFO
64
+ end
65
+
66
+ #| ------------------------------------------------------
67
+ #| prompt method
68
+ #|
69
+ #| Outputs a prompt, collects the user's response, and
70
+ #| returns it.
71
+ #| @param prompt The prompt to output
72
+ #| @param default The default option
73
+ #| @return String
74
+ #| ------------------------------------------------------
75
+ def self.prompt(prompt, default)
76
+ print "#{prompt} [default: #{default}]: "
77
+ choice = $stdin.gets.chomp
78
+ if choice.empty?
79
+ return default
80
+ else
81
+ return choice
82
+ end
83
+ end
84
+
85
+ #| ------------------------------------------------------
86
+ #| section method
87
+ #|
88
+ #| Outputs a formatted-orange section message.
89
+ #| @param message The message to output
90
+ #| @return Void
91
+ #| ------------------------------------------------------
92
+ def self.section(message, addNewline = true)
93
+ if addNewline
94
+ puts "#### #{message}".green
95
+ else
96
+ print "#### #{message}".green
97
+ end
98
+
99
+ @@last_message_type = SECTION
100
+ end
101
+
102
+ #| ------------------------------------------------------
103
+ #| success method
104
+ #|
105
+ #| Outputs a formatted-green success message.
106
+ #| @param message The message to output
107
+ #| @return Void
108
+ #| ------------------------------------------------------
109
+ def self.success(message, addNewline = true)
110
+ if addNewline
111
+ puts "---> SUCCESS: #{message}".green
112
+ else
113
+ print "---> SUCCESS: #{message}".green
114
+ end
115
+
116
+ @@last_message_type = WARNING
117
+ end
118
+
119
+ #| ------------------------------------------------------
120
+ #| warning method
121
+ #|
122
+ #| Outputs a formatted-yellow warning message.
123
+ #| @param message The message to output
124
+ #| @return Void
125
+ #| ------------------------------------------------------
126
+ def self.warning(message, addNewline = true)
127
+ if addNewline
128
+ puts "---> WARNING: #{message}".yellow
129
+ else
130
+ print "---> WARNING: #{message}".yellow
131
+ end
132
+
133
+ @@last_message_type = WARNING
134
+ end
135
+ end
@@ -0,0 +1,136 @@
1
+ require 'singleton'
2
+
3
+ #| ======================================================
4
+ #| ConfigManager Class
5
+ #|
6
+ #| Singleton to manage the YAML config file
7
+ #| for this script
8
+ #| ======================================================
9
+
10
+ class ConfigManager
11
+ include Singleton
12
+
13
+ attr_accessor :configFile
14
+
15
+ #| ------------------------------------------------------
16
+ #| initialize method
17
+ #|
18
+ #| Initializes this singleton with data from the config
19
+ #| file. If the file doesn't exist, an empty hash is
20
+ #| created in anticipation of future config saves.
21
+ #| @return Void
22
+ #| ------------------------------------------------------
23
+ def initialize
24
+ @configFile = SifttterRedux::SRD_CONFIG_FILEPATH
25
+
26
+ if File.exists?(SifttterRedux::SRD_CONFIG_FILEPATH)
27
+ @data = YAML.load_file(SifttterRedux::SRD_CONFIG_FILEPATH)
28
+ @data.each do |k, v|
29
+ define_singleton_method(k) { return v }
30
+ end
31
+ else
32
+ @data = {}
33
+ end
34
+ end
35
+
36
+ #| ------------------------------------------------------
37
+ #| _dump method
38
+ #|
39
+ #| Convenience method that dumps the configuration hash's
40
+ #| data.
41
+ #| @return Void
42
+ #| ------------------------------------------------------
43
+ def _dump
44
+ puts @data
45
+ end
46
+
47
+ #| ------------------------------------------------------
48
+ #| add_to_section method
49
+ #|
50
+ #| Adds a hash to the configuration data.
51
+ #| @param hash The hash to add to configuration
52
+ #| @param section The section into which the hash goes
53
+ #| @return Void
54
+ #| ------------------------------------------------------
55
+ def add_to_section(hash, section)
56
+ unless @data.has_key?(section)
57
+ CliMessage.warning("Attempting to insert into a non-existing section: #{section}; skipping...")
58
+ return
59
+ end
60
+
61
+ @data[section].merge!(hash)
62
+ end
63
+
64
+ #| ------------------------------------------------------
65
+ #| create_section method
66
+ #|
67
+ #| Creates a new section in the configuration hash.
68
+ #| @param section The section to create
69
+ #| @return Void
70
+ #| ------------------------------------------------------
71
+ def create_section(section)
72
+ if @data.has_key?(section)
73
+ CliMessage.warning("Attempting to create existing section (#{section}); skipping...")
74
+ return
75
+ end
76
+
77
+ define_singleton_method(section) { return @data[section] }
78
+ @data.merge!(section => {})
79
+ end
80
+
81
+ #| ------------------------------------------------------
82
+ #| delete_section method
83
+ #|
84
+ #| Deletes a section in the configuration hash.
85
+ #| @param section The section to delete
86
+ #| @return Void
87
+ #| ------------------------------------------------------
88
+ def delete_section(section)
89
+ unless @data.has_key?(section)
90
+ CliMessage.warning("Attempting to delete non-existing section (#{section}); skipping...")
91
+ return
92
+ end
93
+
94
+ remove_singleton_method(section)
95
+ @data.delete(section)
96
+ end
97
+
98
+ #| ------------------------------------------------------
99
+ #| remove_singleton_method method
100
+ #|
101
+ #| Removes a hash from the configuration data based on
102
+ #| its key.
103
+ #| @param hash The hash key remove
104
+ #| @param section The section from which the key comes
105
+ #| @return Void
106
+ #| ------------------------------------------------------
107
+ def remove_from_section(key, section)
108
+ unless @data.has_key?(section) && @data[section].has_key?(key)
109
+ CliMessage.warning("Attempting to remove a non-existing key: #{section}.#{key}; skipping...")
110
+ return
111
+ end
112
+
113
+ @data[section].delete(key)
114
+ end
115
+
116
+ #| ------------------------------------------------------
117
+ #| reset method
118
+ #|
119
+ #| Clears out the configuration data by resetting the hash.
120
+ #| @return Void
121
+ #| ------------------------------------------------------
122
+ def reset
123
+ @data = {}
124
+ end
125
+
126
+ #| ------------------------------------------------------
127
+ #| save_configuration method
128
+ #|
129
+ #| Saves the configuration data to the filesystem.
130
+ #| @return File
131
+ #| ------------------------------------------------------
132
+ def save_configuration
133
+ return File.open(@configFile, 'w') { |f| f.write(@data.to_yaml) }
134
+ end
135
+
136
+ end
@@ -0,0 +1,15 @@
1
+ module SifttterRedux
2
+ VERSION = "0.2.0"
3
+
4
+ # Sifttter and Sifttter Redux Constants
5
+ SRD_CONFIG_FILEPATH = File.join(ENV['HOME'], '.sifttter_redux')
6
+ SFT_LOCAL_FILEPATH = "/tmp/sifttter"
7
+ SFT_REMOTE_FILEPATH = "/Apps/ifttt/sifttter"
8
+
9
+ # Dropbox Upload Constants
10
+ DBU_LOCAL_FILEPATH = "/usr/local/opt"
11
+
12
+ # Day One Constants
13
+ DO_REMOTE_FILEPATH = "/Apps/Day\\ One/Journal.dayone/entries"
14
+ DO_LOCAL_FILEPATH = "/tmp/dayone"
15
+ end
@@ -0,0 +1,102 @@
1
+ require 'chronic'
2
+
3
+ class DateRangeMakerError < StandardError
4
+ def initialize(msg = "You've triggered a DateRangeMakerError")
5
+ super
6
+ end
7
+ end
8
+
9
+ class BadChronicDateError < DateRangeMakerError
10
+ def initialize(msg = "Invalid date provided to Chronic...")
11
+ super
12
+ end
13
+ end
14
+
15
+ class BadDateOrderError < DateRangeMakerError
16
+ def initialize(msg = "The start date must be before or equal to the end date...")
17
+ super
18
+ end
19
+ end
20
+
21
+ class InvalidFlagsError < DateRangeMakerError
22
+ def initialize(msg = "You can't specify -t without specifying -f...")
23
+ super
24
+ end
25
+ end
26
+
27
+ #| ======================================================
28
+ #| DateRangeMaker Class
29
+ #|
30
+ #| Singleton to manage the YAML config file
31
+ #| for this script
32
+ #| ======================================================
33
+ class DateRangeMaker
34
+
35
+ def initialize
36
+
37
+ end
38
+
39
+ def last_seven_days(include_today = false)
40
+ if (include_today)
41
+ _r = (Date.today - 7..Date.today)
42
+ else
43
+ _r = (Date.today - 7...Date.today)
44
+ end
45
+
46
+ return _r
47
+ end
48
+
49
+ def range(options = {})
50
+
51
+ opts = {
52
+ :start_date => Date.today,
53
+ :end_date => nil,
54
+ :include_today => false
55
+ }
56
+
57
+ options.each do |k, v|
58
+ k = k.to_sym
59
+ raise ArgumentError, "Unknown property: #{k}" unless opts.key?(k)
60
+ opts[k] = v
61
+ end
62
+
63
+ begin
64
+ chronic_start_date = Chronic.parse(opts[:start_date]).to_date
65
+ rescue
66
+ raise BadChronicDateError.new("Invalid date provided to Chronic: #{opts[:start_date]}") if !opts[:start_date].nil?
67
+ nil
68
+ end
69
+
70
+ begin
71
+ chronic_end_date = Chronic.parse(opts[:end_date]).to_date
72
+ rescue
73
+ raise BadChronicDateError.new("Invalid date provided to Chronic: #{opts[:end_date]}") if !opts[:end_date].nil?
74
+ nil
75
+ end
76
+
77
+ raise InvalidFlagsError.new if (opts[:start_date].nil? && !opts[:end_date].nil?)
78
+ raise BadDateOrderError.new if (chronic_end_date && chronic_start_date > chronic_end_date)
79
+
80
+ if (!chronic_start_date.nil?)
81
+ if (chronic_end_date.nil?)
82
+ if (opts[:include_today])
83
+ dates = (chronic_start_date..Date.today)
84
+ else
85
+ dates = (chronic_start_date...Date.today)
86
+ end
87
+ else
88
+ dates = (chronic_start_date..chronic_end_date)
89
+ end
90
+ end
91
+
92
+ return dates
93
+ end
94
+
95
+ def today
96
+ return (Date.today..Date.today)
97
+ end
98
+
99
+ def yesterday
100
+ return (Date.today - 1..Date.today - 1)
101
+ end
102
+ end
@@ -0,0 +1,190 @@
1
+ #| ======================================================
2
+ #| METHODS
3
+ #| ======================================================
4
+
5
+ #| ------------------------------------------------------
6
+ #| collect_preferences method
7
+ #|
8
+ #| Collects preferences from the user and stores the
9
+ #| entered values into a configuration file.
10
+ #| @return Void
11
+ #| ------------------------------------------------------
12
+ def collect_preferences
13
+ CliMessage.section('COLLECTING PREFERENCES...')
14
+
15
+ pref = CliMessage.prompt("Location for downloaded Sifttter files from Dropbox", SifttterRedux::SFT_LOCAL_FILEPATH)
16
+ $config.add_to_section({"sifttter_local_filepath" => pref}, "sifttter_redux")
17
+
18
+ pref = CliMessage.prompt("Location of Sifttter files in Dropbox", SifttterRedux::SFT_REMOTE_FILEPATH)
19
+ $config.add_to_section({"sifttter_remote_filepath" => pref}, "sifttter_redux")
20
+
21
+ pref = CliMessage.prompt("Location for downloaded Day One files from Dropbox", SifttterRedux::DO_LOCAL_FILEPATH)
22
+ $config.add_to_section({"dayone_local_filepath" => pref}, "sifttter_redux")
23
+
24
+ pref = CliMessage.prompt("Location of Day One files in Dropbox", SifttterRedux::DO_REMOTE_FILEPATH)
25
+ $config.add_to_section({"dayone_remote_filepath" => pref}, "sifttter_redux")
26
+ end
27
+
28
+ #| ------------------------------------------------------
29
+ #| download_sifttter_files method
30
+ #|
31
+ #| Downloads Sifttter files from Dropbox
32
+ #| @return Void
33
+ #| ------------------------------------------------------
34
+ def download_sifttter_files
35
+ # Download all Sifttter files from Dropbox.
36
+ CliMessage.info('Downloading Sifttter files...', false)
37
+ `#{$db_uploader} download #{$config.sifttter_redux["sifttter_remote_filepath"]} #{$config.sifttter_redux["sifttter_local_filepath"]}`
38
+ CliMessage.finish_message('DONE.')
39
+ end
40
+
41
+ #| ------------------------------------------------------
42
+ #| initialize_procedures method
43
+ #|
44
+ #| Initializes Sifttter Redux by downloading and collecting
45
+ #| all necessary items and info.
46
+ #| @return Void
47
+ #| ------------------------------------------------------
48
+ def initialize_procedures
49
+ $config.reset
50
+ $config.create_section("sifttter_redux")
51
+ $config.add_to_section({"config_location" => $config.configFile}, "sifttter_redux")
52
+
53
+ install_db_uploader
54
+ collect_preferences
55
+
56
+ CliMessage.section("INITIALIZATION COMPLETE!")
57
+
58
+ $config.save_configuration
59
+ end
60
+
61
+ #| ------------------------------------------------------
62
+ #| install_db_uploader method
63
+ #|
64
+ #| Installs Dropbox Uploader to a user-specified location
65
+ #| by cloning the git repository.
66
+ #| @return Void
67
+ #| ------------------------------------------------------
68
+ def install_db_uploader
69
+ valid_directory_chosen = false
70
+
71
+ CliMessage.section('DOWNLOADING DROPBOX UPLOADER...')
72
+
73
+ # Create a new configuration section for Dropbox-Uploader
74
+ $config.create_section("db_uploader")
75
+
76
+ until valid_directory_chosen
77
+ # Prompt the user for a location to save Dropbox Uploader. "
78
+ db_uploader_location = CliMessage.prompt("Location for Dropbox-Uploader", SifttterRedux::DBU_LOCAL_FILEPATH)
79
+ db_uploader_location.chop! if db_uploader_location.end_with?('/')
80
+ db_uploader_location = "/usr/local/opt" if db_uploader_location.empty?
81
+
82
+ # If the entered directory exists, clone the repository.
83
+ if File.directory?(db_uploader_location)
84
+ valid_directory_chosen = true
85
+ db_uploader_location << "/Dropbox-Uploader"
86
+
87
+ # If, for some reason, Dropbox Uploader alread exists at this location,
88
+ # skip the clone.
89
+ if File.directory?(db_uploader_location)
90
+ CliMessage.info("You seem to already have Dropbox Uploader at this location; skipping...")
91
+ else
92
+ %x{git clone https://github.com/andreafabrizi/Dropbox-Uploader.git #{db_uploader_location}}
93
+ end
94
+
95
+ # Save config data to YAML.
96
+ $config.add_to_section({"local_filepath" => db_uploader_location}, "db_uploader")
97
+ else
98
+ puts "Sorry, but #{db_uploader_location} isn't a valid directory."
99
+ end
100
+ end
101
+ end
102
+
103
+ #| ------------------------------------------------------
104
+ #| run_sifttter method
105
+ #|
106
+ #| Modified form of Sifttter
107
+ #|
108
+ #| Sifttter: An IFTTT-to-Day One Logger by Craig Eley 2014
109
+ #| Based on tp-dailylog.rb by Brett Terpstra 2012
110
+ #| @param date The date to use when scanning Sifttter files
111
+ #| @return Void
112
+ #| ------------------------------------------------------
113
+ def run_sifttter(date)
114
+ uuid_command = "uuidgen" if OS.mac?
115
+ uuid_command = "uuid" if OS.linux?
116
+ uuid = %x{#{uuid_command}}.gsub(/-/,'').strip
117
+
118
+ date_for_title = date.strftime('%B %d, %Y')
119
+ datestamp = date.to_time.utc.iso8601
120
+ starred = false
121
+
122
+ template = ERB.new <<-XMLTEMPLATE
123
+ <?xml version="1.0" encoding="UTF-8"?>
124
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
125
+ <plist version="1.0">
126
+ <dict>
127
+ <key>Creation Date</key>
128
+ <date><%= datestamp %></date>
129
+ <key>Entry Text</key>
130
+ <string><%= entrytext %></string>
131
+ <key>Starred</key>
132
+ <<%= starred %>/>
133
+ <key>Tags</key>
134
+ <array>
135
+ <string>daily logs</string>
136
+ </array>
137
+ <key>UUID</key>
138
+ <string><%= uuid %></string>
139
+ </dict>
140
+ </plist>
141
+ XMLTEMPLATE
142
+
143
+ date_regex = "#{date.strftime('%B')} 0?#{date.strftime('%-d')}, #{date.strftime('%Y')}"
144
+ time_regex = "\d{1,2}:\d{1,2}\s?[AaPpMm]{2}"
145
+
146
+ files = %x{find #{$config.sifttter_redux["sifttter_local_filepath"]} -type f -name '*.txt' | grep -v -i daily | sort}
147
+
148
+ projects = []
149
+ files.split("\n").each do |file|
150
+ if File.exists?(file.strip)
151
+ f = File.open(file.strip, encoding: 'UTF-8')
152
+ lines = f.read
153
+ f.close
154
+ project = "### " + File.basename(file).gsub(/^.*?\/([^\/]+)$/,"\\1") + "\n"
155
+
156
+ found_completed = false
157
+ lines.each_line do |line|
158
+ if line =~ /&/
159
+ line.gsub!(/[&]/, 'and')
160
+ end
161
+ if line =~ /#{date_regex}/
162
+ found_completed = true
163
+ project += line.gsub(/@done/,'').gsub(/#{date_regex}\s(-|at)\s/, '').gsub(/#{time_regex}\s-\s/, '').strip + "\n"
164
+ end
165
+ end
166
+ end
167
+ if found_completed
168
+ projects.push(project)
169
+ end
170
+ end
171
+
172
+ if projects.length <=0
173
+ CliMessage.error("No entries found...")
174
+ exit!
175
+ end
176
+
177
+ if projects.length > 0
178
+ entrytext = "# Things done on #{date_for_title}\n\n"
179
+ projects.each do |project|
180
+ entrytext += project.gsub(/.txt/, ' ') + "\n\n"
181
+ end
182
+
183
+ Dir.mkdir($config.sifttter_redux["dayone_local_filepath"]) if !Dir.exists?($config.sifttter_redux["dayone_local_filepath"])
184
+
185
+ fh = File.new(File.expand_path($config.sifttter_redux["dayone_local_filepath"] + "/" + uuid + ".doentry"), 'w+')
186
+ fh.puts template.result(binding)
187
+ fh.close
188
+ CliMessage.success("Entry logged for #{date_for_title}...")
189
+ end
190
+ end
@@ -0,0 +1,47 @@
1
+ #| ======================================================
2
+ #| OS Module
3
+ #|
4
+ #| Module to easily find the running operating system
5
+ #| ======================================================
6
+ module OS
7
+
8
+ #| ------------------------------------------------------
9
+ #| linux? method
10
+ #|
11
+ #| Returns true if the host OS is Linux (false otherwise).
12
+ #| @return Bool
13
+ #| ------------------------------------------------------
14
+ def OS.linux?
15
+ OS.unix? and not OS.mac?
16
+ end
17
+
18
+ #| ------------------------------------------------------
19
+ #| mac? method
20
+ #|
21
+ #| Returns true if the host OS is OS X (false otherwise).
22
+ #| @return Bool
23
+ #| ------------------------------------------------------
24
+ def OS.mac?
25
+ (/darwin/ =~ RUBY_PLATFORM) != nil
26
+ end
27
+
28
+ #| ------------------------------------------------------
29
+ #| unix? method
30
+ #|
31
+ #| Returns true if the host OS is Unix (false otherwise).
32
+ #| @return Bool
33
+ #| ------------------------------------------------------
34
+ def OS.unix?
35
+ !OS.windows?
36
+ end
37
+
38
+ #| ------------------------------------------------------
39
+ #| windows? method
40
+ #|
41
+ #| Returns true if the host OS is Windows (false otherwise).
42
+ #| @return Bool
43
+ #| ------------------------------------------------------
44
+ def OS.windows?
45
+ (/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
46
+ end
47
+ end
@@ -0,0 +1,6 @@
1
+ require 'Sifttter-Redux/cli-message.rb'
2
+ require 'Sifttter-Redux/config-manager.rb'
3
+ require 'Sifttter-Redux/constants.rb'
4
+ require 'Sifttter-Redux/date-range-maker.rb'
5
+ require 'Sifttter-Redux/methods.rb'
6
+ require 'Sifttter-Redux/os.rb'
@@ -0,0 +1,56 @@
1
+ require 'date'
2
+ require 'test_helper'
3
+ require File.join(File.dirname(__FILE__), '..', 'lib/Sifttter-Redux/date-range-maker.rb')
4
+
5
+ class DefaultTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ $drm = DateRangeMaker.new
9
+ end
10
+
11
+ def test_today
12
+ assert_equal($drm.today, (Date.today..Date.today))
13
+ end
14
+
15
+ def test_yesterday
16
+ assert_equal($drm.yesterday, (Date.today - 1..Date.today - 1))
17
+ end
18
+
19
+ def test_last_7_days
20
+ assert_equal($drm.last_seven_days, (Date.today - 7...Date.today))
21
+ end
22
+
23
+ def test_last_7_days_include_today
24
+ assert_equal($drm.last_seven_days(true), (Date.today - 7..Date.today))
25
+ end
26
+
27
+ def test_range_only_start_date
28
+ assert_equal($drm.range({:start_date => "2014-02-01"}), (Date.parse("2014-02-01")...Date.today))
29
+ end
30
+
31
+ def test_range_only_start_date_include_today
32
+ assert_equal($drm.range({:start_date => "2014-02-01", :include_today => true}), (Date.parse("2014-02-01")..Date.today))
33
+ end
34
+
35
+ def test_range_start_date_and_end_date
36
+ assert_equal($drm.range({:start_date => "2014-02-01", :end_date => "2014-02-05"}), (Date.parse("2014-02-01")..Date.parse("2014-02-05")))
37
+ end
38
+
39
+ def test_range_bad_dates
40
+ assert_raise BadChronicDateError do
41
+ $drm.range({:start_date => "Bad Start Date", :end_date => "Bad End Date"})
42
+ end
43
+ end
44
+
45
+ def test_range_end_date_with_no_start_date
46
+ assert_raise InvalidFlagsError do
47
+ $drm.range({:start_date => nil, :end_date => Date.today})
48
+ end
49
+ end
50
+
51
+ def test_range_end_date_before_start_date
52
+ assert_raise BadDateOrderError do
53
+ $drm.range({:start_date => Date.today, :end_date => Date.today - 1})
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,9 @@
1
+ require 'test/unit'
2
+
3
+ # Add test libraries you want to use here, e.g. mocha
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ # Add global extensions to the test case class here
8
+
9
+ end