timet 1.5.4 → 1.5.6
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 -3
- data/CHANGELOG.md +57 -1
- data/lib/timet/application.rb +39 -11
- data/lib/timet/database.rb +11 -11
- data/lib/timet/validation_edit_helper.rb +33 -23
- data/lib/timet/version.rb +2 -2
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d5bbd4564baa142a2bf20160bdd2499f8df24ce2357eb03b8f1d05a26341fac5
|
4
|
+
data.tar.gz: 2fe4c7eb71ab3ff20dc4691caf0bbfad9441e7cf3b1414caaf89ac7571ed4592
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c7d324fe2b3f0aa476caa77cb44414206f6f7c723d47c78a8b9ffd5f5d1319d994520dee9c044d4cb145a2528abf00427c9198a122fefd3e0c85eeff3dcdb5ea
|
7
|
+
data.tar.gz: d9a91d46ef64f42d03d2dcc8356338d4fb27f9f81a7bce5ebc100b008476083239b1b7dbdfe599187e3b2f545ea55211f87b347e5a86a7a864d6d39d3b20e5b3
|
data/.rubocop.yml
CHANGED
@@ -6,12 +6,15 @@ AllCops:
|
|
6
6
|
- timet.gemspec
|
7
7
|
|
8
8
|
Metrics/MethodLength:
|
9
|
-
Max:
|
9
|
+
Max: 15
|
10
10
|
|
11
|
-
|
11
|
+
plugins: rubocop-rspec
|
12
12
|
|
13
13
|
RSpec/ExampleLength:
|
14
|
-
Max:
|
14
|
+
Max: 15
|
15
15
|
|
16
16
|
Metrics/CyclomaticComplexity:
|
17
17
|
Max: 8
|
18
|
+
|
19
|
+
RSpec/MultipleMemoizedHelpers:
|
20
|
+
Max: 15
|
data/CHANGELOG.md
CHANGED
@@ -1,8 +1,63 @@
|
|
1
|
-
## [
|
1
|
+
## [1.5.6] - 2025-04-07
|
2
|
+
|
3
|
+
**Improvements:**
|
4
|
+
|
5
|
+
- **Enhanced Edit Command:** The `edit` command now interactively prompts the user for both the field to edit and its new value if they are not provided as command-line arguments.
|
6
|
+
- **Improved Database Initialization:** Database initialization logic has been extracted into a separate `initialize_database` method within the `Application` class, improving code organization and readability.
|
7
|
+
- **Refactored `validate_and_update`:** The `validate_and_update` method in `ValidationEditHelper` has been refactored to handle both time fields and other fields more robustly. It now raises an `ArgumentError` for invalid fields and returns the updated item.
|
8
|
+
- **Enhanced Security:** The `update_item` method in the `Database` class now utilizes parameterized queries to prevent SQL injection vulnerabilities.
|
9
|
+
- **Improved Data Integrity:** The `update_time_columns` method in the `Database` class now correctly updates the `updated_at` and `created_at` columns to reflect the current time.
|
10
|
+
- **Code Clarity:** Increased the `Metrics/MethodLength` max length to 15 in `.rubocop.yml` to accommodate refactored code.
|
11
|
+
- **Dependency Updates:**
|
12
|
+
- Updated `aws-sdk-s3` to version 1.183.
|
13
|
+
- Updated `csv` to version 3.3.3.
|
14
|
+
- Updated `diff-lcs` to version 1.6.1.
|
15
|
+
- Updated `json` to version 2.10.2.
|
16
|
+
- Updated `parser` to version 3.3.7.4.
|
17
|
+
- Updated `rubocop` to version 1.75.2.
|
18
|
+
- Updated `rubocop-ast` to version 1.44.0.
|
19
|
+
- Added `prism` to version 1.4.0.
|
20
|
+
- Added `logger` to version 1.7.0.
|
21
|
+
- **Test Coverage:** Added new tests to `application_spec.rb` to cover the `edit` command, and to `database_spec.rb` to cover the `update_item` and `update_time_columns` methods. Refactored tests in `validation_edit_helper_spec.rb` to cover changes in `validate_and_update`.
|
22
|
+
- **Git Ignore:** Added `.qodo` to `.gitignore` to prevent it from being added to the repository.
|
23
|
+
|
24
|
+
**Bug Fixes:**
|
25
|
+
|
26
|
+
- **Edit Command:** The `edit` command now correctly updates the database with the new value.
|
27
|
+
- **`update_item`:** The `update_item` method is no longer vulnerable to SQL injection.
|
28
|
+
- **`update_time_columns`:** The `update_time_columns` method now correctly updates the `updated_at` and `created_at` columns.
|
29
|
+
- **.qodo file:** The .qodo file is now ignored by git.
|
30
|
+
|
31
|
+
## [1.5.5] - 2025-02-26
|
32
|
+
|
33
|
+
**Improvements:**
|
34
|
+
|
35
|
+
- Refactored `DatabaseSyncer` specs to improve clarity, maintainability, and reduce redundancy.
|
36
|
+
- Removed `#process_existing_item` as its logic was redundant.
|
37
|
+
- Improved `#remote_wins?` tests with `let` blocks and better descriptions.
|
38
|
+
- Split multi-expectation tests into smaller, focused tests.
|
39
|
+
- Improved test descriptions for accuracy and clarity.
|
40
|
+
- Reorganized tests into more logical `describe` blocks.
|
41
|
+
- Moved S3 download tests to the `Timet::S3Supabase` `describe` block.
|
42
|
+
- Improved CSV export tests with extracted common data, centralized database setup, and a helper method.
|
43
|
+
- Improved message expectations in `#export_report` using `class_spy` and `have_received`.
|
44
|
+
- Improved test readability in `ApplicationHelper` with `let` blocks and separate `it` blocks.
|
45
|
+
- Refactored `DatabaseSyncer` specs with `let` blocks, focused examples, and explicit `expect` assertions.
|
46
|
+
- Prefer `have_received` for setting message expectations.
|
47
|
+
- Improved granularity in specs with smaller, focused examples and shared test data.
|
48
|
+
- Updated gem dependencies to latest versions.
|
49
|
+
- Overall code cleanup and improved test structure.
|
50
|
+
- Update the way rubocop plugins are defined in the config.
|
51
|
+
- Removed unnecessary comments.
|
52
|
+
|
53
|
+
**Bug Fixes:**
|
54
|
+
|
55
|
+
- Fixed a bug in the time field update logic in `validation_edit_helper`, specifically regarding the end time.
|
2
56
|
|
3
57
|
## [1.5.4] - 2025-02-11
|
4
58
|
|
5
59
|
**Improvements:**
|
60
|
+
|
6
61
|
- Added `.env` file creation in CI workflow for testing environment variables.
|
7
62
|
- Updated Code Climate coverage reporting to use `simplecov-lcov` for LCOV format compatibility.
|
8
63
|
- Refactored validation error message tests in `ValidationEditHelper` for clarity and maintainability.
|
@@ -16,6 +71,7 @@
|
|
16
71
|
- Improved error handling and added tests for `S3Supabase`.
|
17
72
|
|
18
73
|
**Bug Fixes:**
|
74
|
+
|
19
75
|
- Fixed environment variable validation in `S3Supabase` to handle `nil` values.
|
20
76
|
- Resolved issues with database synchronization logic in `DatabaseSyncer`.
|
21
77
|
- Fixed test setup and cleanup in `S3Supabase` and `DatabaseSyncer` specs.
|
data/lib/timet/application.rb
CHANGED
@@ -49,22 +49,49 @@ module Timet
|
|
49
49
|
|
50
50
|
BUCKET = 'timet'
|
51
51
|
|
52
|
-
|
53
|
-
|
52
|
+
no_commands do
|
53
|
+
# Initializes the database connection based on the provided options.
|
54
|
+
#
|
55
|
+
# This method determines how to initialize the database connection based on the options provided.
|
56
|
+
# It supports injecting a database instance for testing, using a fallback for RSpec, and initializing
|
57
|
+
# a production database based on valid command arguments.
|
58
|
+
#
|
59
|
+
# @param options [Hash] A hash of options that may include a :database key for injecting a database instance.
|
60
|
+
# @option options [Database] :database An instance of the Database class to be used directly.
|
61
|
+
# @option options [Hash] :current_command The current command being executed, used to validate production
|
62
|
+
# database initialization.
|
63
|
+
#
|
64
|
+
# @return [void] This method does not return a value; it initializes the `@db` instance variable.
|
65
|
+
#
|
66
|
+
# @raise [SystemExit] If invalid arguments are provided for production database initialization.
|
67
|
+
def initialize_database(options)
|
68
|
+
db_from_options = options[:database]
|
54
69
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
70
|
+
# Allow injecting a database instance, primarily for testing
|
71
|
+
if db_from_options
|
72
|
+
@db = db_from_options
|
73
|
+
elsif defined?(RSpec)
|
74
|
+
# Fallback for RSpec if not injected (though injection is preferred)
|
60
75
|
@db = Database.new
|
61
76
|
else
|
62
|
-
|
63
|
-
|
77
|
+
# Production database initialization
|
78
|
+
command_name = options.dig(:current_command, :name)
|
79
|
+
if VALID_ARGUMENTS.include?(command_name)
|
80
|
+
@db = Database.new
|
81
|
+
else
|
82
|
+
warn 'Invalid arguments provided. Please check your input.'
|
83
|
+
exit(1)
|
84
|
+
end
|
64
85
|
end
|
65
86
|
end
|
66
87
|
end
|
67
88
|
|
89
|
+
def initialize(*args)
|
90
|
+
super
|
91
|
+
options = args[2] || {} # Third argument is the options hash
|
92
|
+
initialize_database(options)
|
93
|
+
end
|
94
|
+
|
68
95
|
desc "start [tag] --notes='' --pomodoro=[min]",
|
69
96
|
'Start time tracking for a task labeled with the provided [tag], notes and "pomodoro time"
|
70
97
|
in minutes (optional).
|
@@ -219,13 +246,14 @@ module Timet
|
|
219
246
|
return puts "No tracked time found for id: #{id}" unless item
|
220
247
|
|
221
248
|
display_item(item)
|
222
|
-
|
249
|
+
if field.nil? || new_value.nil?
|
223
250
|
field = select_field_to_edit
|
224
251
|
new_value = prompt_for_new_value(item, field)
|
225
252
|
end
|
226
253
|
|
227
254
|
updated_item = validate_and_update(item, field, new_value)
|
228
|
-
|
255
|
+
@db.update_item(id, field, updated_item[FIELD_INDEX[field]])
|
256
|
+
display_item(updated_item)
|
229
257
|
end
|
230
258
|
|
231
259
|
desc 'delete (d) [id]', 'Delete task => tt d 23'
|
data/lib/timet/database.rb
CHANGED
@@ -115,9 +115,7 @@ module Timet
|
|
115
115
|
#
|
116
116
|
# @note The method executes SQL to update the specified field of the item with the given ID.
|
117
117
|
def update_item(id, field, value)
|
118
|
-
|
119
|
-
|
120
|
-
execute_sql("UPDATE items SET #{field}='#{value}', updated_at=#{Time.now.utc.to_i} WHERE id = #{id}")
|
118
|
+
execute_sql("UPDATE items SET #{field} = ?, updated_at = ? WHERE id = ?", [value, Time.now.utc.to_i, id])
|
121
119
|
end
|
122
120
|
|
123
121
|
# Deletes an item from the items table.
|
@@ -162,19 +160,21 @@ module Timet
|
|
162
160
|
result.empty? ? nil : result[0]
|
163
161
|
end
|
164
162
|
|
165
|
-
# Finds an item
|
163
|
+
# Finds an item by its ID.
|
166
164
|
#
|
167
|
-
# @param id [Integer] The ID of the item to
|
165
|
+
# @param id [Integer] The ID of the item to find.
|
168
166
|
#
|
169
|
-
# @return [Array, nil] The item as an array
|
167
|
+
# @return [Array, nil] The item as an array if found, nil otherwise.
|
170
168
|
#
|
171
169
|
# @example Find an item with ID 1
|
172
|
-
# find_item(1)
|
170
|
+
# find_item(1) # => [1, 1678886400, 1678890000, 'work', 'notes', nil, 1678890000, 1678886400, nil]
|
173
171
|
#
|
174
|
-
# @note The method executes SQL to find the item
|
172
|
+
# @note The method executes a SQL query to find the item by its ID.
|
173
|
+
# @note If the item is found, it returns the item as an array.
|
174
|
+
# @note If the item is not found, it returns nil.
|
175
175
|
def find_item(id)
|
176
|
-
result = execute_sql('SELECT * FROM items WHERE id = ?
|
177
|
-
result.
|
176
|
+
result = execute_sql('SELECT * FROM items WHERE id = ?', [id])
|
177
|
+
result.first.dup if result.any? # Add .dup to create a copy
|
178
178
|
end
|
179
179
|
|
180
180
|
# Fetches all items from the items table that have a start time greater than or equal to today.
|
@@ -316,7 +316,7 @@ module Timet
|
|
316
316
|
result.each do |item|
|
317
317
|
id = item[0]
|
318
318
|
end_time = item[2]
|
319
|
-
execute_sql(
|
319
|
+
execute_sql('UPDATE items SET updated_at = ?, created_at = ? WHERE id = ?', [end_time, end_time, id])
|
320
320
|
end
|
321
321
|
end
|
322
322
|
end
|
@@ -9,32 +9,42 @@ module Timet
|
|
9
9
|
# Constants for time fields.
|
10
10
|
TIME_FIELDS = %w[start end].freeze
|
11
11
|
|
12
|
-
# Validates and updates
|
12
|
+
# Validates and updates an item's attribute based on the provided field and new value.
|
13
13
|
#
|
14
|
-
# @param item [Array] The
|
14
|
+
# @param item [Array] The item to be updated.
|
15
15
|
# @param field [String] The field to be updated.
|
16
|
-
# @param new_value [String
|
17
|
-
#
|
18
|
-
# @return [Array, nil] The updated tracking item if the update was successful, otherwise nil.
|
16
|
+
# @param new_value [String] The new value for the field.
|
19
17
|
#
|
20
|
-
# @
|
21
|
-
# validate_and_update(item, 'notes', 'Updated notes')
|
18
|
+
# @return [Array] The updated item.
|
22
19
|
#
|
23
|
-
# @
|
24
|
-
# @note If the field is not a time field, it directly updates the field with the new value.
|
25
|
-
# @note The method returns the updated tracking item if the update was successful.
|
20
|
+
# @raise [ArgumentError] If the field is invalid or the new value is invalid.
|
26
21
|
def validate_and_update(item, field, new_value)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
22
|
+
case field
|
23
|
+
when 'notes'
|
24
|
+
item[4] = new_value
|
25
|
+
when 'tag'
|
26
|
+
item[3] = new_value
|
27
|
+
when 'start'
|
28
|
+
item[1] = validate_time(new_value)
|
29
|
+
when 'end'
|
30
|
+
item[2] = validate_time(new_value)
|
33
31
|
else
|
34
|
-
|
32
|
+
raise ArgumentError, "Invalid field: #{field}"
|
35
33
|
end
|
34
|
+
item
|
35
|
+
end
|
36
36
|
|
37
|
-
|
37
|
+
# Validates if a given time string is in a valid format.
|
38
|
+
#
|
39
|
+
# @param time_str [String] The time string to validate.
|
40
|
+
#
|
41
|
+
# @return [Integer] The validated time as an integer.
|
42
|
+
#
|
43
|
+
# @raise [ArgumentError] If the time string is not in a valid format.
|
44
|
+
def validate_time(time_str)
|
45
|
+
Time.parse(time_str).to_i
|
46
|
+
rescue ArgumentError
|
47
|
+
raise ArgumentError, "Invalid time format: #{time_str}"
|
38
48
|
end
|
39
49
|
|
40
50
|
private
|
@@ -92,9 +102,9 @@ module Timet
|
|
92
102
|
def update_time_field(item, field, new_time)
|
93
103
|
field_index = Timet::Application::FIELD_INDEX[field]
|
94
104
|
timestamp = item[field_index]
|
95
|
-
|
96
|
-
|
97
|
-
DateTime.strptime(
|
105
|
+
edit_time = Time.at(timestamp || item[1]).to_s.split
|
106
|
+
edit_time[1] = new_time
|
107
|
+
DateTime.strptime(edit_time.join(' '), '%Y-%m-%d %H:%M:%S %z').to_time
|
98
108
|
end
|
99
109
|
|
100
110
|
# Validates if a new time value is valid for a specific time field (start or end).
|
@@ -116,9 +126,9 @@ module Timet
|
|
116
126
|
item_after_start = fetch_item_after_start(id)
|
117
127
|
|
118
128
|
if field == 'start'
|
119
|
-
new_value_epoch
|
129
|
+
new_value_epoch.between?(item_before_end, item_end)
|
120
130
|
else
|
121
|
-
new_value_epoch
|
131
|
+
new_value_epoch.between?(item_start, item_after_start)
|
122
132
|
end
|
123
133
|
end
|
124
134
|
|
data/lib/timet/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: timet
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.5.
|
4
|
+
version: 1.5.6
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Frank Vielma
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2025-
|
11
|
+
date: 2025-04-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: thor
|