mycowriter 0.1.5 → 0.1.7

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 48618e785c70abb88fdb18febb2d72e7c63e94b0334d9d94df64b4fd6ec0108e
4
- data.tar.gz: d79cbcd5f82772306c0266bab9b71893ed776bca32a05ad6b002354f014a4e2a
3
+ metadata.gz: 39c5a2d07b1a5b470a372a3164de8055d040d75a2dc0688b89ddc00312b182ac
4
+ data.tar.gz: 28b25751676dfd32ebd30605a7431a7a6572bbaa31fea296a2414372d8ce15fb
5
5
  SHA512:
6
- metadata.gz: 24c7af5dbe629060fa01a551d0706ac07c04e6c74f762db1c3bc23d2db7674fdbc2c188a2b22cf7e13e48e2e9be28e854a0a47456221339eec7aeadad29551bb
7
- data.tar.gz: bb074ee4851ad39e846aabff2d95a9f2021a80508b7a054193c2d685cbce29c2eb0291f6d853d7bb1cf4f57e0b6f0749871a1f6271b97060e8286488a5217244
6
+ metadata.gz: a5ba7679b9d27b760637e7becfa3bf91f1820d4fe29fb587b2e32f446bf734481778a5e837b0ed4e48797b2bd709dade21fe45d3f0b950862f0837e71d3b5af7
7
+ data.tar.gz: e2dfde5dd18f9d103e91a13a12436a09163653d1a600834802cbbb5ef7ac93f48571a5c6ac29b29a30f7904cd69ed9eb0112f7ab2b99bc6b2df598166733f387
data/CHANGELOG.md CHANGED
@@ -5,6 +5,39 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.1.7] - 2026-02-21
9
+
10
+ ### Fixed
11
+ - **CRITICAL:** Fixed SQL method compatibility - replaced deprecated `sanitize_sql_for_order` with `Arel.sql` + `sanitize_sql_array`
12
+ - Resolves `NoMethodError: undefined method 'sanitize_sql_for_order'` error in Rails 8.0+
13
+ - All ORDER BY clauses now use proper Rails SQL injection protection pattern
14
+
15
+ ### Added
16
+ - Added `genus_name` parameter to species endpoint for inline autocomplete filtering
17
+ - Species autocomplete now filters by preceding genus name (e.g., "Ganoderma sess" → only Ganoderma species)
18
+ - Supports both mushroom form context (`mushroom_id` parameter) and inline text context (`genus_name` parameter)
19
+
20
+ ### Technical Details
21
+ - Changed from `Model.connection.sanitize_sql_for_order([...])` to `Arel.sql(Model.sanitize_sql_array([...]))`
22
+ - Applied fix to genera, species, and mb_lists fallback methods
23
+ - Maintains backward compatibility - existing `mushroom_id` filtering still works
24
+ - New `genus_name` parameter is optional and takes precedence over `mushroom_id` when both present
25
+
26
+ ## [0.1.6] - 2026-02-21
27
+
28
+ ### Fixed
29
+ - **CRITICAL:** Fixed autocomplete result ranking to prioritize best matches over alphabetical order
30
+ - Exact matches now appear first, then prefix matches, then substring matches
31
+ - Example: typing "sess" now shows "sessile" at top of results instead of 12th position after alphabetical entries
32
+ - Applied smart ranking to genera autocomplete, species autocomplete, and mb_lists fallback methods
33
+ - Resolves issue where large genus datasets (e.g., 394 Ganoderma species) buried relevant matches deep in alphabetical lists
34
+
35
+ ### Technical Details
36
+ - Replaced simple alphabetical `ORDER BY name` with CASE-based priority ranking
37
+ - Priority order: 1=exact match, 2=prefix match, 3=substring match, within each group: alphabetical
38
+ - Uses `sanitize_sql_for_order` for SQL injection protection
39
+ - Maintains backward compatibility - same JSON API contract, no breaking changes
40
+
8
41
  ## [0.1.1] - 2025-02-20
9
42
 
10
43
  ### Changed
@@ -11,7 +11,14 @@ module Mycowriter
11
11
  ::Genus
12
12
  .where("name LIKE ?", "#{::Genus.sanitize_sql_like(query)}%")
13
13
  .select(:id, :name)
14
- .order(:name)
14
+ .order(
15
+ Arel.sql(
16
+ ::Genus.sanitize_sql_array([
17
+ "CASE WHEN LOWER(name) = LOWER(?) THEN 1 ELSE 2 END, name",
18
+ query
19
+ ])
20
+ )
21
+ )
15
22
  .limit(Mycowriter.results_limit)
16
23
  .map { |g| { id: g.id, name: g.name } }
17
24
  else
@@ -24,18 +31,23 @@ module Mycowriter
24
31
  render json: results
25
32
  end
26
33
 
27
- # GET /mycowriter/autocomplete/species.json?q=placo&mushroom_id=1
34
+ # GET /mycowriter/autocomplete/species.json?q=placo&mushroom_id=1&genus_name=Ganoderma
28
35
  def species
29
36
  query = params[:q].to_s.strip
30
37
  mushroom_id = params[:mushroom_id]
38
+ genus_name = params[:genus_name]
31
39
 
32
40
  results = if query.length >= Mycowriter.min_characters
33
41
  # Check if the host app has a Species model
34
42
  if defined?(::Species)
35
43
  scope = ::Species.where("name LIKE ?", "%#{::Species.sanitize_sql_like(query)}%")
36
44
 
37
- # Filter by selected genera for this mushroom
38
- if mushroom_id.present? && defined?(::Mushroom)
45
+ # Filter by genus name (for inline autocomplete)
46
+ if genus_name.present? && defined?(::Genus)
47
+ genus = ::Genus.find_by(name: genus_name)
48
+ scope = scope.where(genera_id: genus.id) if genus
49
+ # OR filter by selected genera for this mushroom (for mushroom form)
50
+ elsif mushroom_id.present? && defined?(::Mushroom)
39
51
  mushroom = ::Mushroom.find_by(id: mushroom_id)
40
52
  if mushroom && mushroom.respond_to?(:genera) && mushroom.genera.any?
41
53
  genera_ids = mushroom.genera.pluck(:id)
@@ -44,10 +56,23 @@ module Mycowriter
44
56
  end
45
57
 
46
58
  # Use includes to eager load genera and avoid N+1 queries
59
+ # Smart ranking: exact matches first, then prefix matches, then substring matches
47
60
  species_results = scope
48
61
  .includes(:genus)
49
62
  .select(:id, :name, :genera_id)
50
- .order(:name)
63
+ .order(
64
+ Arel.sql(
65
+ ::Species.sanitize_sql_array([
66
+ "CASE
67
+ WHEN LOWER(name) = LOWER(?) THEN 1
68
+ WHEN LOWER(name) LIKE LOWER(?) THEN 2
69
+ ELSE 3
70
+ END, name",
71
+ query,
72
+ "#{query}%"
73
+ ])
74
+ )
75
+ )
51
76
  .limit(Mycowriter.results_limit)
52
77
 
53
78
  species_results.map do |sp|
@@ -87,7 +112,19 @@ module Mycowriter
87
112
 
88
113
  scope
89
114
  .select(:id, :taxon_name)
90
- .order(:taxon_name)
115
+ .order(
116
+ Arel.sql(
117
+ ::MbList.sanitize_sql_array([
118
+ "CASE
119
+ WHEN LOWER(taxon_name) = LOWER(?) THEN 1
120
+ WHEN LOWER(taxon_name) LIKE LOWER(?) THEN 2
121
+ ELSE 3
122
+ END, taxon_name",
123
+ query,
124
+ "#{query}%"
125
+ ])
126
+ )
127
+ )
91
128
  .limit(Mycowriter.results_limit)
92
129
  .map { |m| { id: m.id, name: m.taxon_name } }
93
130
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Mycowriter
4
- VERSION = "0.1.5"
4
+ VERSION = "0.1.7"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mycowriter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.5
4
+ version: 0.1.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Will Johnston