turbo_luxury_stain_guide 1.0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 212cc769eeff72807d086f8ea0b43246c7daaba5e0dfe2d665ab48876feb5a8c
4
+ data.tar.gz: cc4784b2dbb84776af1e60ac2c3a42ff3f032ba6cdc4a17b1e5ecfadbbe775f2
5
+ SHA512:
6
+ metadata.gz: f142f762c0e3914e9d1c7d8229f83fc130f9eed9b64da29c2454043a602ceb58e0b557303b29b38b657531980e26ff7d4f81d81fa251b0fb890f917918d4aea0
7
+ data.tar.gz: 75388226da6e2cdbf3c6c6fa08efe95de86c458cb58f2c2b981b78ccde5892fa4477df414ba31fd739702134ef6a3ebf1fa8b94d8a8b55e5cd5e800dd4b273da
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Turbo Luxury Car Wash
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # turbo_luxury_stain_guide
2
+
3
+ Step-by-step first aid for car interior stains. Give it a stain (coffee, grease, ink, blood or vomit,
4
+ pet accident, mud, chocolate, dye transfer) and the surface (fabric, leather, vinyl, plastic, carpet),
5
+ and it returns the supplies, the steps in order, what to avoid, how urgent it is and whether the job is
6
+ better left to a professional. Pure Ruby, no dependencies, with a command-line tool.
7
+
8
+ Maintained by Turbo Luxury Car Wash, the team behind [car interior cleaning Abu Dhabi](https://turboluxurycarwash.ae/services/car-interior-cleaning-abu-dhabi/) with mobile service at the customer's location.
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ gem install turbo_luxury_stain_guide
14
+ ```
15
+
16
+ ## Library
17
+
18
+ ```ruby
19
+ require "turbo_luxury_stain_guide"
20
+
21
+ advice = TurboLuxuryStainGuide.advise("latte", "leather") # aliases work
22
+ advice.urgency # => "high"
23
+ advice.steps # ordered steps
24
+ advice.avoid # what not to do
25
+ advice.surface_note # leather-specific note
26
+ advice.professional # => false
27
+ puts advice
28
+ ```
29
+
30
+ Unknown stains or surfaces raise `TurboLuxuryStainGuide::Error` with the valid choices.
31
+
32
+ ## CLI
33
+
34
+ ```sh
35
+ turbo-luxury-stain-guide advise coffee leather
36
+ turbo-luxury-stain-guide list
37
+ ```
38
+
39
+ Example output:
40
+
41
+ ```
42
+ Coffee, tea and sugary drinks on leather seat (act straight away)
43
+
44
+ You need: microfibre cloths, cool water, mild upholstery cleaner
45
+
46
+ 1. Blot (never rub) to lift as much liquid as possible.
47
+ 2. Dab with a cloth dampened in cool water, working from the edge toward the centre.
48
+ 3. Apply a mild upholstery cleaner, agitate gently and blot again.
49
+ 4. Rinse with a barely damp cloth to remove cleaner residue, then dry.
50
+ Note for leather: Use a pH-neutral leather cleaner instead of general upholstery cleaner, then condition once dry.
51
+
52
+ Avoid: Hot water, which can set tannins
53
+ Avoid: Rubbing, which spreads the stain into the fibres
54
+ ```
55
+
56
+ The advice is general guidance. Always test a cleaner on a hidden spot first, and use a professional
57
+ for old, large or colour-lifting stains.
58
+
59
+ ## Tests
60
+
61
+ ```sh
62
+ ruby test/stain_guide_test.rb
63
+ ```
64
+
65
+ ## Licence
66
+
67
+ MIT
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require "turbo_luxury_stain_guide"
3
+
4
+ HELP = <<~TXT
5
+ turbo-luxury-stain-guide - car interior stain first aid
6
+
7
+ Usage:
8
+ turbo-luxury-stain-guide advise STAIN [SURFACE] (SURFACE defaults to fabric)
9
+ turbo-luxury-stain-guide list
10
+ TXT
11
+
12
+ args = ARGV.dup
13
+ case args.first
14
+ when "list"
15
+ puts "Stains: #{TurboLuxuryStainGuide.stains.join(', ')}"
16
+ puts "Surfaces: #{TurboLuxuryStainGuide.surfaces.join(', ')}"
17
+ when "advise"
18
+ if args[1].nil?
19
+ warn "advise needs a stain name"
20
+ exit 2
21
+ end
22
+ begin
23
+ puts TurboLuxuryStainGuide.advise(args[1], args[2] || "fabric")
24
+ rescue TurboLuxuryStainGuide::Error => e
25
+ warn e.message
26
+ exit 2
27
+ end
28
+ when nil, "-h", "--help"
29
+ puts HELP
30
+ exit(args.empty? ? 1 : 0)
31
+ else
32
+ warn "unknown command #{args.first.inspect}"
33
+ warn HELP
34
+ exit 2
35
+ end
@@ -0,0 +1,46 @@
1
+ module TurboLuxuryStainGuide
2
+ # Combines the stain data with surface-specific notes.
3
+ module Advisor
4
+ Advice = Struct.new(:stain, :stain_name, :surface, :surface_name, :urgency, :supplies, :steps, :avoid,
5
+ :surface_note, :professional, keyword_init: true) do
6
+ def to_s
7
+ lines = ["#{stain_name} on #{surface_name.downcase} (act #{urgency_phrase})", ""]
8
+ lines << "You need: #{supplies.join(', ')}"
9
+ lines << ""
10
+ steps.each_with_index { |s, i| lines << "#{i + 1}. #{s}" }
11
+ lines << "Note for #{surface}: #{surface_note}" if surface_note
12
+ lines << ""
13
+ avoid.each { |a| lines << "Avoid: #{a}" }
14
+ lines << "If it is old, large or the colour is lifting from the seat itself, a professional clean is the safe choice." if professional
15
+ lines.join("\n")
16
+ end
17
+
18
+ private
19
+
20
+ def urgency_phrase
21
+ { "high" => "straight away", "medium" => "within the hour", "low" => "when dry" }.fetch(urgency, urgency)
22
+ end
23
+ end
24
+
25
+ module_function
26
+
27
+ def resolve(aliases, table, name)
28
+ key = name.to_s.strip.downcase
29
+ return key if table.key?(key)
30
+ aliases[key]
31
+ end
32
+
33
+ def advise(stain, surface)
34
+ stain_id = TurboLuxuryStainGuide.resolve_stain(stain)
35
+ raise TurboLuxuryStainGuide::Error, "Unknown stain #{stain.inspect}. Try: #{Data::STAINS.keys.join(', ')}" unless stain_id
36
+ surface_id = TurboLuxuryStainGuide.resolve_surface(surface)
37
+ raise TurboLuxuryStainGuide::Error, "Unknown surface #{surface.inspect}. Try: #{Data::SURFACES.keys.join(', ')}" unless surface_id
38
+ s = Data::STAINS.fetch(stain_id)
39
+ Advice.new(
40
+ stain: stain_id, stain_name: s[:name], surface: surface_id, surface_name: Data::SURFACES.fetch(surface_id),
41
+ urgency: s[:urgency], supplies: s[:supplies], steps: s[:steps], avoid: s[:avoid],
42
+ surface_note: s[:surface_notes][surface_id], professional: s[:professional]
43
+ )
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,143 @@
1
+ module TurboLuxuryStainGuide
2
+ module Data
3
+ SURFACES = {
4
+ "fabric" => "Fabric seat or headliner",
5
+ "leather" => "Leather seat",
6
+ "vinyl" => "Vinyl or synthetic leather",
7
+ "plastic" => "Hard plastic trim or dashboard",
8
+ "carpet" => "Carpet or floor mat"
9
+ }.freeze
10
+
11
+ SURFACE_ALIASES = {
12
+ "cloth" => "fabric", "upholstery" => "fabric", "seat" => "fabric", "headliner" => "fabric",
13
+ "leatherette" => "vinyl", "pu" => "vinyl", "synthetic" => "vinyl",
14
+ "trim" => "plastic", "dashboard" => "plastic", "dash" => "plastic", "door panel" => "plastic",
15
+ "mat" => "carpet", "floor" => "carpet", "floor mat" => "carpet", "rug" => "carpet"
16
+ }.freeze
17
+
18
+ # urgency: how quickly to act. method: base technique; surface_notes: overrides per surface.
19
+ STAINS = {
20
+ "coffee" => {
21
+ name: "Coffee, tea and sugary drinks", urgency: "high",
22
+ supplies: ["microfibre cloths", "cool water", "mild upholstery cleaner"],
23
+ steps: ["Blot (never rub) to lift as much liquid as possible.",
24
+ "Dab with a cloth dampened in cool water, working from the edge toward the centre.",
25
+ "Apply a mild upholstery cleaner, agitate gently and blot again.",
26
+ "Rinse with a barely damp cloth to remove cleaner residue, then dry."],
27
+ avoid: ["Hot water, which can set tannins", "Rubbing, which spreads the stain into the fibres"],
28
+ surface_notes: {
29
+ "leather" => "Use a pH-neutral leather cleaner instead of general upholstery cleaner, then condition once dry.",
30
+ "carpet" => "Blot with a thick pad of paper towel pressed down firmly before cleaning.",
31
+ "plastic" => "Wipe with a damp cloth; sugary residue is sticky, so follow with a dry cloth."
32
+ },
33
+ professional: false
34
+ },
35
+ "grease" => {
36
+ name: "Grease, oil, sunscreen and food fat", urgency: "medium",
37
+ supplies: ["paper towels", "cornstarch or baking soda", "soft brush", "vacuum", "upholstery cleaner"],
38
+ steps: ["Blot off any surface oil without pressing it deeper.",
39
+ "Cover with cornstarch or baking soda and leave for 15 to 30 minutes to absorb the oil.",
40
+ "Vacuum the powder, repeat if the spot is still dark.",
41
+ "Treat what remains with upholstery cleaner and blot dry."],
42
+ avoid: ["Water first: it spreads oil and makes it harder to lift"],
43
+ surface_notes: {
44
+ "leather" => "Wipe gently, absorb with powder, then clean with leather cleaner and condition. Do not scrub.",
45
+ "plastic" => "Wipe with a diluted interior cleaner; sunscreen film on trim usually needs two passes."
46
+ },
47
+ professional: false
48
+ },
49
+ "ink" => {
50
+ name: "Ink and pen marks", urgency: "high",
51
+ supplies: ["isopropyl alcohol", "cotton swabs", "white cloth"],
52
+ steps: ["Test the alcohol on a hidden spot first.",
53
+ "Dab (do not wipe) with a swab dampened with isopropyl alcohol, using a fresh swab as each one picks up ink.",
54
+ "Blot with a clean white cloth after each pass.",
55
+ "Finish with a damp cloth and dry."],
56
+ avoid: ["Wiping across the mark, which spreads ink", "Alcohol on unfinished or aniline leather"],
57
+ surface_notes: {
58
+ "leather" => "Ink on leather often needs a professional, especially on pale or aniline-dyed leather. Try a dedicated leather ink remover only after a hidden-spot test.",
59
+ "vinyl" => "A swab of isopropyl alcohol usually lifts ballpoint ink from vinyl in a few passes."
60
+ },
61
+ professional: true
62
+ },
63
+ "blood" => {
64
+ name: "Blood, vomit, milk and other protein stains", urgency: "high",
65
+ supplies: ["cold water", "microfibre cloths", "enzyme cleaner", "mild soap"],
66
+ steps: ["Blot up solids and liquid first; scrape solids with a card edge.",
67
+ "Dab with cold water only, working inward.",
68
+ "Apply an enzyme cleaner, leave for the time on the label, then blot.",
69
+ "Rinse with a barely damp cloth and dry with the doors open to remove odour."],
70
+ avoid: ["Hot water or steam, which cook proteins into the fibres", "Heavy soaking of seat foam"],
71
+ surface_notes: {
72
+ "leather" => "Wipe with a cloth dampened in cool water and a drop of mild soap, then leather cleaner. Skip enzyme sprays on untreated leather.",
73
+ "carpet" => "Use extra enzyme cleaner and blot repeatedly: carpet padding holds odour."
74
+ },
75
+ professional: false
76
+ },
77
+ "pet" => {
78
+ name: "Pet urine and accidents", urgency: "high",
79
+ supplies: ["paper towels", "enzyme cleaner for pet odour", "microfibre cloths", "fan or open doors"],
80
+ steps: ["Blot as much as possible with paper towels pressed down firmly.",
81
+ "Saturate the area lightly with enzyme cleaner so it reaches the same depth as the accident.",
82
+ "Leave it damp for the label time, then blot and let it air-dry fully with the doors open.",
83
+ "Repeat if the smell returns when the car warms up."],
84
+ avoid: ["Ammonia cleaners, which smell like urine to pets and encourage repeat marking",
85
+ "Steam, which can lock the odour in"],
86
+ surface_notes: {
87
+ "fabric" => "If it soaked into the foam, a professional hot-water extraction is the reliable fix.",
88
+ "leather" => "Clean the surface promptly and check the seams; liquid in the stitching leaves odour."
89
+ },
90
+ professional: true
91
+ },
92
+ "mud" => {
93
+ name: "Mud, sand and dirt", urgency: "low",
94
+ supplies: ["stiff brush", "vacuum", "upholstery cleaner", "microfibre cloths"],
95
+ steps: ["Let wet mud dry completely.",
96
+ "Crumble and brush it off, then vacuum thoroughly.",
97
+ "Treat any shadow left behind with upholstery cleaner and blot dry."],
98
+ avoid: ["Wiping wet mud, which grinds it into the fibres"],
99
+ surface_notes: {
100
+ "leather" => "Vacuum first so grit does not scratch the grain, then wipe with leather cleaner.",
101
+ "carpet" => "Beat or brush the mat outside, then vacuum slowly in two directions."
102
+ },
103
+ professional: false
104
+ },
105
+ "chocolate" => {
106
+ name: "Chocolate, melted sweets and gum", urgency: "medium",
107
+ supplies: ["ice cubes in a bag", "plastic card", "mild soap", "microfibre cloths"],
108
+ steps: ["Harden the residue with an ice bag for a few minutes.",
109
+ "Lift the solid with the edge of a card.",
110
+ "Clean the remaining mark with cool water and a drop of mild soap, then blot dry."],
111
+ avoid: ["Hot water or heat, which melts sugar deeper into the surface"],
112
+ surface_notes: {
113
+ "leather" => "Do not use ice directly on cold-sensitive leather for long; lift the residue gently and finish with leather cleaner."
114
+ },
115
+ professional: false
116
+ },
117
+ "dye" => {
118
+ name: "Red wine, juice, makeup and dye transfer", urgency: "high",
119
+ supplies: ["cool water", "microfibre cloths", "mild upholstery cleaner", "white vinegar (diluted)"],
120
+ steps: ["Blot immediately.",
121
+ "Dab with cool water, then a mild upholstery cleaner.",
122
+ "For fabric, try a half-and-half mix of white vinegar and water on a hidden spot first, then on the stain.",
123
+ "Rinse with a barely damp cloth and dry."],
124
+ avoid: ["Vinegar on leather or natural stone trim", "Letting the stain dry before treating it"],
125
+ surface_notes: {
126
+ "leather" => "Dye transfer from jeans or clothing needs a leather-specific cleaner; stop if colour lifts from the seat itself and call a professional."
127
+ },
128
+ professional: true
129
+ }
130
+ }.freeze
131
+
132
+ STAIN_ALIASES = {
133
+ "tea" => "coffee", "latte" => "coffee", "soda" => "coffee", "juice" => "dye", "cola" => "coffee",
134
+ "oil" => "grease", "sunscreen" => "grease", "butter" => "grease", "food" => "grease", "fat" => "grease",
135
+ "pen" => "ink", "marker" => "ink", "ballpoint" => "ink",
136
+ "vomit" => "blood", "milk" => "blood", "protein" => "blood",
137
+ "urine" => "pet", "dog" => "pet", "cat" => "pet", "accident" => "pet",
138
+ "dirt" => "mud", "sand" => "mud",
139
+ "sweets" => "chocolate", "candy" => "chocolate", "gum" => "chocolate",
140
+ "wine" => "dye", "red wine" => "dye", "makeup" => "dye", "denim" => "dye", "lipstick" => "dye"
141
+ }.freeze
142
+ end
143
+ end
@@ -0,0 +1,3 @@
1
+ module TurboLuxuryStainGuide
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,35 @@
1
+ require_relative "turbo_luxury_stain_guide/version"
2
+ require_relative "turbo_luxury_stain_guide/data"
3
+ require_relative "turbo_luxury_stain_guide/advisor"
4
+
5
+ # First-aid advice for car interior stains.
6
+ module TurboLuxuryStainGuide
7
+ class Error < StandardError; end
8
+
9
+ module_function
10
+
11
+ # Advice for a stain on a surface. Both are case-insensitive and aliases are accepted.
12
+ def advise(stain, surface = "fabric")
13
+ Advisor.advise(stain, surface)
14
+ end
15
+
16
+ # All stain ids.
17
+ def stains
18
+ Data::STAINS.keys
19
+ end
20
+
21
+ # All surface ids.
22
+ def surfaces
23
+ Data::SURFACES.keys
24
+ end
25
+
26
+ # Resolve an alias ("latte") to a stain id ("coffee"), or nil.
27
+ def resolve_stain(name)
28
+ Advisor.resolve(Data::STAIN_ALIASES, Data::STAINS, name)
29
+ end
30
+
31
+ # Resolve an alias ("seat leather") to a surface id, or nil.
32
+ def resolve_surface(name)
33
+ Advisor.resolve(Data::SURFACE_ALIASES, Data::SURFACES, name)
34
+ end
35
+ end
metadata ADDED
@@ -0,0 +1,52 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: turbo_luxury_stain_guide
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Turbo Luxury Car Wash
8
+ bindir: exe
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies: []
12
+ description: Look up how to treat coffee, grease, ink, blood, pet accidents, mud and
13
+ other stains on fabric, leather, vinyl, plastic or carpet in a car, with supplies,
14
+ what to avoid and when to call a professional.
15
+ executables:
16
+ - turbo-luxury-stain-guide
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - exe/turbo-luxury-stain-guide
23
+ - lib/turbo_luxury_stain_guide.rb
24
+ - lib/turbo_luxury_stain_guide/advisor.rb
25
+ - lib/turbo_luxury_stain_guide/data.rb
26
+ - lib/turbo_luxury_stain_guide/version.rb
27
+ homepage: https://turboluxurycarwash.ae/services/car-interior-cleaning-abu-dhabi/
28
+ licenses:
29
+ - MIT
30
+ metadata:
31
+ homepage_uri: https://turboluxurycarwash.ae/services/car-interior-cleaning-abu-dhabi/
32
+ source_code_uri: https://github.com/nbkdevs/turbo-luxury/tree/main/packages/rubygems
33
+ bug_tracker_uri: https://github.com/nbkdevs/turbo-luxury/issues
34
+ rubygems_mfa_required: 'true'
35
+ rdoc_options: []
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: 2.7.0
43
+ required_rubygems_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubygems_version: 4.0.20
50
+ specification_version: 4
51
+ summary: Step-by-step first aid for car interior stains by stain type and seat surface.
52
+ test_files: []