rooq 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.
data/README.md ADDED
@@ -0,0 +1,98 @@
1
+ # rOOQ
2
+
3
+ A Ruby query builder inspired by [jOOQ](https://www.jooq.org/). Build type-safe SQL queries using a fluent, chainable API.
4
+
5
+ ## Features
6
+
7
+ - **Fluent Query Builder**: Chainable methods for building SELECT, INSERT, UPDATE, and DELETE queries
8
+ - **Immutable Queries**: Each builder method returns a new query object
9
+ - **Schema Validation**: Generate Ruby code from database schemas with runtime validation
10
+ - **PostgreSQL Support**: Full PostgreSQL dialect with parameterized queries
11
+ - **Advanced SQL Features**:
12
+ - DISTINCT, GROUP BY, HAVING
13
+ - Window functions (ROW_NUMBER, RANK, LAG, LEAD, etc.)
14
+ - Common Table Expressions (CTEs)
15
+ - Set operations (UNION, INTERSECT, EXCEPT)
16
+ - CASE WHEN expressions
17
+ - Aggregate functions (COUNT, SUM, AVG, MIN, MAX)
18
+ - Grouping sets (CUBE, ROLLUP, GROUPING SETS)
19
+ - **CLI Tool**: Generate schema files from the command line
20
+ - **Optional Sorbet Types**: Full type annotations with optional generation
21
+
22
+ ## Installation
23
+
24
+ Add this line to your application's Gemfile:
25
+
26
+ ```ruby
27
+ gem 'rooq'
28
+ ```
29
+
30
+ And then execute:
31
+
32
+ ```bash
33
+ bundle install
34
+ ```
35
+
36
+ ## Quick Start
37
+
38
+ ```ruby
39
+ require 'rooq'
40
+
41
+ # Define a table
42
+ books = Rooq::Table.new(:books) do |t|
43
+ t.field :id, :integer
44
+ t.field :title, :string
45
+ t.field :author_id, :integer
46
+ t.field :published_in, :integer
47
+ end
48
+
49
+ # Build a query
50
+ query = Rooq::DSL.select(books.TITLE, books.PUBLISHED_IN)
51
+ .from(books)
52
+ .where(books.PUBLISHED_IN.gte(2010))
53
+ .order_by(books.TITLE.asc)
54
+ .limit(10)
55
+
56
+ result = query.to_sql
57
+ # result.sql => "SELECT books.title, books.published_in FROM books WHERE books.published_in >= $1 ORDER BY books.title ASC LIMIT 10"
58
+ # result.params => [2010]
59
+ ```
60
+
61
+ ## CLI Usage
62
+
63
+ Generate Ruby table definitions from your PostgreSQL database:
64
+
65
+ ```bash
66
+ # Generate schema to lib/schema.rb (default)
67
+ rooq generate -d myapp_development
68
+
69
+ # Generate with custom namespace (writes to lib/my_app/db.rb)
70
+ rooq generate -d myapp_development -n MyApp::DB
71
+
72
+ # Generate without Sorbet types
73
+ rooq generate -d myapp_development --no-typed
74
+
75
+ # Print to stdout instead of file
76
+ rooq generate -d myapp_development --stdout
77
+
78
+ # See all options
79
+ rooq help
80
+ ```
81
+
82
+ ## Documentation
83
+
84
+ See [USAGE.md](USAGE.md) for detailed usage examples.
85
+
86
+ ## Development
87
+
88
+ ```bash
89
+ # Install dependencies
90
+ bundle install
91
+
92
+ # Run tests
93
+ bundle exec rake test
94
+ ```
95
+
96
+ ## License
97
+
98
+ The gem is available as open source under the terms of the MIT License.
data/Rakefile ADDED
@@ -0,0 +1,130 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rake/testtask"
4
+ require "bundler/gem_tasks"
5
+ require "fileutils"
6
+ require "find"
7
+
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << "lib" << "test"
10
+ t.test_files = FileList["test/**/*_test.rb"]
11
+ t.verbose = true
12
+ end
13
+
14
+ namespace :docs do
15
+ desc "Fix YARD documentation for subpath hosting"
16
+ task :fix do
17
+ require_relative "lib/rooq/version"
18
+ version = Rooq::VERSION
19
+ base_url = "https://ggalmazor.com/rooq/"
20
+ doc_dir = File.join(__dir__, "doc")
21
+
22
+ unless Dir.exist?(doc_dir)
23
+ puts "Error: #{doc_dir} not found. Run 'yard doc' first."
24
+ exit 1
25
+ end
26
+
27
+ # 1. Fix app.js (the AJAX navigation issue)
28
+ app_js_path = File.join(doc_dir, "js", "app.js")
29
+ if File.exist?(app_js_path)
30
+ content = File.read(app_js_path)
31
+ search_pattern = /if \(e\.data\.action === "navigate"\) \{/
32
+ if content.match?(search_pattern)
33
+ puts "Applying fix to #{app_js_path}..."
34
+
35
+ replacement = <<~'JS'.chomp
36
+ if (e.data.action === "navigate") {
37
+ /*
38
+ When hosted at a subpath (e.g., https://ggalmazor.com/rooq/),
39
+ we need to ensure relative URLs from the iframe are resolved
40
+ against the documentation's root path, not the current browser URL
41
+ which might have changed due to pushState.
42
+ */
43
+ const indexPage = window.location.pathname.indexOf('/index.html');
44
+ const rootPath = indexPage !== -1
45
+ ? window.location.pathname.substring(0, indexPage + 1)
46
+ : window.location.pathname.replace(/\/[^\/]*$/, "/");
47
+
48
+ const baseUrl = window.location.origin + rootPath;
49
+ const url = new URL(e.data.url, baseUrl);
50
+ JS
51
+
52
+ content.gsub!(search_pattern, replacement)
53
+ content.gsub!("fetch(e.data.url)", "fetch(url.href)")
54
+ content.gsub!('history.pushState({}, document.title, e.data.url)', "history.pushState({}, document.title, url.href)")
55
+ redundant_url_parser = /const url = new URL\(e\.data\.url, "https:\/\/localhost"\);/
56
+ content.gsub!(redundant_url_parser, "")
57
+
58
+ File.write(app_js_path, content)
59
+ puts "Fix applied to app.js."
60
+ end
61
+ end
62
+
63
+ # 2. Convert all relative links to absolute links in HTML files and inject version
64
+ puts "Converting relative links to absolute and injecting version #{version} in #{doc_dir}..."
65
+
66
+ extra_files_mapping = {
67
+ "USAGE_md.html" => "file.USAGE.html",
68
+ "CHANGELOG_md.html" => "file.CHANGELOG.html"
69
+ }
70
+
71
+ Find.find(doc_dir) do |path|
72
+ next unless path.end_with?(".html")
73
+
74
+ content = File.read(path)
75
+
76
+ # Inject version into title if it's the standard "rOOQ Documentation"
77
+ content.gsub!("rOOQ Documentation", "rOOQ v#{version} Documentation")
78
+
79
+ # Inject version into the #menu element for better visibility
80
+ content.gsub!(/<div id="menu">/, "<div id=\"menu\">\n <span class=\"version\">v#{version}</span>")
81
+
82
+ # YARD uses relative paths like "", "Rooq", "Rooq/SomeSubClass"
83
+ # We want to find href="../something.html" and replace it with base_url + something.html
84
+
85
+ new_content = content.gsub(/(href|src)=["']([^"']+)["']/) do |match|
86
+ attr = ::Regexp.last_match(1)
87
+ url = ::Regexp.last_match(2)
88
+
89
+ # If it's an absolute URL starting with our base_url, we might need to fix it
90
+ if url.start_with?(base_url)
91
+ relative_part = url.sub(base_url, "")
92
+ if extra_files_mapping.key?(relative_part)
93
+ "#{attr}=\"#{base_url}#{extra_files_mapping[relative_part]}\""
94
+ else
95
+ match
96
+ end
97
+ # Skip other absolute URLs, anchors, and data URIs
98
+ elsif url.start_with?("http://", "https://", "#", "data:") || url.empty?
99
+ match
100
+ else
101
+ # Fix links to extra files that YARD renames
102
+ target_url = extra_files_mapping[url] || url
103
+
104
+ # If it's one of our extra files, it's at the root of doc/
105
+ absolute_file_path = if extra_files_mapping.key?(url)
106
+ File.join(File.expand_path(doc_dir), target_url)
107
+ else
108
+ File.expand_path(target_url, File.dirname(path))
109
+ end
110
+
111
+ if absolute_file_path.start_with?(File.expand_path(doc_dir))
112
+ relative_to_doc_root = absolute_file_path.sub("#{File.expand_path(doc_dir)}/", "")
113
+ # Handle index.html at root
114
+ relative_to_doc_root = "" if relative_to_doc_root == File.expand_path(doc_dir)
115
+
116
+ "#{attr}=\"#{base_url}#{relative_to_doc_root}\""
117
+ else
118
+ match
119
+ end
120
+ end
121
+ end
122
+
123
+ File.write(path, new_content) if new_content != content
124
+ end
125
+
126
+ puts "Finished converting links."
127
+ end
128
+ end
129
+
130
+ task default: :test