Skip to content

Commit 6bd5c89

Browse files
authored
Merge pull request #146 from rsim/improvement/agents_instructions
Add agents instructions, improve code style
2 parents 22dd64e + 6df80d3 commit 6bd5c89

24 files changed

Lines changed: 602 additions & 517 deletions

.github/copilot-instructions.md

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1 @@
1-
## Project Overview
2-
3-
mondrian-olap is a JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library.
4-
5-
## Technology Stack
6-
7-
- JRuby 9.4 or later (compatible with Ruby 3.1+)
8-
- Java 8 or later
9-
- Mondrian OLAP Java library from a fork https://github.com/rsim/mondrian/tree/9.3.0.0-rsim
10-
- Databases: PostgreSQL, MySQL, Oracle, Microsoft SQL Server, ClickHouse or other JDBC compatible databases
11-
- Testing: RSpec
1+
Use AGENTS.md for code style and guidelines.

.gitignore

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@
33
.DS_Store
44
.autotest
55
.ruby-*
6-
/mise.local.toml
6+
mise.local.toml
7+
CLAUDE.local.md
8+
.claude
79
coverage
810
doc
911
pkg

AGENTS.md

Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
# mondrian-olap
2+
3+
This file provides guidance to AI coding agents working with this repository.
4+
5+
## Overview
6+
7+
mondrian-olap is a JRuby gem for performing multidimensional queries of relational database data using Mondrian OLAP Java library.
8+
9+
## Codebase Structure
10+
11+
### Main Entry Points
12+
13+
- `lib/mondrian-olap.rb` - Main gem entry point that requires all components
14+
- `lib/mondrian/olap.rb` - Core module and Java library initialization
15+
- `lib/mondrian/olap/connection.rb` - Database connection management
16+
- `lib/mondrian/olap/schema.rb` - OLAP schema definition DSL
17+
- `lib/mondrian/olap/cube.rb` - Cube operations and queries
18+
- `lib/mondrian/olap/query.rb` - Query builder for MDX-like queries
19+
- `lib/mondrian/olap/result.rb` - Query result processing
20+
21+
### Key Modules
22+
23+
- `Mondrian::OLAP::Connection` - Manages connections to OLAP data sources
24+
- `Mondrian::OLAP::Schema` - Provides DSL for defining OLAP schemas programmatically
25+
- `Mondrian::OLAP::Cube` - Represents OLAP cubes and provides query interface
26+
- `Mondrian::OLAP::Query` - Builds and executes MDX-like queries
27+
- `Mondrian::OLAP::Result` - Handles query results with axes and cells
28+
29+
### Java Integration
30+
31+
- `lib/mondrian/jars/` - Contains Mondrian OLAP Java library JAR files
32+
- The gem bridges Ruby code with Java Mondrian library using JRuby's Java integration
33+
- Java objects are wrapped in Ruby classes to provide idiomatic Ruby API
34+
35+
## Common Workflows
36+
37+
### Making Changes
38+
39+
1. **Adding new schema features** - Modify `lib/mondrian/olap/schema.rb` and add corresponding specs in
40+
`spec/schema_definition_spec.rb`
41+
2. **Extending query capabilities** - Update `lib/mondrian/olap/query.rb` and test in `spec/query_spec.rb`
42+
3. **Connection enhancements** - Change `lib/mondrian/olap/connection.rb` with tests in `spec/connection_spec.rb`
43+
4. **Cube operations** - Modify `lib/mondrian/olap/cube.rb` and add specs in `spec/cube_spec.rb`
44+
45+
### Testing Changes
46+
47+
1. Write or update RSpec tests for the changed functionality.
48+
2. Run specific test file: `rspec spec/cube_spec.rb`.
49+
3. Run all tests with default database: `rake spec`.
50+
4. Test with specific databases: `rake spec:postgresql`, `rake spec:mysql`, etc.
51+
5. Ensure tests pass with multiple database backends before finalizing changes.
52+
53+
## Technology Stack
54+
55+
- **JRuby** 9.4 or later (compatible with Ruby 3.1+)
56+
- **Java** 8 or later LTS version
57+
- **Mondrian OLAP** Java library from a fork https://github.com/rsim/mondrian/tree/9.3.0.0-rsim
58+
- **Databases**: PostgreSQL, MySQL, Oracle, Microsoft SQL Server, ClickHouse or other JDBC compatible databases
59+
- **Testing**: RSpec
60+
61+
### JRuby-Specific Considerations
62+
63+
- This gem requires JRuby and will not work with standard MRI Ruby.
64+
- Use JRuby's Java integration features to interact with Mondrian Java library.
65+
- Java objects can be accessed directly in JRuby code.
66+
- JDBC drivers are used for database connections instead of native Ruby database adapters.
67+
68+
## Code Style and Guidelines
69+
70+
### General
71+
72+
- Use meaningful semantic names for variables, methods, and classes.
73+
- Use query and command method conventions.
74+
- Query methods use nouns and do not modify state and do not have side effects. Boolean methods end with `?`.
75+
- Command methods use verbs that describe the action being taken and may modify state.
76+
- Use consistent naming, use the same terminology throughout the codebase.
77+
- Do not use similar variable or method names for different data or objects.
78+
- Write comments to explain why something is done, not what is done.
79+
- Write comments only when it is not obvious from the code.
80+
- Start full sentence comments with a capital letter.
81+
- Prefer self-explanatory code with semantic names over detailed comments.
82+
- Keep methods small and focused on a single task.
83+
- Write simple readable code. Do not obfuscate simple logic.
84+
- Validate correct spelling for variable, method, class names, as well as for comments.
85+
86+
### Ruby
87+
88+
- Do not modify objects (like Hash and Array) that are referenced by argument variables.
89+
This might cause unexpected side effects in the caller. It is OK to assign a new object to an argument variable.
90+
- Return collections (arrays or other) from methods with plural names. Do not return nil from methods with plural names,
91+
return empty collections in such cases.
92+
- Use &:method for collections:
93+
`collection.map(&:method)` instead of `collection.map { |item| item.method }`.
94+
- Use safe navigation operator `&.` when calling methods on objects that might be nil.
95+
- When continuing the method call on the next line, then end the first line with a dot.
96+
- Use the new hash syntax `key: 'value'` instead of the old syntax `:key => 'value'`.
97+
- Use the old hash syntax only for rake task dependencies, for example, `task :build => :compile`.
98+
- Use simple parentheses declaring an array of strings `%w()` instead of other symbols like `%w[]`.
99+
- Use frozen string literal comments for all Ruby files and ensure that frozen strings are not modified.
100+
101+
### Testing
102+
103+
- Use RSpec for Ruby testing.
104+
- Run individual RSpec test file with e.g. `rspec spec/cube_spec.rb`.
105+
- Run all RSpec tests with `rake spec` (with the default `mysql` database).
106+
- Run all tests with a specified database:
107+
`rake spec:mysql`, `rake spec:postgresql`, `rake spec:sqlserver`, `rake spec:oracle`
108+
- In most cases use RSpec should syntax and not expect syntax, for example, `result.should == expected`.
109+
- Use RSpec expect syntax only for block expectations, for example, `expect { action }.to raise_error(SomeError)`.
110+
- Test data is located in `spec/support/data/` directory.
111+
- Database-specific schema fixtures are in `spec/fixtures/` directory.

CLAUDE.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
@AGENTS.md

README.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
[![GitHub Actions status](https://github.com/rsim/mondrian-olap/actions/workflows/tests.yml/badge.svg)](https://github.com/rsim/mondrian-olap/actions/workflows/tests.yml)
2-
[![AppVeyor status](https://ci.appveyor.com/api/projects/status/08xd4tyty2k3wxba/branch/master?svg=true)](https://ci.appveyor.com/project/rsim/mondrian-olap)
32

43
mondrian-olap
54
=============

appveyor.yml

Lines changed: 0 additions & 42 deletions
This file was deleted.

lib/mondrian/olap.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require 'java'
24
require 'nokogiri'
35

lib/mondrian/olap/connection.rb

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
module Mondrian
24
module OLAP
35
class Connection
@@ -19,7 +21,7 @@ def initialize(params = {})
1921

2022
def connect
2123
Error.wrap_native_exception do
22-
# hack to call private constructor of MondrianOlap4jDriver
24+
# Hack to call private constructor of MondrianOlap4jDriver
2325
# to avoid using DriverManager which fails to load JDBC drivers
2426
# because of not seeing JRuby required jar files
2527
cons = Java::MondrianOlap4j::MondrianOlap4jDriver.java_class.declared_constructor
@@ -30,7 +32,7 @@ def connect
3032
props.setProperty('JdbcUser', @params[:username]) if @params[:username]
3133
props.setProperty('JdbcPassword', @params[:password]) if @params[:password]
3234

33-
# on Oracle increase default row prefetch size
35+
# On Oracle increase default row prefetch size
3436
# as default 10 is very low and slows down loading of all dimension members
3537
if @driver == 'oracle'
3638
prefetch_rows = @params[:prefetch_rows] || 100
@@ -39,7 +41,7 @@ def connect
3941

4042
conn_string = connection_string
4143

42-
# latest Mondrian version added ClassResolver which uses current thread class loader to load some classes
44+
# Latest Mondrian version added ClassResolver which uses current thread class loader to load some classes
4345
# therefore need to set it to JRuby class loader to ensure that Mondrian classes are found
4446
# (e.g. when running mondrian-olap inside OSGi container)
4547
current_thread = Java::JavaLang::Thread.currentThread
@@ -53,7 +55,7 @@ def connect
5355

5456
@raw_connection = @raw_jdbc_connection.unwrap(Java::OrgOlap4j::OlapConnection.java_class)
5557
@raw_catalog = @raw_connection.getOlapCatalog
56-
# currently it is assumed that there is just one schema per connection catalog
58+
# Currently it is assumed that there is just one schema per connection catalog
5759
@raw_schema = @raw_catalog.getSchemas.first
5860
@raw_mondrian_connection = @raw_connection.getMondrianConnection
5961
@raw_schema_reader = @raw_mondrian_connection.getSchemaReader
@@ -89,7 +91,7 @@ def execute(query_string, parameters = {})
8991
end
9092
end
9193

92-
# access mondrian.olap.Parameter object
94+
# Access mondrian.olap.Parameter object
9395
def mondrian_parameter(parameter_name)
9496
Error.wrap_native_exception do
9597
@raw_schema_reader.getParameter(parameter_name)
@@ -144,7 +146,7 @@ def self.raw_schema_key(schema_key)
144146
end
145147

146148
def cube_names
147-
@raw_schema.getCubes.map{|c| c.getName}
149+
@raw_schema.getCubes.map(&:getName)
148150
end
149151

150152
def cube(name)
@@ -193,7 +195,7 @@ def role_name
193195
end
194196

195197
def role_names
196-
# workaround to access non-public method (was not public when using inside Torquebox)
198+
# Workaround to access non-public method
197199
# @raw_connection.getRoleNames.to_a
198200
@raw_connection.java_method(:getRoleNames).call.to_a
199201
end
@@ -206,7 +208,7 @@ def role_name=(name)
206208

207209
def role_names=(names)
208210
Error.wrap_native_exception do
209-
# workaround to access non-public method (was not public when using inside Torquebox)
211+
# Workaround to access non-public method
210212
# @raw_connection.setRoleNames(Array(names))
211213
names = Array(names)
212214
@raw_connection.java_method(:setRoleNames, [Java::JavaUtil::List.java_class]).call(names)
@@ -225,7 +227,7 @@ def locale=(locale)
225227
@raw_connection.setLocale(java_locale)
226228
end
227229

228-
# access MondrianServer instance
230+
# Access MondrianServer instance
229231
def mondrian_server
230232
Error.wrap_native_exception do
231233
@raw_connection.getMondrianConnection.getServer
@@ -325,11 +327,11 @@ def jdbc_uri
325327

326328
def connection_string
327329
string = "jdbc:mondrian:Jdbc=#{quote_string(jdbc_uri)};JdbcDrivers=#{jdbc_driver};"
328-
# by default use content checksum to reload schema when catalog has changed
330+
# By default use content checksum to reload schema when catalog has changed
329331
string += "UseContentChecksum=true;" unless @params[:use_content_checksum] == false
330332
string += "PinSchemaTimeout=#{@params[:pin_schema_timeout]};" if @params[:pin_schema_timeout]
331333
if role = @params[:role] || @params[:roles]
332-
roles = Array(role).map{|r| r && r.to_s.gsub(',', ',,')}.compact
334+
roles = Array(role).map { |r| r && r.to_s.gsub(',', ',,') }.compact
333335
string += "Role=#{quote_string(roles.join(','))};" unless roles.empty?
334336
end
335337
if locale = @params[:locale]
@@ -371,14 +373,14 @@ def jdbc_uri_mysql
371373
alias_method :jdbc_uri_mariadb, :jdbc_uri_generic
372374

373375
def jdbc_uri_oracle
374-
# connection using TNS alias
376+
# Connection using TNS alias
375377
if @params[:database] && !@params[:host] && !@params[:url] && ENV['TNS_ADMIN']
376378
"jdbc:oracle:thin:@#{@params[:database]}"
377379
else
378380
@params[:url] || begin
379381
database = @params[:database]
380382
unless database =~ %r{^(:|/)}
381-
# assume database is a SID if no colon or slash are supplied (backward-compatibility)
383+
# Assume database is a SID if no colon or slash are supplied (backward-compatibility)
382384
database = ":#{database}"
383385
end
384386
"jdbc:oracle:thin:@#{@params[:host] || 'localhost'}:#{@params[:port] || 1521}#{database}"
@@ -470,7 +472,7 @@ def catalog_content
470472
if @params[:catalog_content]
471473
@params[:catalog_content]
472474
elsif @params[:schema]
473-
@params[:schema].to_xml(:driver => @driver)
475+
@params[:schema].to_xml(driver: @driver)
474476
else
475477
raise ArgumentError, "Specify catalog with :catalog, :catalog_content or :schema option"
476478
end
@@ -483,7 +485,7 @@ def quote_string(string)
483485
def set_statement_parameters(statement, parameters)
484486
if parameters && !parameters.empty?
485487
parameters = parameters.dup
486-
# define addtional parameters which can be accessed from user defined functions
488+
# Define additional parameters which can be accessed from user defined functions
487489
if define_parameters = parameters.delete(:define_parameters)
488490
query_validator = statement.getQuery.createValidator
489491
define_parameters.each do |dp_name, dp_value|

lib/mondrian/olap/cube.rb

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
# frozen_string_literal: true
2+
13
require 'forwardable'
24

35
module Mondrian
@@ -66,7 +68,7 @@ def virtual?
6668
end
6769

6870
def dimensions
69-
@dimenstions ||= @raw_cube.getDimensions.map { |d| dimension_from_raw(d) }
71+
@dimensions ||= @raw_cube.getDimensions.map { |d| dimension_from_raw(d) }
7072
end
7173

7274
def dimension_names
@@ -113,8 +115,7 @@ def member_by_segments(*segment_names)
113115
raw_member && Member.new(raw_member)
114116
end
115117

116-
def_delegators :@cache_control, :flush_region_cache_with_segments, :flush_region_cache_with_segments
117-
def_delegators :@cache_control, :flush_region_cache_with_full_names, :flush_region_cache_with_full_names
118+
def_delegators :@cache_control, :flush_region_cache_with_segments, :flush_region_cache_with_full_names
118119

119120
private
120121

@@ -187,7 +188,6 @@ def annotations
187188
def visible?
188189
@raw_dimension.isVisible
189190
end
190-
191191
end
192192

193193
class Hierarchy
@@ -274,7 +274,6 @@ def annotations
274274
def visible?
275275
@raw_hierarchy.isVisible
276276
end
277-
278277
end
279278

280279
class Level
@@ -372,7 +371,6 @@ def annotations
372371
def visible?
373372
@raw_level.isVisible
374373
end
375-
376374
end
377375

378376
class Member
@@ -461,9 +459,7 @@ def descendants_at_level(level)
461459

462460
members = [self]
463461
(descendants_level_index - current_level_index).times do
464-
members = members.map do |member|
465-
member.children
466-
end.flatten
462+
members = members.flat_map(&:children)
467463
end
468464
members
469465
end
@@ -532,7 +528,7 @@ def flush_region_cache_with_segments(*segment_names)
532528
end
533529

534530
def flush_region_cache_with_full_names(*full_names)
535-
members = full_names.map { |name| @cube.member(*name).mondrian_member }
531+
members = full_names.map { |name| @cube.member(name).mondrian_member }
536532
flush(members)
537533
end
538534

0 commit comments

Comments
 (0)