infotable 0.1.0 → 1.0.1

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: fcc9d70ae1c44035698031c04e51a334a77d3d8985fe41ca987b623a571b056b
4
- data.tar.gz: 22469f7a9cca784d2452897de1169eef204dd3f992c055dc89f39f1dbb84b78f
3
+ metadata.gz: c5a1b907c771796e4046d0c74dabfdb403255e7cc4eec0d9f6cda1464f2f61fb
4
+ data.tar.gz: 352b469bb54ba80c3db5c05bfe9236be554575169af9354ff003d335e81131e4
5
5
  SHA512:
6
- metadata.gz: fc8b744dfed6976d6e225f50106bb6c6cdfc00fd61625904d02d2aba7676ecf4e9e36129bee6b986e1fec9537e62543f8d958e4fb631d346af8f8966a4eb6285
7
- data.tar.gz: 762d41b80a2ea2aef2e504586cf6da9098525da095d52aee5a5ed74625a4de3f66059ed9d89ff82f936277f4c6e55643e9e94e950fd01fafa2087fc9cc41a2c0
6
+ metadata.gz: 5df9ceb7142c4db38807616045f6ff3dd2946fd0719031c661870e7fe6f5fced31a7de71a4e8ab3cf922ee70d51c1182faa1e9da1bbaa486385ae64d481007e5
7
+ data.tar.gz: 1c40aa535af8a5b44fa1c3583cd09e4dbd3347a6cc4380e64e9fabc8fdda70dec9463a3b5cec22a10480810c05c50cbc6e91b14ca52ae590ab3eaefcbe81cfb7
data/README.md CHANGED
@@ -44,7 +44,7 @@ That's it! You now have a fully functional datatable with sorting and pagination
44
44
 
45
45
  - Rails 8.0+
46
46
  - Ruby 3.3+
47
- - PostgreSQL (filters use `ILIKE`, `HAVING` and `CAST(... AS numeric)`; other databases are not supported)
47
+ - PostgreSQL or SQLite (other databases are not supported). On SQLite, text filters and search ignore case for ASCII letters only (`ś` does not match `Ś`); PostgreSQL matches all letters
48
48
  - [Pagy](https://github.com/ddnexus/pagy) 43 (installed as a dependency)
49
49
  - Tailwind CSS v4 (via `tailwindcss-rails` gem) - **Required for styling**
50
50
  - Hotwire (Turbo + Stimulus)
@@ -0,0 +1,5 @@
1
+ /* Picked up by tailwindcss-rails (app/assets/tailwind/<engine>/engine.css): the host app
2
+ imports it with `@import "../builds/tailwind/infotable";` so Tailwind scans these files. */
3
+ @source "../../../views/infotable";
4
+ @source "../../../../lib/infotable/formatters";
5
+ @source "../../javascripts/infotable";
@@ -96,25 +96,6 @@ module Infotable
96
96
  template "initializer.rb", "config/initializers/infotable.rb"
97
97
  end
98
98
 
99
- def add_to_gitignore
100
- gitignore_entry = "# InfoTable - Generated from ERB template, do not commit\napp/assets/stylesheets/infotable.css\napp/assets/tailwind/infotable.css\n"
101
-
102
- if File.exist?(".gitignore")
103
- content = File.read(".gitignore")
104
- unless content.include?("infotable.css")
105
- append_to_file ".gitignore", "\n#{gitignore_entry}"
106
- say " ✅ Added generated infotable.css to .gitignore", :green
107
- else
108
- say " ℹ️ infotable.css already in .gitignore", :blue
109
- end
110
- else
111
- create_file ".gitignore", gitignore_entry
112
- say " ✅ Created .gitignore with infotable.css", :green
113
- end
114
-
115
- say " 📝 Commit infotable.css.erb (the template), not infotable.css (generated)", :cyan
116
- end
117
-
118
99
  def show_post_install_message
119
100
  say "\n" + "=" * 60, :green
120
101
  say "✅ InfoTable installation complete!", :green
@@ -144,9 +125,7 @@ module Infotable
144
125
  say "💡 Important Notes:", :cyan
145
126
  say "=" * 60, :cyan
146
127
  say "• InfoTable requires the tailwindcss-rails gem", :green
147
- say "• infotable.css.erb is processed by Rake task before Tailwind builds", :green
148
- say "• Gem paths resolve dynamically for each environment (dev/production)", :green
149
- say "• Commit infotable.css.erb (template), gitignore infotable.css (generated)", :cyan
128
+ say "• Tailwind scans the gem through @import \"../builds/tailwind/infotable\" in your Tailwind entry file", :green
150
129
 
151
130
  say "\n📖 Documentation: https://github.com/jcieslar/infotable", :cyan
152
131
  end
@@ -496,61 +475,28 @@ import InfoTableDrawerController from "infotable/controllers/drawer_controller"
496
475
  return
497
476
  end
498
477
 
499
- # Create separate infotable.css file with resolved gem paths
500
- create_infotable_css_file
501
-
502
- # Tailwind only scans the gem's views if the main CSS imports the generated file
478
+ # tailwindcss-rails writes app/assets/builds/tailwind/infotable.css (it imports the gem's
479
+ # app/assets/tailwind/infotable/engine.css) on every build; Tailwind only scans the gem's
480
+ # views if the main CSS imports it.
481
+ import = %(@import "../builds/tailwind/infotable";)
503
482
  content = File.read(tailwind_file)
504
- if content.match?(/@import\s+["']\.?\/?infotable["']/)
483
+ if content.include?(import)
505
484
  say " ℹ️ InfoTable import already exists in #{File.basename(tailwind_file)}", :blue
506
485
  else
507
- if content.include?("@import \"tailwindcss\"")
486
+ if content.match?(/@import\s+["']tailwindcss["'];?\s*\n/)
508
487
  inject_into_file tailwind_file, after: /@import\s+["']tailwindcss["'];?\s*\n/ do
509
- "\n/* InfoTable - Requires tailwindcss-rails gem */\n@import './infotable';\n"
488
+ "#{import}\n"
510
489
  end
511
490
  else
512
- prepend_to_file tailwind_file, "/* InfoTable - Requires tailwindcss-rails gem */\n@import './infotable';\n\n"
491
+ prepend_to_file tailwind_file, "#{import}\n"
513
492
  end
514
493
 
515
- say " ✅ Added @import './infotable' to #{File.basename(tailwind_file)}", :green
494
+ say " ✅ Added #{import} to #{File.basename(tailwind_file)}", :green
516
495
  end
517
496
 
518
497
  say " 📝 Run: rails tailwindcss:build", :cyan
519
498
  end
520
499
 
521
- def create_infotable_css_file
522
- # Determine the stylesheets directory
523
- stylesheets_dir = if File.exist?("app/assets/stylesheets")
524
- "app/assets/stylesheets"
525
- elsif File.exist?("app/assets/tailwind")
526
- "app/assets/tailwind"
527
- else
528
- "app/assets/stylesheets"
529
- end
530
-
531
- infotable_css_path = File.join(stylesheets_dir, "infotable.css.erb")
532
-
533
- # Create ERB file that dynamically resolves gem paths
534
- css_content = <<~CSS
535
- /* InfoTable Tailwind Sources - ERB Template */
536
- /* Requires: tailwindcss-rails gem (https://github.com/rails/tailwindcss-rails) */
537
- /* Paths are resolved dynamically via ERB from Infotable module */
538
-
539
- /* Local table definitions in your Rails app */
540
- @source "../../app/infotable";
541
-
542
- /* InfoTable gem paths (resolved via ERB at build time) */
543
- @source "<%= Infotable.views_path %>";
544
- @source "<%= Infotable.formatters_path %>";
545
- CSS
546
-
547
- create_file infotable_css_path, css_content
548
-
549
- say " ✅ Created #{infotable_css_path} with ERB templates", :green
550
- say " 📝 Gem paths resolve dynamically via Infotable module", :cyan
551
- say " 📝 Works in all environments automatically", :green
552
- end
553
-
554
500
  def package_json_has?(package)
555
501
  return false unless File.exist?("package.json")
556
502
 
@@ -39,13 +39,5 @@ module Infotable
39
39
  initializer "infotable.i18n" do |app|
40
40
  config.i18n.load_path += Dir[root.join("config/locales/**/*.yml")]
41
41
  end
42
-
43
- # Expose gem paths via ENV variables for Tailwind CSS v4 @source directives
44
- # This allows Tailwind to scan the gem's views/formatters at build time
45
- # Works in development, production, Docker, CI - everywhere!
46
- initializer "infotable.tailwind", before: :load_config_initializers do
47
- ENV["INFOTABLE_VIEWS_PATH"] = root.join("app/views/infotable").to_s
48
- ENV["INFOTABLE_FORMATTERS_PATH"] = root.join("lib/infotable/formatters").to_s
49
- end
50
42
  end
51
43
  end
@@ -119,7 +119,7 @@ module Infotable
119
119
 
120
120
  case op
121
121
  when "contains"
122
- result = result.public_send(filter_method, "#{column_ref} ILIKE ?", "%#{sanitize_like(value)}%") if value.present?
122
+ result = result.public_send(filter_method, like(column_ref), "%#{sanitize_like(value)}%") if value.present?
123
123
  when "exact"
124
124
  if is_virtual
125
125
  result = result.having("LOWER(#{column_ref}) = LOWER(?)", value) if value.present?
@@ -127,11 +127,11 @@ module Infotable
127
127
  result = result.where("LOWER(#{column_ref}) = LOWER(?)", value) if value.present?
128
128
  end
129
129
  when "not_contain"
130
- result = result.public_send(filter_method, "NOT #{column_ref} ILIKE ?", "%#{sanitize_like(value)}%") if value.present?
130
+ result = result.public_send(filter_method, like(column_ref, negate: true), "%#{sanitize_like(value)}%") if value.present?
131
131
  when "starts_with"
132
- result = result.public_send(filter_method, "#{column_ref} ILIKE ?", "#{sanitize_like(value)}%") if value.present?
132
+ result = result.public_send(filter_method, like(column_ref), "#{sanitize_like(value)}%") if value.present?
133
133
  when "ends_with"
134
- result = result.public_send(filter_method, "#{column_ref} ILIKE ?", "%#{sanitize_like(value)}") if value.present?
134
+ result = result.public_send(filter_method, like(column_ref), "%#{sanitize_like(value)}") if value.present?
135
135
  when "is_empty"
136
136
  result = result.public_send(filter_method, "#{column_ref} IS NULL OR #{column_ref} = ''")
137
137
  when "is_not_empty"
@@ -299,6 +299,10 @@ module Infotable
299
299
  DECLARED_TYPES[column[:type]]
300
300
  end
301
301
 
302
+ def like(column_ref, negate: false)
303
+ Infotable::Sql.like(query.model.connection, column_ref, negate: negate)
304
+ end
305
+
302
306
  # Helper to sanitize LIKE values
303
307
  def sanitize_like(value)
304
308
  return "" if value.blank?
@@ -1,5 +1,5 @@
1
1
  module Infotable
2
- # Global search: every whitespace-separated term must match (ILIKE) at least one
2
+ # Global search: every whitespace-separated term must match (case-insensitively) at least one
3
3
  # searchable column. Non-text columns are matched on their text representation.
4
4
  class SearchProcessor
5
5
  MAX_TERMS = 5
@@ -19,8 +19,8 @@ module Infotable
19
19
 
20
20
  terms.reduce(query) do |relation, word|
21
21
  pattern = "%#{ActiveRecord::Base.sanitize_sql_like(word)}%"
22
- conditions = columns.map { "CAST(#{column_ref(_1)} AS text) ILIKE :pattern" }
23
- relation.where(conditions.join(" OR "), pattern: pattern)
22
+ conditions = columns.map { Infotable::Sql.like(query.model.connection, "CAST(#{column_ref(_1)} AS text)") }
23
+ relation.where(conditions.join(" OR "), *[ pattern ] * columns.size)
24
24
  end
25
25
  end
26
26
 
@@ -0,0 +1,22 @@
1
+ module Infotable
2
+ # The few SQL fragments that differ between databases (PostgreSQL, SQLite).
3
+ module Sql
4
+ module_function
5
+
6
+ def postgresql?(connection)
7
+ connection.adapter_name.match?(/postg/i)
8
+ end
9
+
10
+ def sqlite?(connection)
11
+ connection.adapter_name.match?(/sqlite/i)
12
+ end
13
+
14
+ # Case-insensitive LIKE with one bind placeholder. SQLite's LIKE ignores case for ASCII only;
15
+ # PostgreSQL gets a real ILIKE.
16
+ def like(connection, ref, negate: false)
17
+ operator = postgresql?(connection) ? "ILIKE ?" : "LIKE ?"
18
+ operator += " ESCAPE '\\'" if sqlite?(connection) # SQLite has no default escape character
19
+ "#{'NOT ' if negate}#{ref} #{operator}"
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Infotable
2
- VERSION = "0.1.0"
2
+ VERSION = "1.0.1"
3
3
  end
data/lib/infotable.rb CHANGED
@@ -10,6 +10,7 @@ module Infotable
10
10
  autoload :FilterProcessor, "infotable/filter_processor"
11
11
  autoload :BatchActions, "infotable/batch_actions"
12
12
  autoload :SearchProcessor, "infotable/search_processor"
13
+ autoload :Sql, "infotable/sql"
13
14
  autoload :TableResolver, "infotable/table_resolver"
14
15
 
15
16
  # Formatters
@@ -30,18 +31,6 @@ module Infotable
30
31
  Engine.root
31
32
  end
32
33
 
33
- # Returns the path to InfoTable views directory
34
- # For use in Tailwind CSS @source directives via ERB
35
- def self.views_path
36
- root.join("app/views/infotable").to_s
37
- end
38
-
39
- # Returns the path to InfoTable formatters directory
40
- # For use in Tailwind CSS @source directives via ERB
41
- def self.formatters_path
42
- root.join("lib/infotable/formatters").to_s
43
- end
44
-
45
34
  # Returns paths that should be scanned by Tailwind CSS
46
35
  # For use with Node-based Tailwind (tailwind.config.js)
47
36
  def self.tailwind_content_paths
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: infotable
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jakub Cieślar
@@ -137,6 +137,7 @@ files:
137
137
  - app/assets/javascripts/infotable/controllers/drawer_controller.js
138
138
  - app/assets/javascripts/infotable/controllers/table_controller.js
139
139
  - app/assets/javascripts/infotable/index.js
140
+ - app/assets/tailwind/infotable/engine.css
140
141
  - app/controllers/infotable/application_controller.rb
141
142
  - app/controllers/infotable/tables_controller.rb
142
143
  - app/helpers/infotable/application_helper.rb
@@ -163,7 +164,6 @@ files:
163
164
  - config/locales/pl.yml
164
165
  - config/routes.rb
165
166
  - lib/generators/infotable/install/install_generator.rb
166
- - lib/generators/infotable/install/templates/TAILWIND_CONFIG.md
167
167
  - lib/generators/infotable/install/templates/initializer.rb
168
168
  - lib/generators/infotable/install/templates/tailwind.config.js
169
169
  - lib/generators/infotable/table/table_generator.rb
@@ -184,9 +184,9 @@ files:
184
184
  - lib/infotable/formatters/link_formatter.rb
185
185
  - lib/infotable/formatters/percent_formatter.rb
186
186
  - lib/infotable/search_processor.rb
187
+ - lib/infotable/sql.rb
187
188
  - lib/infotable/table_resolver.rb
188
189
  - lib/infotable/version.rb
189
- - lib/tasks/infotable.rake
190
190
  homepage: https://infotable.infovation.eu/
191
191
  licenses:
192
192
  - MIT
@@ -1,131 +0,0 @@
1
- # Tailwind Configuration for InfoTable
2
-
3
- InfoTable supports both Tailwind CSS v4 (recommended) and v3.
4
-
5
- ## Tailwind v4 (CSS-based Config) - RECOMMENDED
6
-
7
- Tailwind v4 uses CSS-based configuration with `@source` directives. The installer creates a separate CSS file for InfoTable.
8
-
9
- ### What the Installer Does
10
-
11
- When you run `rails generate infotable:install`, it:
12
-
13
- 1. **Creates `app/assets/stylesheets/infotable.css`** with ENV variable references:
14
- ```css
15
- /* InfoTable Tailwind Sources */
16
-
17
- /* Local table definitions in your Rails app */
18
- @source "../../app/infotable";
19
-
20
- /* InfoTable gem paths (resolved dynamically via ENV variables) */
21
- @source "$INFOTABLE_VIEWS_PATH";
22
- @source "$INFOTABLE_FORMATTERS_PATH";
23
- ```
24
-
25
- 2. **Adds import to `application.tailwind.css`**:
26
- ```css
27
- @import "tailwindcss";
28
-
29
- /* InfoTable gem styles */
30
- @import './infotable';
31
- ```
32
-
33
- ### Why This Approach?
34
-
35
- - **Always correct**: Rails engine knows the exact gem location
36
- - **Environment-agnostic**: Works in dev, staging, production, Docker, CI
37
- - **Safe to commit**: No environment-specific paths
38
- - **Zero maintenance**: Install once, works everywhere
39
- - **Simple**: No glob patterns or complex path logic
40
-
41
- ### ENV Variable Strategy
42
-
43
- The InfoTable engine sets ENV variables at Rails boot time:
44
- ```ruby
45
- # lib/infotable/engine.rb
46
- initializer "infotable.tailwind" do
47
- ENV["INFOTABLE_VIEWS_PATH"] = root.join("app/views/infotable").to_s
48
- ENV["INFOTABLE_FORMATTERS_PATH"] = root.join("lib/infotable/formatters").to_s
49
- end
50
- ```
51
-
52
- Tailwind v4 expands these ENV variables when building:
53
- ```css
54
- @source "$INFOTABLE_VIEWS_PATH";
55
- ```
56
- → Expands to the actual gem path at build time
57
-
58
- ### Works Everywhere Automatically
59
-
60
- The same CSS file works in all environments:
61
- - **Development**: ENV variables point to your local gem location
62
- - **Production**: ENV variables point to production gem location
63
- - **CI/CD**: ENV variables point to CI gem location
64
- - **Docker**: ENV variables point to container gem location
65
-
66
- Rails `Infotable::Engine.root` always returns the correct path!
67
-
68
- You can safely commit `infotable.css` to version control!
69
-
70
- ## Tailwind v3 (JS-based Config) - FALLBACK
71
-
72
- If using Tailwind v3, the installer adds dynamic path resolution to your `tailwind.config.js`:
73
-
74
- ```javascript
75
- const { execSync } = require('child_process');
76
- const path = require('path');
77
-
78
- // Helper to resolve gem path dynamically
79
- function resolveGemPath(gemName) {
80
- try {
81
- const bundlerCmd = `bundle show ${gemName}`;
82
- const gemPath = execSync(bundlerCmd, { encoding: 'utf-8' }).trim();
83
- return gemPath;
84
- } catch (error) {
85
- console.warn(`Warning: Could not resolve ${gemName} gem path:`, error.message);
86
- return null;
87
- }
88
- }
89
-
90
- const infotableGemPath = resolveGemPath('infotable');
91
-
92
- module.exports = {
93
- content: [
94
- './app/infotable/**/*.rb',
95
- ...(infotableGemPath ? [
96
- path.join(infotableGemPath, 'app/views/infotable/**/*.{erb,rb}'),
97
- path.join(infotableGemPath, 'app/assets/javascripts/infotable/**/*.js'),
98
- path.join(infotableGemPath, 'lib/infotable/formatters/**/*.rb'),
99
- path.join(infotableGemPath, 'app/components/**/*.{erb,rb}'),
100
- ] : []),
101
- ],
102
- // ... rest of config
103
- }
104
- ```
105
-
106
- This approach dynamically resolves the gem path at **build time** (every time Tailwind builds).
107
-
108
- ## Troubleshooting
109
-
110
- ### Styles Not Appearing
111
-
112
- 1. Check your Tailwind CSS file includes InfoTable @source directives
113
- 2. Verify gem path is correct: `bundle show infotable`
114
- 3. Update paths if needed: `rake infotable:update_tailwind_paths`
115
- 4. Rebuild: `rails tailwindcss:build`
116
-
117
- ### After Changing Gem Installation Method
118
-
119
- If you switch from `path:` to `git:` or vice versa:
120
-
121
- ```bash
122
- rake infotable:update_tailwind_paths
123
- rails tailwindcss:build
124
- ```
125
-
126
- ## Why Different Approaches?
127
-
128
- - **Tailwind v4**: @source doesn't support variables, so paths must be static and resolved at install time
129
- - **Tailwind v3**: JavaScript config supports dynamic resolution via `execSync`, so paths are resolved at build time
130
-
131
- Both approaches work reliably, but v4 is recommended for better Rails integration and performance.
@@ -1,55 +0,0 @@
1
- namespace :infotable do
2
- desc "Generate infotable.css from ERB template with resolved gem paths"
3
- task generate_tailwind_sources: :environment do
4
- # Find the ERB template
5
- erb_path = if File.exist?("app/assets/stylesheets/infotable.css.erb")
6
- "app/assets/stylesheets/infotable.css.erb"
7
- elsif File.exist?("app/assets/tailwind/infotable.css.erb")
8
- "app/assets/tailwind/infotable.css.erb"
9
- else
10
- puts "⚠️ infotable.css.erb not found. Run 'rails generate infotable:install' first."
11
- next
12
- end
13
-
14
- output_path = erb_path.sub(".erb", "")
15
-
16
- # Read and process the ERB template
17
- require "erb"
18
- template = File.read(erb_path)
19
- result = ERB.new(template).result(binding)
20
-
21
- # Write the generated CSS
22
- File.write(output_path, result)
23
-
24
- puts "✅ Generated #{output_path} from ERB template"
25
- puts " Views: #{Infotable.views_path}"
26
- puts " Formatters: #{Infotable.formatters_path}"
27
- end
28
-
29
- # Alias for backwards compatibility
30
- task update_tailwind_paths: :generate_tailwind_sources
31
-
32
- desc "Display InfoTable gem paths for Tailwind CSS (for debugging)"
33
- task show_tailwind_paths: :environment do
34
- puts "\n" + "=" * 60
35
- puts "InfoTable Gem Paths"
36
- puts "=" * 60
37
- puts "Gem root: #{Infotable.root}"
38
- puts "Views path: #{Infotable.views_path}"
39
- puts "Formatters path: #{Infotable.formatters_path}"
40
- puts "=" * 60 + "\n"
41
- end
42
- end
43
-
44
- # Hook into Tailwind CSS build tasks
45
- if Rake::Task.task_defined?("tailwindcss:build")
46
- Rake::Task["tailwindcss:build"].enhance([ "infotable:generate_tailwind_sources" ])
47
- end
48
-
49
- if Rake::Task.task_defined?("tailwindcss:watch")
50
- Rake::Task["tailwindcss:watch"].enhance([ "infotable:generate_tailwind_sources" ])
51
- end
52
-
53
- if Rake::Task.task_defined?("assets:precompile")
54
- Rake::Task["assets:precompile"].enhance([ "infotable:generate_tailwind_sources" ])
55
- end