Skip to content

Commit d007d66

Browse files
Toby CarvanToby Carvan
authored andcommitted
refactor: updated readme, added highlighted search results
1 parent 4bce3c7 commit d007d66

7 files changed

Lines changed: 107 additions & 30 deletions

File tree

README.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,12 @@ A Ruby command-line application for searching and analyzing client data from JSO
77
## Features
88

99
- **Name Search**: Search through all clients and return those with names matching a given query (case-insensitive, supports regex patterns)
10+
- TTY output includes **syntax highlighting** to visually highlight matched text
1011
- **Duplicate Email Detection**: Find clients with duplicate email addresses in the dataset
12+
- **Rating Filter**: Filter clients by minimum rating threshold
1113
- **Dataset Generation**: Generate realistic test datasets with customizable size and guaranteed duplicates
1214
- **Multiple Output Formats**: Support for TTY, CSV, JSON, XML, and YAML output formats
15+
- All formats support optional `result` fields (rating and feedback comments)
1316
- **Flexible Dataset Support**: Specify custom dataset files via command-line options
1417
- **Robust Error Handling**: Validates file existence and JSON format before processing
1518
- **Graceful Data Handling**: Safely processes datasets with missing or invalid fields
@@ -55,6 +58,7 @@ challenge duplicates -f data.json
5558
| `generate` | `g` | Generate test dataset | `challenge generate -f data.json --size 1000` |
5659
| `search` | `s` | Find clients by name, using regex | `challenge search "John" -f data.json` |
5760
| `duplicates` | `d` | Find duplicate emails | `challenge duplicates -f data.json` |
61+
| `filter_by_rating` | - | Filter clients by minimum rating | `challenge filter_by_rating 4.0 -f data.json` |
5862
| `version` | - | Show version number | `challenge version` |
5963

6064
### Output Formats
@@ -76,6 +80,10 @@ challenge search "^John" -f data.json # Names starting with "John"
7680
challenge search "Miller$" -f data.json # Names ending with "Miller"
7781
challenge search "J.*n" -f data.json # Names starting with J and ending with n
7882

83+
# Filter by rating
84+
challenge filter_by_rating 3.5 -f data.json # Clients with rating >= 3.5
85+
challenge filter_by_rating 4.0 -f data.json --output json
86+
7987
# Short aliases
8088
challenge s "John" -f data.json # search
8189
challenge d -f data.json # duplicates
@@ -101,22 +109,45 @@ The application expects JSON files containing an array of client objects with th
101109
{
102110
"id": 1,
103111
"full_name": "John Doe",
104-
"email": "john.doe@gmail.com"
112+
"email": "john.doe@gmail.com",
113+
"result": {
114+
"rating": 4.5,
115+
"feedback": [
116+
{
117+
"comment": "Great job on the project!",
118+
"date": "2023-10-01"
119+
},
120+
{
121+
"comment": "Excellent communication skills.",
122+
"date": "2023-10-15"
123+
}
124+
]
125+
}
105126
},
106127
{
107128
"id": 2,
108129
"full_name": "Jane Smith",
109-
"email": "jane.smith@yahoo.com"
130+
"email": "jane.smith@yahoo.com",
131+
"result": {
132+
"rating": 3.8,
133+
"feedback": []
134+
}
110135
}
111136
]
112137
```
113138

114-
Required fields:
139+
**Required fields:**
115140

116141
- `id`: Unique identifier
117142
- `full_name`: Client's full name
118143
- `email`: Client's email address
119144

145+
**Optional fields:**
146+
147+
- `result`: Object containing performance data (used by `filter_by_rating` command)
148+
- `rating`: Numeric rating value
149+
- `feedback`: Array of feedback objects with `comment` and `date` fields (date is optional)
150+
120151
## Testing
121152

122153
Run the test suite using RSpec:
@@ -249,7 +280,7 @@ Given more time, the following enhancements would be prioritized:
249280
- **Interactive Mode**: REPL-style interface for multiple queries
250281
- **Search Suggestions**: Auto-complete and suggestion features
251282
- **Progress Indicators**: Progress bars for long-running operations
252-
- **Colored Output**: Syntax highlighting and colored output for better readability
283+
- **Colored Output**: Syntax highlighting for search results in TTY format (implemented)
253284

254285
## Development
255286

lib/challenge/formatters/csv_formatter.rb

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ def format_search_results(results, query)
1010
if results.empty?
1111
"# No clients found matching '#{query}'"
1212
else
13-
lines = ["# Found #{results.size} client(s) matching '#{query}':", csv_header]
14-
results.each { |client| lines << format_client_csv(client) }
13+
lines = ["# Found #{results.size} client(s) matching '#{query}':", extended_csv_header]
14+
results.each { |client| lines << format_extended_client_csv(client) }
1515
lines.join("\n")
1616
end
1717
end
@@ -20,8 +20,8 @@ def format_duplicate_results(duplicates)
2020
if duplicates.empty?
2121
'# No duplicate emails found'
2222
else
23-
lines = ['# Found duplicate emails:', csv_header]
24-
duplicates.each { |client| lines << format_client_csv(client) }
23+
lines = ['# Found duplicate emails:', extended_csv_header]
24+
duplicates.each { |client| lines << format_extended_client_csv(client) }
2525
lines.join("\n")
2626
end
2727
end
@@ -54,15 +54,17 @@ def format_client_csv(client)
5454
CSV.generate_line([client['id'], client['full_name'], client['email']]).strip
5555
end
5656

57-
def filtered_csv_header
57+
def extended_csv_header
5858
'id,full_name,email,rating,feedback_comments'
5959
end
60+
alias filtered_csv_header extended_csv_header
6061

61-
def format_filtered_client_csv(client)
62+
def format_extended_client_csv(client)
6263
rating = client.dig('result', 'rating')
6364
feedback = feedback_comments(client).join(' | ')
6465
CSV.generate_line([client['id'], client['full_name'], client['email'], rating, feedback]).strip
6566
end
67+
alias format_filtered_client_csv format_extended_client_csv
6668

6769
def feedback_comments(client)
6870
Array(client.dig('result', 'feedback')).filter_map do |entry|

lib/challenge/formatters/json_formatter.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def format_search_results(results, query)
1010
{
1111
query: query,
1212
count: results.size,
13-
clients: results
13+
clients: results.map { |client| serialize_client(client) }
1414
}.to_json
1515
end
1616

@@ -21,7 +21,7 @@ def format_duplicate_results(duplicates)
2121
email_groups = duplicates.group_by { |client| client['email'] }
2222
{
2323
duplicates: email_groups.map do |email, clients|
24-
{ email: email, clients: clients }
24+
{ email: email, clients: clients.map { |client| serialize_client(client) } }
2525
end,
2626
count: duplicates.size
2727
}.to_json
@@ -53,18 +53,27 @@ def format_version(version)
5353

5454
private
5555

56-
def serialize_filtered_client(client)
57-
{
56+
def serialize_client(client)
57+
result = {
5858
id: client['id'],
5959
full_name: client['full_name'],
60-
email: client['email'],
61-
result: {
60+
email: client['email']
61+
}
62+
63+
if client['result']
64+
result[:result] = {
6265
rating: client.dig('result', 'rating'),
6366
feedback: Array(client.dig('result', 'feedback')).map do |entry|
6467
entry.is_a?(Hash) ? entry['comment'] : entry
6568
end.compact
6669
}
67-
}
70+
end
71+
72+
result
73+
end
74+
75+
def serialize_filtered_client(client)
76+
serialize_client(client)
6877
end
6978
end
7079
end

lib/challenge/formatters/tty_formatter.rb

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ def format_search_results(results, query)
1010
else
1111
lines = ["Found #{results.size} client(s) matching '#{query}':"]
1212
results.each do |client|
13-
lines << "- #{format_client(client)}"
13+
lines << "- #{format_client(client, query)}"
14+
lines.concat(format_client_details(client))
1415
end
1516
lines.join("\n")
1617
end
@@ -26,6 +27,7 @@ def format_duplicate_results(duplicates)
2627
lines << "\n#{email}:"
2728
clients.each do |client|
2829
lines << " - #{format_client(client)}"
30+
format_client_details(client).each { |detail| lines << " #{detail}" }
2931
end
3032
end
3133
lines.join("\n")
@@ -61,8 +63,32 @@ def format_version(version)
6163

6264
private
6365

64-
def format_client(client)
65-
"#{client['full_name']} <#{client['email']}> \e[90m##{client['id']}\e[0m"
66+
def format_client(client, highlight = nil)
67+
name = highlight ? highlight_match(client['full_name'], highlight) : client['full_name']
68+
email = client['email']
69+
70+
"#{name} <#{email}> \e[90m##{client['id']}\e[0m"
71+
end
72+
73+
def highlight_match(text, pattern)
74+
return text unless pattern
75+
76+
text.gsub(Regexp.new(pattern, Regexp::IGNORECASE)) do |match|
77+
"\e[1;33m#{match}\e[0m"
78+
end
79+
rescue RegexpError
80+
text
81+
end
82+
83+
def format_client_details(client)
84+
details = []
85+
rating = client.dig('result', 'rating')
86+
details << " Rating: #{rating}" if rating
87+
88+
feedback_comments = extract_feedback_comments(client)
89+
details << " Feedback: #{feedback_comments.map { |c| "\"#{c}\"" }.join(', ')}" if feedback_comments.any?
90+
91+
details
6692
end
6793

6894
def extract_feedback_comments(client)

lib/challenge/formatters/xml_formatter.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ class XMLFormatter
77
def format_search_results(results, query)
88
lines = ['<?xml version="1.0" encoding="UTF-8"?>']
99
lines << "<search_results query=\"#{escape_xml(query)}\" count=\"#{results.size}\">"
10-
results.each { |client| lines << format_client_xml(client, ' ') }
10+
results.each { |client| lines << format_filtered_client_xml(client, ' ') }
1111
lines << '</search_results>'
1212
lines.join("\n")
1313
end
@@ -21,7 +21,7 @@ def format_duplicate_results(duplicates)
2121
lines << "<duplicate_results count=\"#{duplicates.size}\">"
2222
email_groups.each do |email, clients|
2323
lines << " <duplicate_group email=\"#{escape_xml(email)}\">"
24-
clients.each { |client| lines << format_client_xml(client, ' ') }
24+
clients.each { |client| lines << format_filtered_client_xml(client, ' ') }
2525
lines << ' </duplicate_group>'
2626
end
2727
lines << '</duplicate_results>'

lib/challenge/formatters/yaml_formatter.rb

Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ def format_search_results(results, query)
1010
{
1111
'query' => query,
1212
'count' => results.size,
13-
'clients' => results
13+
'clients' => results.map { |client| serialize_client(client) }
1414
}.to_yaml
1515
end
1616

@@ -21,7 +21,7 @@ def format_duplicate_results(duplicates)
2121
email_groups = duplicates.group_by { |client| client['email'] }
2222
{
2323
'duplicates' => email_groups.map do |email, clients|
24-
{ 'email' => email, 'clients' => clients }
24+
{ 'email' => email, 'clients' => clients.map { |client| serialize_client(client) } }
2525
end,
2626
'count' => duplicates.size
2727
}.to_yaml
@@ -53,18 +53,27 @@ def format_version(version)
5353

5454
private
5555

56-
def serialize_filtered_client(client)
57-
{
56+
def serialize_client(client)
57+
result = {
5858
'id' => client['id'],
5959
'full_name' => client['full_name'],
60-
'email' => client['email'],
61-
'result' => {
60+
'email' => client['email']
61+
}
62+
63+
if client['result']
64+
result['result'] = {
6265
'rating' => client.dig('result', 'rating'),
6366
'feedback' => Array(client.dig('result', 'feedback')).filter_map do |entry|
6467
entry.is_a?(Hash) ? entry['comment'] : entry
6568
end
6669
}
67-
}
70+
end
71+
72+
result
73+
end
74+
75+
def serialize_filtered_client(client)
76+
serialize_client(client)
6877
end
6978
end
7079
end

lib/challenge/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# frozen_string_literal: true
22

33
module Challenge
4-
VERSION = '1.4'
4+
VERSION = '1.4.1'
55
end

0 commit comments

Comments
 (0)