schema-model 0.7.4 → 0.9.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/ARCHITECTURE.md DELETED
@@ -1,161 +0,0 @@
1
- # Architecture
2
-
3
- This document describes the internal architecture of the `schema-model` gem.
4
-
5
- ## Overview
6
-
7
- The gem transforms hash data into strongly-typed Ruby objects with parsing, validation, and nested associations. The core flow is:
8
-
9
- ```
10
- Hash Data → from_hash() → Parser Methods → Schema Instance
11
- ↓
12
- parsing_errors (if invalid)
13
- ```
14
-
15
- ## Module Hierarchy
16
-
17
- ```
18
- Schema::All (convenience bundle)
19
- ├── Schema::Model (core attribute system)
20
- │ └── Schema::Parsers::Common
21
- ├── Schema::Associations::HasOne
22
- ├── Schema::Associations::HasMany
23
- ├── Schema::Parsers::American
24
- ├── Schema::Parsers::Array
25
- ├── Schema::Parsers::Hash
26
- ├── Schema::Parsers::Json
27
- └── Schema::ActiveModelValidations
28
-
29
- Opt-in (via schema_include):
30
- ├── Schema::ArrayHeaders (map CSV headers to attributes)
31
- └── Schema::Arrays (models to/from flat arrays)
32
-
33
- Standalone:
34
- ├── Schema::CSVParser (class; reads CSV rows into models)
35
- └── SchemaValidator (ActiveModel validator for `schema: true`)
36
- ```
37
-
38
- `schema_include` both includes a module and records it in `schema_config[:schema_includes]`, so it is also included in every nested association class, including ones defined later.
39
-
40
- ## Core Components
41
-
42
- ### Schema::Model (`lib/schema/model.rb`)
43
-
44
- The foundation module providing:
45
-
46
- - **`attribute(name, type, options)`**: Defines schema fields. Each call:
47
- 1. Adds field metadata to the class's `schema` hash via `add_value_to_class_method`
48
- 2. Generates getter, setter, and `<name>_was_set?` methods
49
- 3. Setter invokes `parse_<type>` method automatically
50
-
51
- - **`from_hash(data, skip_fields)`**: Class method that creates instance and calls `update_attributes`
52
-
53
- - **`update_attributes(data, skip_fields)`**: Iterates the data's key/value pairs, matches them against the schema, and invokes setters. Plain attributes are set before associations, so an association's `external_type_field` can read a sibling attribute. Each key is looked up on its own: symbol keys in `schema`, anything else in `schema_with_string_keys` (rebuilt whenever `schema` changes). `skip_fields` entries match a field by name or alias, as a symbol or string; a `{ association: [...] }` entry passes a nested list down.
54
-
55
- - **Aliases**: each alias adds a second schema entry with `alias_of:` pointing at the real attribute, plus aliased getter/setter methods. Serialization and array conversion skip entries with `alias_of`.
56
-
57
- - **`as_json` / `to_hash`**: Serialization back to hash format
58
-
59
- ### Schema::Parsers::Common (`lib/schema/parsers/common.rb`)
60
-
61
- Base parser methods for fundamental types:
62
- - `parse_integer`, `parse_float`, `parse_string`, `parse_string_or_nil`
63
- - `parse_boolean`, `parse_time`, `parse_date`
64
-
65
- Each parser:
66
- 1. Accepts `(field_name, parsing_errors, value)`
67
- 2. Returns converted value or nil
68
- 3. Adds to `parsing_errors` on failure (never raises)
69
-
70
- Additional parsers extend these: `Parsers::American` (date formats), `Parsers::Array`, `Parsers::Hash`, `Parsers::Json`.
71
-
72
- ### Schema::Associations (`lib/schema/associations/`)
73
-
74
- **HasOne** and **HasMany** define nested relationships:
75
-
76
- ```ruby
77
- has_one(:profile) { attribute :bio, :string }
78
- has_many(:posts) { attribute :title, :string }
79
- ```
80
-
81
- Each association gets a generated class (`SchemaHasOne<Name>` / `SchemaHasMany<Name>` by default, or `class_name:`), defined as a constant inside the parent class. `base_class:` subclasses an existing schema instead of creating a fresh model class.
82
-
83
- Both use **SchemaCreator** (`schema_creator.rb`) to:
84
- 1. Determine which class to instantiate (static or dynamic)
85
- 2. Call `from_hash` on the nested class
86
- 3. Propagate parsing errors to parent: `invalid` when the nested model has parsing errors, `incompatible` when the value isn't a hash (or, for has-many, an array), and `unknown` when no dynamic type matches
87
-
88
- `has_many ..., from: :hash` accepts a hash keyed by id instead of an array; each key is written to `hash_key_field` (default `:id`) on the nested model.
89
-
90
- **DynamicTypes** enables polymorphic associations:
91
-
92
- ```ruby
93
- has_many(:items, type_field: :kind) do
94
- add_type('widget') { attribute :size, :integer }
95
- add_type('gadget') { attribute :power, :float }
96
- default_type { } # fallback
97
- end
98
- ```
99
-
100
- The `type_field` option tells SchemaCreator which key in the nested data determines the subclass (the type field attribute's aliases are checked too); `external_type_field` reads the type from an attribute on the parent instead. `type_ignorecase: true` compares type names case-insensitively. Each `add_type` creates a subclass of the association class, defined on the parent class as `<Name>AssociationType<Type>`.
101
-
102
- `Associations::Base` stores the parent class by name and resolves it with `Object.const_get` (so reloaded constants are picked up); a parent that was anonymous when the association was declared is kept by reference instead.
103
-
104
- ### Schema::Utils (`lib/schema/utils.rb`)
105
-
106
- Utility methods for:
107
- - `classify_name`: String → ClassName conversion
108
- - `create_schema_class`: Dynamically creates nested schema classes
109
- - `add_association_class`: Wires up association with proper modules
110
- - `add_attribute_default_methods` / `add_association_default_methods`: Default value handling (`copy_default` hands each read a deep copy of the default)
111
-
112
- ### Error Handling
113
-
114
- Two error storage mechanisms:
115
-
116
- 1. **Schema::Errors** (`lib/schema/errors.rb`): Simple hash-based storage, used standalone
117
-
118
- 2. **ActiveModel::Errors**: When `Schema::ActiveModelValidations` is included, `parsing_errors` returns `ActiveModel::Errors` instance
119
-
120
- Parsing errors are distinct from validation errors:
121
- - **Parsing errors**: Type conversion failures (string "abc" → integer)
122
- - **Validation errors**: Business rule failures (via `validates` DSL)
123
-
124
- `parsed!` raises `ParsingException` when there are parsing errors and `valid_model!` raises `ValidationException` when validations fail; `valid!` calls both. `valid?` runs validations only and does not look at parsing errors.
125
-
126
- Parsing errors use these codes from `Schema::ParsingErrors`: `invalid`, `incompatible`, `unknown`, `unknown_attribute`, `unhandled_type`.
127
-
128
- ### CSV and Arrays
129
-
130
- - **`Schema::ArrayHeaders`** (`lib/schema/array_headers.rb`): `map_headers_to_attributes(headers)` returns a nested hash of `{ field: { index: n } }` entries. Has-many fields are matched as `<prefix><n><key>` (prefix is the association name or an alias, `n` counts from 1) and map to `{ indexes: [...] }`. Has-one fields are matched by their own key or alias; a column already claimed by the parent or an earlier has-one isn't reused. Associations never map to a column of their own.
131
- - **`Schema::Arrays`** (`lib/schema/arrays.rb`): `to_headers`, `to_empty_array`, `to_a` and `from_array(array, mapped_headers)`. Has-many associations need a `size:` option for the fixed-width methods.
132
- - **`Schema::CSVParser`** (`lib/schema/csv_parser.rb`): wraps a `CSV` object, maps its header row once, and yields a model per row (`each`, `shift`, `missing_fields`).
133
-
134
- ### Inheritance Helper Integration
135
-
136
- The gem uses `inheritance-helper` for schema inheritance. Key method:
137
- - `add_value_to_class_method(:schema, name => options)`: Accumulates schema definitions across class hierarchy
138
-
139
- This allows schema classes to inherit attributes from parent classes.
140
-
141
- ## Data Flow Example
142
-
143
- ```ruby
144
- class OrderSchema
145
- include Schema::All
146
- attribute :total, :float
147
- has_one(:customer) { attribute :name, :string }
148
- end
149
-
150
- order = OrderSchema.from_hash({
151
- total: "99.50",
152
- customer: { name: "Alice" }
153
- })
154
- ```
155
-
156
- 1. `from_hash` calls `new.update_attributes(data)`
157
- 2. `update_attributes` iterates keys, finds `:total` in schema
158
- 3. Calls `self.total = "99.50"` → invokes `parse_float`
159
- 4. For `:customer`, recognizes association, delegates to SchemaCreator
160
- 5. SchemaCreator calls `OrderSchema::SchemaHasOneCustomer.from_hash({name: "Alice"})`
161
- 6. Returns populated `OrderSchema` instance
data/CLAUDE.md DELETED
@@ -1,87 +0,0 @@
1
- # CLAUDE.md
2
-
3
- This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4
-
5
- ## Build and Test Commands
6
-
7
- ```bash
8
- # Install dependencies
9
- bundle install
10
-
11
- # Run all tests
12
- bundle exec rspec
13
-
14
- # Run a single test file
15
- bundle exec rspec spec/schema/model_spec.rb
16
-
17
- # Run a specific test by line number
18
- bundle exec rspec spec/schema/model_spec.rb:42
19
-
20
- # Run linter
21
- bundle exec rubocop
22
-
23
- # Auto-fix linter issues
24
- bundle exec rubocop -A
25
- ```
26
-
27
- ## Architecture
28
-
29
- This is a Ruby gem (`schema-model`) for data transformation, validation, and type safety. It transforms hash data into strongly-typed model objects.
30
-
31
- ### Core Module Structure
32
-
33
- - **Schema::Model** (`lib/schema/model.rb`) - Foundation module providing `attribute` definitions, `from_hash` class method, and attribute accessors.
34
-
35
- - **Schema::All** (`lib/schema/all.rb`) - Convenience module bundling Model + Associations + Parsers + ActiveModel validations. This is the typical include.
36
-
37
- - **Schema::Parsers** - Type parsers in `lib/schema/parsers/`:
38
- - `Common` - integer, string, string_or_nil, float, time, date, boolean (included by `Schema::Model`)
39
- - `American` - american_date, american_time (MM/DD/YYYY format)
40
- - `Array` - array with optional separator and data_type
41
- - `Hash` - hash/dictionary values
42
- - `Json` - JSON string parsing
43
-
44
- - **Schema::Associations** - `HasOne` and `HasMany` for nested relationships. `DynamicTypes` enables polymorphic associations via `type_field`/`add_type`.
45
-
46
- - **Schema::Arrays** (`lib/schema/arrays.rb`) - Convert models to/from flat arrays for CSV support.
47
-
48
- - **Schema::ArrayHeaders** (`lib/schema/array_headers.rb`) - Map CSV headers to schema attributes.
49
-
50
- - **Schema::CSVParser** (`lib/schema/csv_parser.rb`) - Class that reads `CSV` rows into models (needs ArrayHeaders + Arrays on the model).
51
-
52
- - **SchemaValidator** (`lib/schema_validator.rb`) - ActiveModel validator behind `validates :assoc, schema: true`.
53
-
54
- ### Key Patterns
55
-
56
- **Attribute Definition**: Each `attribute` call generates getter, setter, and `<name>_was_set?` predicate. Setter invokes type-specific parser.
57
-
58
- **Parsing Errors**: Stored in `parsing_errors`. Parsers add errors for invalid values rather than raising exceptions. With ActiveModelValidations, use `parsed?`/`parsed!`; `valid?` does not check parsing errors.
59
-
60
- **Schema Inheritance**: Uses `inheritance-helper` gem. Schema definitions accumulate via `add_value_to_class_method(:schema, ...)`.
61
-
62
- **Dynamic Types**: For polymorphic associations, use `type_field` option with `add_type` blocks. Supports `external_type_field`, `type_ignorecase`, and `default_type`.
63
-
64
- ## CI
65
-
66
- `.github/workflows/ci.yml` runs RuboCop and the specs on the `.ruby-version` Ruby. On pushes to `master`, it publishes `coverage.svg` (line) and `branches.svg` (branch) from `script/coverage_badge.rb` to the orphan `badges` branch for the README badges.
67
-
68
- ## Releases
69
-
70
- Releases are automated by release-please (`.github/workflows/release.yml`). Conventional commits on `master` (`fix:` → patch, `feat:` → minor, `!`/`BREAKING CHANGE` → major) update an open release PR that bumps `lib/schema/version.rb` (`Schema::VERSION`, read by the gemspec) and `CHANGELOG.md`. Merging that PR tags `vX.Y.Z`, creates the GitHub release, and publishes the gem to RubyGems. Don't bump the version by hand.
71
-
72
- ## Code Commits
73
-
74
- Format using angular formatting:
75
- ```
76
- <type>(<scope>): <short summary>
77
- ```
78
- - **type**: build|ci|docs|feat|fix|perf|refactor|test
79
- - **scope**: The feature or component of the service we're working on
80
- - **summary**: Summary in present tense. Not capitalized. No period at the end.
81
-
82
- ## Documentation Maintenance
83
-
84
- When modifying the codebase, keep documentation in sync:
85
- - **ARCHITECTURE.md** - Update when adding/removing classes, changing component relationships, or altering data flow patterns
86
- - **README.md** - Update when adding new features, changing public APIs, or modifying usage examples
87
- - **CHANGELOG.md** - Generated by release-please from commit messages; don't edit by hand except to fix release notes
data/Gemfile DELETED
@@ -1,16 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source 'https://rubygems.org'
4
-
5
- gem 'inheritance-helper'
6
-
7
- group :development do
8
- gem 'activemodel'
9
- gem 'csv'
10
- gem 'rubocop'
11
- end
12
-
13
- group :spec do
14
- gem 'rspec'
15
- gem 'simplecov'
16
- end
data/Gemfile.lock DELETED
@@ -1,134 +0,0 @@
1
- GEM
2
- remote: https://rubygems.org/
3
- specs:
4
- activemodel (8.1.4)
5
- activesupport (= 8.1.4)
6
- activesupport (8.1.4)
7
- base64
8
- bigdecimal
9
- concurrent-ruby (~> 1.0, >= 1.3.1)
10
- connection_pool (>= 2.2.5)
11
- drb
12
- i18n (>= 1.6, < 2)
13
- json
14
- logger (>= 1.4.2)
15
- minitest (>= 5.1)
16
- securerandom (>= 0.3)
17
- tzinfo (~> 2.0, >= 2.0.5)
18
- uri (>= 0.13.1)
19
- ast (2.4.3)
20
- base64 (0.3.0)
21
- bigdecimal (4.1.3)
22
- concurrent-ruby (1.3.8)
23
- connection_pool (3.0.2)
24
- csv (3.3.6)
25
- diff-lcs (1.6.2)
26
- drb (2.2.3)
27
- i18n (1.15.2)
28
- concurrent-ruby (~> 1.0)
29
- inheritance-helper (0.2.5)
30
- json (3.0.2)
31
- language_server-protocol (3.17.0.6)
32
- lint_roller (1.1.0)
33
- logger (1.7.0)
34
- minitest (6.0.6)
35
- drb (~> 2.0)
36
- prism (~> 1.5)
37
- parallel (2.3.0)
38
- parser (3.3.12.0)
39
- ast (~> 2.4.1)
40
- racc
41
- prism (1.9.0)
42
- racc (1.8.1)
43
- rainbow (3.1.1)
44
- regexp_parser (2.13.1)
45
- rspec (3.13.2)
46
- rspec-core (~> 3.13.0)
47
- rspec-expectations (~> 3.13.0)
48
- rspec-mocks (~> 3.13.0)
49
- rspec-core (3.13.6)
50
- rspec-support (~> 3.13.0)
51
- rspec-expectations (3.13.5)
52
- diff-lcs (>= 1.2.0, < 2.0)
53
- rspec-support (~> 3.13.0)
54
- rspec-mocks (3.13.8)
55
- diff-lcs (>= 1.2.0, < 2.0)
56
- rspec-support (~> 3.13.0)
57
- rspec-support (3.13.7)
58
- rubocop (1.91.0)
59
- json (>= 2.3)
60
- language_server-protocol (~> 3.17.0.2)
61
- lint_roller (~> 1.1.0)
62
- parallel (>= 1.10)
63
- parser (>= 3.3.0.2)
64
- rainbow (>= 2.2.2, < 4.0)
65
- regexp_parser (>= 2.9.3, < 3.0)
66
- rubocop-ast (>= 1.49.0, < 2.0)
67
- ruby-progressbar (~> 1.7)
68
- unicode-display_width (>= 2.4.0, < 4.0)
69
- rubocop-ast (1.50.0)
70
- parser (>= 3.3.7.2)
71
- prism (~> 1.7)
72
- ruby-progressbar (1.13.0)
73
- securerandom (0.4.1)
74
- simplecov (1.3.2)
75
- tzinfo (2.0.6)
76
- concurrent-ruby (~> 1.0)
77
- unicode-display_width (3.3.0)
78
- unicode-emoji (~> 4.3)
79
- unicode-emoji (4.3.0)
80
- uri (1.1.1)
81
-
82
- PLATFORMS
83
- ruby
84
- x86_64-darwin-24
85
-
86
- DEPENDENCIES
87
- activemodel
88
- csv
89
- inheritance-helper
90
- rspec
91
- rubocop
92
- simplecov
93
-
94
- CHECKSUMS
95
- activemodel (8.1.4) sha256=9a0906c127bd118aa5f06157db12b8f750da83dccee72db2f658547a0ae7a0e8
96
- activesupport (8.1.4) sha256=891dd3fe43c4fc8391934b237b1189bb1f661c932a84a840043db9d46a4e80df
97
- ast (2.4.3) sha256=954615157c1d6a382bc27d690d973195e79db7f55e9765ac7c481c60bdb4d383
98
- base64 (0.3.0) sha256=27337aeabad6ffae05c265c450490628ef3ebd4b67be58257393227588f5a97b
99
- bigdecimal (4.1.3) sha256=61ebe1e5e559bdc3cc6f2c0ee7f427321fc838f59611c294356eb04d6e21cf66
100
- concurrent-ruby (1.3.8) sha256=b2f1be836e968ccc78ccfce277ea79c72a88633f22306782c16ff23fb415d1e1
101
- connection_pool (3.0.2) sha256=33fff5ba71a12d2aa26cb72b1db8bba2a1a01823559fb01d29eb74c286e62e0a
102
- csv (3.3.6) sha256=aba61e7e507a66f03d45cb1f3c4b6359861c3504038b422962875dce099e4456
103
- diff-lcs (1.6.2) sha256=9ae0d2cba7d4df3075fe8cd8602a8604993efc0dfa934cff568969efb1909962
104
- drb (2.2.3) sha256=0b00d6fdb50995fe4a45dea13663493c841112e4068656854646f418fda13373
105
- i18n (1.15.2) sha256=00f9eb62412fe593b2a65a97daa75300d37abb8f7202ec748e94b6d46a9dd1b5
106
- inheritance-helper (0.2.5) sha256=e318d548d2e201910ab484b2139a11e61f3dfac012c4aba090bcfaa5f9687b85
107
- json (3.0.2) sha256=8e6d7e7b11384c21230430cef90b71f14849a34a1f4452796670f7c981bd19df
108
- language_server-protocol (3.17.0.6) sha256=5ef2c0c138f8267e1bc631d3328347d354f96724b0af22f2c79516120443b7f0
109
- lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87
110
- logger (1.7.0) sha256=196edec7cc44b66cfb40f9755ce11b392f21f7967696af15d274dde7edff0203
111
- minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
112
- parallel (2.3.0) sha256=f75a3e904101ce6a1ccb6b8dc800cbafd42166d83330192438dcf29395167a6f
113
- parser (3.3.12.0) sha256=21a6d7f755d5a24dfbdc6e6b772e4e879a52e7631a88bc5a3a134606052c9828
114
- prism (1.9.0) sha256=7b530c6a9f92c24300014919c9dcbc055bf4cdf51ec30aed099b06cd6674ef85
115
- racc (1.8.1) sha256=4a7f6929691dbec8b5209a0b373bc2614882b55fc5d2e447a21aaa691303d62f
116
- rainbow (3.1.1) sha256=039491aa3a89f42efa1d6dec2fc4e62ede96eb6acd95e52f1ad581182b79bc6a
117
- regexp_parser (2.13.1) sha256=5aedb6b7c35688f51e86eef17a15374dfd287c83f4ca644b2c5f3ec50a33b44b
118
- rspec (3.13.2) sha256=206284a08ad798e61f86d7ca3e376718d52c0bc944626b2349266f239f820587
119
- rspec-core (3.13.6) sha256=a8823c6411667b60a8bca135364351dda34cd55e44ff94c4be4633b37d828b2d
120
- rspec-expectations (3.13.5) sha256=33a4d3a1d95060aea4c94e9f237030a8f9eae5615e9bd85718fe3a09e4b58836
121
- rspec-mocks (3.13.8) sha256=086ad3d3d17533f4237643de0b5c42f04b66348c28bf6b9c2d3f4a3b01af1d47
122
- rspec-support (3.13.7) sha256=0640e5570872aafefd79867901deeeeb40b0c9875a36b983d85f54fb7381c47c
123
- rubocop (1.91.0) sha256=9c82b7bf391c5d7e3798c5b9996e22a1fe3bd7468e351dfdeb96140c058296d0
124
- rubocop-ast (1.50.0) sha256=b9ca88300da0803ee222ad20cdb30494c0a784eed06fdc35d254b06d662788db
125
- ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
126
- securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
127
- simplecov (1.3.2) sha256=2a22ec1759f1b8db60881a2e038758956fc69ef93ed669be0bde0678de6d0e85
128
- tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b
129
- unicode-display_width (3.3.0) sha256=4b7aa66a4b11db50f6f7e98411215cd0dd10eaecb665d36eec5bcacb3fa0b613
130
- unicode-emoji (4.3.0) sha256=11c02fa73290378c066bb0562cd4c87d8e1b706fbbe0059ca12746a4244de8ce
131
- uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
132
-
133
- BUNDLED WITH
134
- 4.0.20
@@ -1,12 +0,0 @@
1
- {
2
- "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json",
3
- "bootstrap-sha": "de8a6c418c6b662696e48b7af3b2d8ad55fb3b4e",
4
- "packages": {
5
- ".": {
6
- "release-type": "ruby",
7
- "package-name": "schema-model",
8
- "version-file": "lib/schema/version.rb",
9
- "include-component-in-tag": false
10
- }
11
- }
12
- }
data/schema-model.gemspec DELETED
@@ -1,24 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'lib/schema/version'
4
-
5
- Gem::Specification.new do |s|
6
- s.name = 'schema-model'
7
- s.version = Schema::VERSION
8
- s.licenses = ['MIT']
9
- s.summary = 'Typed Ruby models built from hashes, JSON and CSV rows'
10
- s.description = 'Declare typed attributes and Schema turns incoming hashes, JSON and CSV rows into Ruby objects. ' \
11
- 'Values are coerced to integers, floats, booleans, dates, times, arrays and hashes, and bad input ' \
12
- 'is recorded as parsing errors instead of raised. Supports nested has_one/has_many associations, ' \
13
- 'polymorphic types chosen by a type field, CSV header mapping, and ActiveModel validations.'
14
- s.authors = ['Doug Youch']
15
- s.email = 'dougyouch@gmail.com'
16
- s.homepage = 'https://github.com/dougyouch/schema'
17
- s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
18
- s.required_ruby_version = '>= 3.2'
19
- s.bindir = 'bin'
20
- s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) }
21
-
22
- s.add_dependency 'inheritance-helper'
23
- s.metadata['rubygems_mfa_required'] = 'true'
24
- end
data/script/console DELETED
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- $LOAD_PATH << File.expand_path('../lib', __dir__)
5
- require 'schema-model'
6
- require 'irb'
7
- IRB.start(__FILE__)
@@ -1,49 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- # Renders a shields-style SVG badge from SimpleCov's .last_run.json.
5
- # script/coverage_badge.rb coverage/.last_run.json coverage.svg # line coverage
6
- # script/coverage_badge.rb coverage/.last_run.json branches.svg branch # branch coverage
7
-
8
- require 'json'
9
-
10
- input, output, metric = ARGV
11
- abort "usage: #{$PROGRAM_NAME} <.last_run.json> <out.svg> [line|branch]" unless input && output
12
-
13
- result = JSON.parse(File.read(input)).fetch('result')
14
- title = metric == 'branch' ? 'branches' : 'coverage'
15
- percent = (metric == 'branch' ? result.fetch('branch') : result['line'] || result.fetch('covered_percent')).to_f
16
- label = "#{percent.floor(1)}%"
17
-
18
- color =
19
- if percent >= 95 then '#4c1'
20
- elsif percent >= 90 then '#97ca00'
21
- elsif percent >= 80 then '#dfb317'
22
- elsif percent >= 70 then '#fe7d37'
23
- else '#e05d44'
24
- end
25
-
26
- # ~7px per character at 11px Verdana, plus padding
27
- left_width = 63
28
- right_width = (label.length * 7) + 10
29
- width = left_width + right_width
30
-
31
- File.write(output, <<~SVG)
32
- <svg xmlns="http://www.w3.org/2000/svg" width="#{width}" height="20" role="img" aria-label="#{title}: #{label}">
33
- <title>#{title}: #{label}</title>
34
- <linearGradient id="s" x2="0" y2="100%">
35
- <stop offset="0" stop-color="#bbb" stop-opacity=".1"/>
36
- <stop offset="1" stop-opacity=".1"/>
37
- </linearGradient>
38
- <clipPath id="r"><rect width="#{width}" height="20" rx="3" fill="#fff"/></clipPath>
39
- <g clip-path="url(#r)">
40
- <rect width="#{left_width}" height="20" fill="#555"/>
41
- <rect x="#{left_width}" width="#{right_width}" height="20" fill="#{color}"/>
42
- <rect width="#{width}" height="20" fill="url(#s)"/>
43
- </g>
44
- <g fill="#fff" text-anchor="middle" font-family="Verdana,Geneva,DejaVu Sans,sans-serif" font-size="11">
45
- <text x="#{left_width / 2}" y="14">#{title}</text>
46
- <text x="#{left_width + (right_width / 2)}" y="14">#{label}</text>
47
- </g>
48
- </svg>
49
- SVG