flex-cartesian 0.1.2 → 0.1.4

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.
Files changed (4) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +104 -2
  3. data/lib/golden.rb +137 -0
  4. metadata +4 -64
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 96675f38efe3e792ac84749a93c495b5770de5b0b918331274b9f53999940a7a
4
- data.tar.gz: 0313d03ed02f6e568a803403628fdf1dedf5af9b883575d702b15a12dbf166dd
3
+ metadata.gz: 7f3e10474552a7bfa80b189b46769b1dc621468b9a068918e42cce44d14879ab
4
+ data.tar.gz: 1dd5bea186d416ebb39bc6f74d268d271516cbbf945a9c3fbfa97d4682bf85bc
5
5
  SHA512:
6
- metadata.gz: 6bd8c8a7dca528707bb800b7f2fed94c6fe0fbcd1fcc2e114eb00d66505f8928a0afd88e7e38794d21cbe1c2be3813d9d43d7232b3e1d37583d0d19910ff1694
7
- data.tar.gz: 16049ce389e509ca7264ea1b5752c6bd539270d63f211cf95e08a386bf9a3f6d67ba5615cb6fc4efca9033a01a70b1bd631b083f2f2ad6614f29850d2fe68ea7
6
+ metadata.gz: eefbe47d1db3cf1e4316502de159d68963c633d72ea84fc38a378eefa235747845b8d7496d26534eeed6986e2e16cf9728366a1b3015e6e509f0c57a8ace5b3a
7
+ data.tar.gz: d4eaf78c1280145ad57841a984bde38b0f5f25b69845a59300877ed04ad6c48a2f325a144b6ff97aad6933fb9ed0b36bdc9b6c0c8156126fffd4a8c1d55aca32
data/README.md CHANGED
@@ -24,10 +24,10 @@
24
24
 
25
25
  ## Installation
26
26
 
27
- Install dependencies:
28
-
29
27
  ```bash
30
28
  bundle install
29
+ gem build flex-cartesian.gemspec
30
+ gem install flex-cartesian-*.gem
31
31
  ```
32
32
 
33
33
  ## Usage
@@ -77,6 +77,108 @@ s.output(format: :markdown, align: true)
77
77
  s.output(format: :csv)
78
78
  ```
79
79
 
80
+ ## API Overview
81
+
82
+ ### Initialization
83
+ ```ruby
84
+ FlexCartesian.new(dimensions_hash)
85
+ ```
86
+ - `dimensions_hash`: a hash with named dimensions; each value can be an `Enumerable` (e.g., arrays, ranges).
87
+
88
+ Example:
89
+ ```ruby
90
+ dimensions = {
91
+ dim1: [1, 2],
92
+ dim2: ['x', 'y'],
93
+ dim3: [true, false]
94
+ }
95
+
96
+ FlexCartesian.new(dimensions)
97
+ ```
98
+
99
+ ---
100
+
101
+ ### Iterate Over All Combinations
102
+ ```ruby
103
+ # With block
104
+ cartesian(dims = nil, lazy: false) { |vector| ... }
105
+
106
+ # Without block: returns Enumerator
107
+ cartesian(dims = nil, lazy: false)
108
+ ```
109
+ - `dims`: optional dimensions hash (default is the one provided at initialization).
110
+ - `lazy`: if true, returns a lazy enumerator.
111
+
112
+ Each combination is passed as a `Struct` with fields matching the dimension names:
113
+ ```ruby
114
+ s.cartesian { |v| puts "#{v.dim1} - #{v.dim2}" }
115
+ ```
116
+
117
+ ---
118
+
119
+ ### Count Total Combinations
120
+ ```ruby
121
+ size(dims = nil) → Integer
122
+ ```
123
+ Returns the number of possible combinations.
124
+
125
+ ---
126
+
127
+ ### Convert to Array
128
+ ```ruby
129
+ to_a(limit: nil) → Array
130
+ ```
131
+ - `limit`: maximum number of combinations to collect.
132
+
133
+ ---
134
+
135
+ ### Iterate with Progress Bar
136
+ ```ruby
137
+ progress_each(dims = nil, lazy: false, title: "Processing") { |v| ... }
138
+ ```
139
+ Displays a progress bar using `ruby-progressbar`.
140
+
141
+ ---
142
+
143
+ ### Print Table to Console
144
+ ```ruby
145
+ output(
146
+ separator: " | ",
147
+ colorize: false,
148
+ align: false,
149
+ format: :plain # or :markdown, :csv
150
+ limit: nil
151
+ )
152
+ ```
153
+ Prints all combinations in table form (plain/markdown/CSV).
154
+ Markdown example:
155
+ ```
156
+ | dim1 | dim2 |
157
+ |------|------|
158
+ | 1 | "a" |
159
+ | 2 | "b" |
160
+ ```
161
+
162
+ ---
163
+
164
+ ### Load from JSON or YAML
165
+ ```ruby
166
+ FlexCartesian.from_json("file.json")
167
+ FlexCartesian.from_yaml("file.yaml")
168
+ ```
169
+
170
+ ---
171
+
172
+ ### Output from Vectors
173
+ Each yielded combination is a `Struct` extended with:
174
+ ```ruby
175
+ output(separator: " | ", colorize: false, align: false)
176
+ ```
177
+ Example:
178
+ ```ruby
179
+ s.cartesian { |v| v.output(colorize: true, align: true) }
180
+ ```
181
+
80
182
  ## License
81
183
 
82
184
  This project is licensed under the terms of the GNU General Public License v3.0.
data/lib/golden.rb ADDED
@@ -0,0 +1,137 @@
1
+ require 'ostruct'
2
+ require 'progressbar'
3
+ require 'colorize'
4
+ require 'json'
5
+ require 'yaml'
6
+
7
+ module FlexOutput
8
+ def output(separator: " | ", colorize: false, align: false)
9
+ return puts "(empty struct)" unless respond_to?(:members) && respond_to?(:values)
10
+
11
+ values_list = members.zip(values.map { |v| v.inspect })
12
+
13
+ # calculate widths, if align required
14
+ widths = align ? values_list.map { |k, v| [k.to_s.size, v.size].max } : []
15
+
16
+ line = values_list.each_with_index.map do |(_, val), i|
17
+ str = val.to_s
18
+ str = str.ljust(widths[i]) if align
19
+ colorize ? str.colorize(:cyan) : str
20
+ end
21
+
22
+ puts line.join(separator)
23
+ end
24
+ end
25
+
26
+ class FlexCartesian
27
+ attr :dimensions
28
+
29
+ def initialize(dimensions = nil)
30
+ @dimensions = dimensions
31
+ end
32
+
33
+ def cartesian(dims = nil, lazy: false)
34
+ dimensions = dims || @dimensions
35
+ return nil unless dimensions.is_a?(Hash)
36
+
37
+ names = dimensions.keys
38
+ values = dimensions.values.map { |dim| dim.is_a?(Enumerable) ? dim.to_a : [dim] }
39
+
40
+ return to_enum(:cartesian, dims, lazy: lazy) unless block_given?
41
+ return if values.any?(&:empty?)
42
+
43
+ struct_class = Struct.new(*names).tap { |sc| sc.include(FlexOutput) }
44
+
45
+ base = values.first.product(*values[1..])
46
+ enum = lazy ? base.lazy : base
47
+
48
+ enum.each do |combo|
49
+ yield struct_class.new(*combo)
50
+ end
51
+ end
52
+
53
+ def size(dims = nil)
54
+ dimensions = dims || @dimensions
55
+ return 0 unless dimensions.is_a?(Hash)
56
+
57
+ values = dimensions.values.map { |dim| dim.is_a?(Enumerable) ? dim.to_a : [dim] }
58
+ return 0 if values.any?(&:empty?)
59
+
60
+ values.map(&:size).inject(1, :*)
61
+ end
62
+
63
+ def to_a(limit: nil)
64
+ result = []
65
+ cartesian do |v|
66
+ result << v
67
+ break if limit && result.size >= limit
68
+ end
69
+ result
70
+ end
71
+
72
+ def progress_each(dims = nil, lazy: false, title: "Processing")
73
+ total = size(dims)
74
+ bar = ProgressBar.create(title: title, total: total, format: '%t [%B] %p%% %e')
75
+
76
+ cartesian(dims, lazy: lazy) do |v|
77
+ yield v
78
+ bar.increment
79
+ end
80
+ end
81
+
82
+ def output(separator: " | ", colorize: false, align: false, format: :plain, limit: nil)
83
+ rows = []
84
+ cartesian do |v|
85
+ rows << v
86
+ break if limit && rows.size >= limit
87
+ end
88
+ return if rows.empty?
89
+
90
+ headers = rows.first.members.map(&:to_s)
91
+
92
+ # Get widths
93
+ widths = align ? headers.to_h { |h|
94
+ [h, [h.size, *rows.map { |r| fmt_cell(r[h], false).size }].max]
95
+ } : {}
96
+
97
+ # Title
98
+ case format
99
+ when :markdown
100
+ puts "| " + headers.map { |h| h.ljust(widths[h] || h.size) }.join(" | ") + " |"
101
+ puts "|-" + headers.map { |h| "-" * (widths[h] || h.size) }.join("-|-") + "-|"
102
+ when :csv
103
+ puts headers.join(",")
104
+ else
105
+ puts headers.map { |h| fmt_cell(h, colorize, widths[h]) }.join(separator)
106
+ end
107
+
108
+ # Rows
109
+ rows.each do |row|
110
+ line = headers.map { |h| fmt_cell(row[h], colorize, widths[h]) }
111
+ puts format == :csv ? line.join(",") : line.join(separator)
112
+ end
113
+ end
114
+
115
+ def self.from_json(path)
116
+ data = JSON.parse(File.read(path), symbolize_names: true)
117
+ new(data)
118
+ end
119
+
120
+ def self.from_yaml(path)
121
+ data = YAML.safe_load(File.read(path), symbolize_names: true)
122
+ new(data)
123
+ end
124
+
125
+ private
126
+
127
+ def fmt_cell(value, colorize, width = nil)
128
+ str = case value
129
+ when String then value # rows - without inspect
130
+ else value.inspect # the rest is good to inspect
131
+ end
132
+ str = str.ljust(width) if width
133
+ colorize ? str.colorize(:cyan) : str
134
+ end
135
+
136
+ end
137
+
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: flex-cartesian
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yury Rassokhin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-07-02 00:00:00.000000000 Z
11
+ date: 2025-07-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: colorize
@@ -52,69 +52,8 @@ dependencies:
52
52
  - - "~>"
53
53
  - !ruby/object:Gem::Version
54
54
  version: '2.0'
55
- - !ruby/object:Gem::Dependency
56
- name: yaml
57
- requirement: !ruby/object:Gem::Requirement
58
- requirements:
59
- - - ">="
60
- - !ruby/object:Gem::Version
61
- version: '0'
62
- type: :runtime
63
- prerelease: false
64
- version_requirements: !ruby/object:Gem::Requirement
65
- requirements:
66
- - - ">="
67
- - !ruby/object:Gem::Version
68
- version: '0'
69
- description: |
70
- Flexible and human-friendly Cartesian product enumerator for Ruby.
71
- Supports dimension-agnostic iteration, named dimensions, structured output,
55
+ description: Supports dimension-agnostic iterators, named dimensions, tabular output,
72
56
  lazy/eager evaluation, progress bar, JSON/YAML loading, and export to Markdown/CSV.
73
-
74
- Example:
75
-
76
- require 'flex-cartesian'
77
-
78
- # Define Cartesian space with named dimensions:
79
- example = {
80
- dim1: [1, 2],
81
- dim2: ['x', 'y'],
82
- dim3: [true, false]
83
- }
84
- s = FlexCartesian.new(example)
85
-
86
- # Iterate over all combinations and calculate function on each combination:
87
- s.cartesian { |v| puts "#{v.dim1}-#{v.dim2}" if v.dim3 }
88
-
89
- # Get number of Cartesian combinations:
90
- puts "Total size: #{s.size}"
91
-
92
- # Convert Cartesian space to array of combinations
93
- array = s.to_a(limit: 3)
94
- puts array.inspect
95
-
96
- def do_something(v)
97
- end
98
-
99
- # Display progress bar (useful for large Cartesian spaces)
100
- s.progress_each { |v| do_something(v) }
101
-
102
- # Print Cartesian space as table
103
- s.output(align: true)
104
-
105
- # Lazy evaluation without materializing entire Cartesian product in memory:
106
- s.cartesian(lazy: true).take(2).each { |v| puts v.inspect }
107
-
108
- # Load from JSON or YAML
109
- File.write('example.json', JSON.pretty_generate(example))
110
- s = FlexCartesian.from_json('example.json')
111
- s.output
112
-
113
- # Export to Markdown
114
- s.output(format: :markdown, align: true)
115
-
116
- # Export to CSV
117
- s.output(format: :csv)
118
57
  email:
119
58
  - yuri.rassokhin@gmail.com
120
59
  executables: []
@@ -125,6 +64,7 @@ files:
125
64
  - LICENSE
126
65
  - README.md
127
66
  - lib/flex-cartesian.rb
67
+ - lib/golden.rb
128
68
  homepage: https://github.com/Yuri-Rassokhin/flex-cartesian
129
69
  licenses:
130
70
  - GPL-3.0