class2 0.6.0 → 0.7.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.
- checksums.yaml +4 -4
- data/.github/workflows/ci.yml +25 -0
- data/Changes +8 -0
- data/README.md +31 -3
- data/class2.gemspec +4 -3
- data/lib/class2/autoload/namespaced.rb +1 -1
- data/lib/class2/inflector.rb +177 -0
- data/lib/class2/version.rb +1 -1
- data/lib/class2.rb +26 -17
- metadata +12 -34
- data/Appraisals +0 -11
- data/gemfiles/as4.gemfile +0 -7
- data/gemfiles/as5.gemfile +0 -7
- data/gemfiles/as6.gemfile +0 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70badfe7a9fb6e6ad1df0f513ffc6bd4a322ab559457b62c810c54949b6862f8
|
|
4
|
+
data.tar.gz: c9feafd17d6e2375b868b22e87f4bac8d83db1dc26b538ff7ad13fd36d71d3f0
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 32006d461e4e85a7c7b04c60ce9f388b670d55b3a1607afc8e2ab017b5f771aa949a411505ae7a0822c5a023865bf4e7260d79cd9997e4a73ceaa320b492bf35
|
|
7
|
+
data.tar.gz: ae9e57e7b101a98c2c9bece0ca8d583f6022acc81b5e293a2c0e540d8b65e128d9d2ae589c826c9aeb0fb45fb30534e43f423e9b1207eb5747f724da00ef8633
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: ["**"]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
|
|
12
|
+
strategy:
|
|
13
|
+
fail-fast: false
|
|
14
|
+
matrix:
|
|
15
|
+
ruby: ["2.7", "3.0", "3.1", "3.2", "3.3", "3.4", "4.0"]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v7
|
|
19
|
+
- name: Set up Ruby
|
|
20
|
+
uses: ruby/setup-ruby@v1
|
|
21
|
+
with:
|
|
22
|
+
ruby-version: ${{ matrix.ruby }}
|
|
23
|
+
bundler-cache: true
|
|
24
|
+
- name: Run the default task
|
|
25
|
+
run: bundle exec rake
|
data/Changes
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
2026-09-16 v0.7.0
|
|
2
|
+
--------------------
|
|
3
|
+
* Drop ActiveSupport for inflections and use dry-inflector
|
|
4
|
+
|
|
5
|
+
2026-09-16 v0.6.1
|
|
6
|
+
--------------------
|
|
7
|
+
* Add Class2::Inflector wrapper
|
|
8
|
+
|
|
1
9
|
2024-10-15 v0.6.0
|
|
2
10
|
--------------------
|
|
3
11
|
* Bug fix: autoloading in data section with namespace
|
data/README.md
CHANGED
|
@@ -124,6 +124,32 @@ For more info on accessor formats and JSON see:
|
|
|
124
124
|
* [`Class2::UpperCamelCase`](https://www.rubydoc.info/gems/class2/Class2/UpperCamelCase)
|
|
125
125
|
* [`Class2::LowerCamelCase`](https://www.rubydoc.info/gems/class2/Class2/LowerCamelCase)
|
|
126
126
|
|
|
127
|
+
[Using Ruby-specific JSON extensions](https://github.com/ruby/json?tab=readme-ov-file#usage) you can define
|
|
128
|
+
Ruby types in the JSON class2 will use for type conversion:
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"your_class": {
|
|
133
|
+
"id": 0,
|
|
134
|
+
"name": "string",
|
|
135
|
+
"updated_at": {"json_class":"Time","s":0,"n":0},
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
Then require the appropriate JSON conversion class:
|
|
141
|
+
|
|
142
|
+
```rb
|
|
143
|
+
require "json/add/time"
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
This will result in class2 creating a `MyClass` class with the following:
|
|
147
|
+
|
|
148
|
+
- `#id` returning an `Integer` instance
|
|
149
|
+
- `#name` returning a `String` instance
|
|
150
|
+
- `#updated_at` returning a `Time` instance
|
|
151
|
+
|
|
152
|
+
This will work for any [supported conversions](#conversions).
|
|
127
153
|
|
|
128
154
|
You can also autoload a definition from a DATA section:
|
|
129
155
|
|
|
@@ -165,8 +191,7 @@ To control the creation of the top-level methods, see the
|
|
|
165
191
|
|
|
166
192
|
#### Naming
|
|
167
193
|
|
|
168
|
-
`class2` uses
|
|
169
|
-
[`String#classify`](http://api.rubyonrails.org/classes/String.html#method-i-classify)
|
|
194
|
+
`class2` uses [`dry-inflector`](https://github.com/dry-rb/dry-inflector)
|
|
170
195
|
to turn keys into class names: `:foo` will be `Foo`, `:foo_bars` will
|
|
171
196
|
be `FooBar`.
|
|
172
197
|
|
|
@@ -176,7 +201,9 @@ used to derive the class names from the plural attribute names. An
|
|
|
176
201
|
`:addresses` key with an `Array` value will result in a class named
|
|
177
202
|
`Address` being created.
|
|
178
203
|
|
|
179
|
-
Plurality is determined by
|
|
204
|
+
Plurality is determined by
|
|
205
|
+
[`dry-inflector`](https://github.com/dry-rb/dry-inflector) loaded with
|
|
206
|
+
ActiveSupport 6.1's rules. No `String` methods are added.
|
|
180
207
|
|
|
181
208
|
#### Conversions
|
|
182
209
|
|
|
@@ -191,6 +218,7 @@ You can use any of these classes or their instances in your class definitions:
|
|
|
191
218
|
* `Float`
|
|
192
219
|
* `Hash`
|
|
193
220
|
* `Integer`
|
|
221
|
+
* `Time`
|
|
194
222
|
* `TrueClass`/`FalseClass` - either one will cause a boolean conversion
|
|
195
223
|
|
|
196
224
|
Custom conversions are possible, just add the conversion to
|
data/class2.gemspec
CHANGED
|
@@ -18,10 +18,11 @@ Gem::Specification.new do |spec|
|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
19
19
|
spec.require_paths = ["lib"]
|
|
20
20
|
|
|
21
|
-
spec.
|
|
21
|
+
spec.required_ruby_version = ">= 2.7"
|
|
22
|
+
|
|
23
|
+
spec.add_dependency "dry-inflector", "~> 1.1.0"
|
|
22
24
|
|
|
23
25
|
spec.add_development_dependency "bundler"
|
|
24
|
-
spec.add_development_dependency "rake", "~>
|
|
25
|
-
spec.add_development_dependency "appraisal"
|
|
26
|
+
spec.add_development_dependency "rake", "~> 13"
|
|
26
27
|
spec.add_development_dependency "minitest"
|
|
27
28
|
end
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "dry/inflector"
|
|
4
|
+
|
|
5
|
+
class Class2
|
|
6
|
+
#
|
|
7
|
+
# String inflection used to convert attribute names into class names, method
|
|
8
|
+
# names, and JSON keys.
|
|
9
|
+
#
|
|
10
|
+
# dry-inflector provides the rule engine, but it is loaded with ActiveSupport
|
|
11
|
+
# 6.1's rules and case conversion so that the names Class2 generates are the
|
|
12
|
+
# same as when ActiveSupport provided the inflector. dry-inflector's rules,
|
|
13
|
+
# acronyms (API, JSON, HTTP, ...), and uncountables differ from ActiveSupport's
|
|
14
|
+
# and would change class names and plural attribute detection.
|
|
15
|
+
#
|
|
16
|
+
module Inflector
|
|
17
|
+
extend self
|
|
18
|
+
|
|
19
|
+
# ActiveSupport's uncountables.
|
|
20
|
+
UNCOUNTABLE = %w[
|
|
21
|
+
equipment information rice money species series fish sheep jeans police
|
|
22
|
+
].freeze
|
|
23
|
+
|
|
24
|
+
#
|
|
25
|
+
# Rules are prepended, so the last one added is the first one applied and
|
|
26
|
+
# they are added here in ActiveSupport's order so they have its precedence.
|
|
27
|
+
#
|
|
28
|
+
INFLECTOR = Dry::Inflector.new do |inflections|
|
|
29
|
+
inflections.plural(/$/, "s")
|
|
30
|
+
inflections.plural(/s$/i, "s")
|
|
31
|
+
inflections.plural(/^(ax|test)is$/i, '\1es')
|
|
32
|
+
inflections.plural(/(octop|vir)us$/i, '\1i')
|
|
33
|
+
inflections.plural(/(octop|vir)i$/i, '\1i')
|
|
34
|
+
inflections.plural(/(alias|status)$/i, '\1es')
|
|
35
|
+
inflections.plural(/(bu)s$/i, '\1ses')
|
|
36
|
+
inflections.plural(/(buffal|tomat)o$/i, '\1oes')
|
|
37
|
+
inflections.plural(/([ti])um$/i, '\1a')
|
|
38
|
+
inflections.plural(/([ti])a$/i, '\1a')
|
|
39
|
+
inflections.plural(/sis$/i, "ses")
|
|
40
|
+
inflections.plural(/(?:([^f])fe|([lr])f)$/i, '\1\2ves')
|
|
41
|
+
inflections.plural(/(hive)$/i, '\1s')
|
|
42
|
+
inflections.plural(/([^aeiouy]|qu)y$/i, '\1ies')
|
|
43
|
+
inflections.plural(/(x|ch|ss|sh)$/i, '\1es')
|
|
44
|
+
inflections.plural(/(matr|vert|ind)(?:ix|ex)$/i, '\1ices')
|
|
45
|
+
inflections.plural(/^(m|l)ouse$/i, '\1ice')
|
|
46
|
+
inflections.plural(/^(m|l)ice$/i, '\1ice')
|
|
47
|
+
inflections.plural(/^(ox)$/i, '\1en')
|
|
48
|
+
inflections.plural(/^(oxen)$/i, '\1')
|
|
49
|
+
inflections.plural(/(quiz)$/i, '\1zes')
|
|
50
|
+
|
|
51
|
+
# ActiveSupport leaves a word alone when none of its rules match. These
|
|
52
|
+
# rules have a higher precedence than dry-inflector's defaults, which have
|
|
53
|
+
# irregulars ActiveSupport doesn't (foot/feet, tooth/teeth, goose/geese),
|
|
54
|
+
# so this rule -- the one with the lowest precedence -- matches everything
|
|
55
|
+
# and leaves it alone.
|
|
56
|
+
inflections.singular(/$/, '\0')
|
|
57
|
+
inflections.singular(/s$/i, "")
|
|
58
|
+
inflections.singular(/(ss)$/i, '\1')
|
|
59
|
+
inflections.singular(/(n)ews$/i, '\1ews')
|
|
60
|
+
inflections.singular(/([ti])a$/i, '\1um')
|
|
61
|
+
inflections.singular(/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '\1sis')
|
|
62
|
+
inflections.singular(/(^analy)(sis|ses)$/i, '\1sis')
|
|
63
|
+
inflections.singular(/([^f])ves$/i, '\1fe')
|
|
64
|
+
inflections.singular(/(hive)s$/i, '\1')
|
|
65
|
+
inflections.singular(/(tive)s$/i, '\1')
|
|
66
|
+
inflections.singular(/([lr])ves$/i, '\1f')
|
|
67
|
+
inflections.singular(/([^aeiouy]|qu)ies$/i, '\1y')
|
|
68
|
+
inflections.singular(/(s)eries$/i, '\1eries')
|
|
69
|
+
inflections.singular(/(m)ovies$/i, '\1ovie')
|
|
70
|
+
inflections.singular(/(x|ch|ss|sh)es$/i, '\1')
|
|
71
|
+
inflections.singular(/^(m|l)ice$/i, '\1ouse')
|
|
72
|
+
inflections.singular(/(bus)(es)?$/i, '\1')
|
|
73
|
+
inflections.singular(/(o)es$/i, '\1')
|
|
74
|
+
inflections.singular(/(shoe)s$/i, '\1')
|
|
75
|
+
inflections.singular(/(cris|test)(is|es)$/i, '\1is')
|
|
76
|
+
inflections.singular(/^(a)x[ie]s$/i, '\1xis')
|
|
77
|
+
inflections.singular(/(octop|vir)(us|i)$/i, '\1us')
|
|
78
|
+
inflections.singular(/(alias|status)(es)?$/i, '\1')
|
|
79
|
+
inflections.singular(/^(ox)en/i, '\1')
|
|
80
|
+
inflections.singular(/(vert|ind)ices$/i, '\1ex')
|
|
81
|
+
inflections.singular(/(matr)ices$/i, '\1ix')
|
|
82
|
+
inflections.singular(/(quiz)zes$/i, '\1')
|
|
83
|
+
inflections.singular(/(database)s$/i, '\1')
|
|
84
|
+
|
|
85
|
+
# ActiveSupport adds a rule for both forms of an irregular, so that the
|
|
86
|
+
# plural pluralizes to itself ("people" => "people"). Every word here has
|
|
87
|
+
# the same first letter in both forms, which is all ActiveSupport's rules
|
|
88
|
+
# for this case need.
|
|
89
|
+
{
|
|
90
|
+
"person" => "people",
|
|
91
|
+
"man" => "men",
|
|
92
|
+
"child" => "children",
|
|
93
|
+
"sex" => "sexes",
|
|
94
|
+
"move" => "moves",
|
|
95
|
+
"zombie" => "zombies"
|
|
96
|
+
}.each do |singular, plural|
|
|
97
|
+
s0, srest = singular[0], singular[1..-1]
|
|
98
|
+
p0, prest = plural[0], plural[1..-1]
|
|
99
|
+
|
|
100
|
+
inflections.plural(/(#{s0})#{srest}$/i, "\\1#{prest}")
|
|
101
|
+
inflections.plural(/(#{p0})#{prest}$/i, "\\1#{prest}")
|
|
102
|
+
|
|
103
|
+
inflections.singular(/(#{s0})#{srest}$/i, "\\1#{srest}")
|
|
104
|
+
inflections.singular(/(#{p0})#{prest}$/i, "\\1#{srest}")
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
# dry-inflector considers a word uncountable if any of its
|
|
108
|
+
# underscore-separated segments is ("user_money"), and its list has words
|
|
109
|
+
# ActiveSupport's doesn't (moose, deer, ...). Drop it and use #uncountable?
|
|
110
|
+
# below instead.
|
|
111
|
+
inflections.uncountables.clear
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def pluralize(word)
|
|
115
|
+
word = word.to_s
|
|
116
|
+
uncountable?(word) ? word : INFLECTOR.pluralize(word)
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def singularize(word)
|
|
120
|
+
word = word.to_s
|
|
121
|
+
uncountable?(word) ? word : INFLECTOR.singularize(word)
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
#
|
|
125
|
+
# Convert a table, attribute, or method name into a class name.
|
|
126
|
+
# "user_statuses" => "UserStatus", "admin/user" => "Admin::User".
|
|
127
|
+
#
|
|
128
|
+
def classify(word)
|
|
129
|
+
camelize(singularize(word.to_s.sub(/.*\./, "")))
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
#
|
|
133
|
+
# "some_value" => "SomeValue", "some_value", true => "someValue".
|
|
134
|
+
# "/" becomes "::".
|
|
135
|
+
#
|
|
136
|
+
# Unlike ActiveSupport::Inflector#camelize this has no acronym support, so
|
|
137
|
+
# there's no need to pass a symbol to get "lower" camel case.
|
|
138
|
+
#
|
|
139
|
+
def camelize(word, lower = false)
|
|
140
|
+
word = word.to_s.dup
|
|
141
|
+
|
|
142
|
+
if lower
|
|
143
|
+
word.sub!(/^\w/) { |c| c.downcase }
|
|
144
|
+
else
|
|
145
|
+
word.sub!(/^[a-z\d]*/) { |c| c.capitalize }
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
word.gsub!(/(?:_|(\/))([a-z\d]*)/i) { "#{$1}#{$2.capitalize}" }
|
|
149
|
+
word.gsub!("/", "::")
|
|
150
|
+
word
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
#
|
|
154
|
+
# "SomeValue" => "some_value", "APIClient" => "api_client",
|
|
155
|
+
# "Admin::User" => "admin/user".
|
|
156
|
+
#
|
|
157
|
+
def underscore(word)
|
|
158
|
+
word = word.to_s.dup
|
|
159
|
+
return word unless word =~ /[A-Z-]|::/
|
|
160
|
+
|
|
161
|
+
word.gsub!("::", "/")
|
|
162
|
+
word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2')
|
|
163
|
+
word.gsub!(/([a-z\d])([A-Z])/, '\1_\2')
|
|
164
|
+
word.tr!("-", "_")
|
|
165
|
+
word.downcase!
|
|
166
|
+
word
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
private
|
|
170
|
+
|
|
171
|
+
# ActiveSupport only considers the whole word, e.g. "police" is uncountable
|
|
172
|
+
# but "military_police" is not.
|
|
173
|
+
def uncountable?(word)
|
|
174
|
+
UNCOUNTABLE.any? { |uncountable| /\b#{Regexp.escape(uncountable)}\Z/i =~ word }
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
end
|
data/lib/class2/version.rb
CHANGED
data/lib/class2.rb
CHANGED
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
require "date"
|
|
4
4
|
require "time" # for parse()
|
|
5
5
|
require "json"
|
|
6
|
-
require "
|
|
7
|
-
require "active_support/inflector"
|
|
6
|
+
require "rbconfig" # for autoload()
|
|
8
7
|
|
|
9
8
|
require "class2/version"
|
|
9
|
+
require "class2/inflector"
|
|
10
10
|
|
|
11
11
|
no_export = ENV["CLASS2_NO_EXPORT"]
|
|
12
12
|
|
|
@@ -61,9 +61,17 @@ class Class2
|
|
|
61
61
|
|
|
62
62
|
def autoload(namespace = Object, stack = nil) # :nodoc:
|
|
63
63
|
failure = lambda { |message| abort "class2: cannot autoload class definitions: #{message}" }
|
|
64
|
+
#
|
|
65
|
+
# Ignore our autoload file and require()'s internals. Both their file
|
|
66
|
+
# names and the backtrace format change between Rubies: kernel_require.rb
|
|
67
|
+
# and bundled_gems.rb (3.3+) are where the frames come from, and since
|
|
68
|
+
# 3.4 frames are quoted with ' instead of `. Some Rubies report the
|
|
69
|
+
# rubygems frames as <internal:...>, which is not a readable path.
|
|
70
|
+
#
|
|
71
|
+
libdir = RbConfig::CONFIG["rubylibdir"]
|
|
64
72
|
failure["cannot find the right caller"] unless (stack || caller).find do |line|
|
|
65
|
-
|
|
66
|
-
|
|
73
|
+
line =~ /(.+):\d+:in\s+[`']\S/ && $1.index("/class2/autoload.rb").nil? &&
|
|
74
|
+
!$1.start_with?(libdir, "<internal:")
|
|
67
75
|
end
|
|
68
76
|
|
|
69
77
|
# Give this precedence over global DATA constant
|
|
@@ -135,7 +143,7 @@ class Class2
|
|
|
135
143
|
object.each { |klass, attrs| make_class(namespace, klass, attrs, block) }
|
|
136
144
|
end
|
|
137
145
|
|
|
138
|
-
name =
|
|
146
|
+
name = Inflector.classify(name)
|
|
139
147
|
return if namespace.const_defined?(name, false)
|
|
140
148
|
|
|
141
149
|
make_method_name = lambda { |x| x.to_s.gsub(/[^\w]+/, "_") } # good enough
|
|
@@ -224,7 +232,7 @@ class Class2
|
|
|
224
232
|
method = make_method_name[method]
|
|
225
233
|
attr_writer method
|
|
226
234
|
|
|
227
|
-
retval = method ==
|
|
235
|
+
retval = method == Inflector.pluralize(method) ? "[]" : "#{namespace}::#{Inflector.classify(method)}.new"
|
|
228
236
|
class_eval <<-CODE
|
|
229
237
|
def #{method}
|
|
230
238
|
@#{method} ||= #{retval}
|
|
@@ -249,10 +257,11 @@ class Class2
|
|
|
249
257
|
if self.class.__nested_attributes.include?(key.respond_to?(:to_sym) ? key.to_sym : key) &&
|
|
250
258
|
(value.is_a?(Hash) || value.is_a?(Array))
|
|
251
259
|
|
|
252
|
-
name =
|
|
260
|
+
name = Inflector.classify(key)
|
|
253
261
|
|
|
254
|
-
# parent
|
|
255
|
-
|
|
262
|
+
# Neither Module#parent nor #module_parent, both ActiveSupport additions.
|
|
263
|
+
# For a class with no name this is Object, like #module_parent.
|
|
264
|
+
parent = self.class.name.to_s.split("::")[0..-2].inject(Object) { |mod, c| mod.const_get(c) }
|
|
256
265
|
next unless parent.const_defined?(name)
|
|
257
266
|
|
|
258
267
|
klass = parent.const_get(name)
|
|
@@ -293,7 +302,7 @@ class Class2
|
|
|
293
302
|
module UpperCamelCase
|
|
294
303
|
module Attributes
|
|
295
304
|
def self.included(klass)
|
|
296
|
-
Util.convert_attributes(klass) { |v|
|
|
305
|
+
Util.convert_attributes(klass) { |v| Inflector.camelize(v) }
|
|
297
306
|
end
|
|
298
307
|
end
|
|
299
308
|
|
|
@@ -314,13 +323,13 @@ class Class2
|
|
|
314
323
|
module LowerCamelCase
|
|
315
324
|
module Attributes
|
|
316
325
|
def self.included(klass)
|
|
317
|
-
Util.convert_attributes(klass) { |v|
|
|
326
|
+
Util.convert_attributes(klass) { |v| Inflector.camelize(v, true) }
|
|
318
327
|
end
|
|
319
328
|
end
|
|
320
329
|
|
|
321
330
|
module JSON
|
|
322
331
|
def as_json(*)
|
|
323
|
-
Util.as_json(self, :camelize,
|
|
332
|
+
Util.as_json(self, :camelize, true)
|
|
324
333
|
end
|
|
325
334
|
|
|
326
335
|
def to_json(*argz)
|
|
@@ -343,7 +352,7 @@ class Class2
|
|
|
343
352
|
#
|
|
344
353
|
module Attributes
|
|
345
354
|
def self.included(klass)
|
|
346
|
-
Util.convert_attributes(klass) { |v|
|
|
355
|
+
Util.convert_attributes(klass) { |v| Inflector.underscore(v) }
|
|
347
356
|
end
|
|
348
357
|
end
|
|
349
358
|
|
|
@@ -363,18 +372,18 @@ class Class2
|
|
|
363
372
|
end
|
|
364
373
|
|
|
365
374
|
module Util
|
|
366
|
-
def self.as_json(klass, *argz)
|
|
375
|
+
def self.as_json(klass, method, *argz)
|
|
367
376
|
hash = {}
|
|
368
377
|
klass.to_h.each do |k, v|
|
|
369
378
|
if v.is_a?(Hash)
|
|
370
|
-
v = as_json(v, *argz)
|
|
379
|
+
v = as_json(v, method, *argz)
|
|
371
380
|
elsif v.is_a?(Array)
|
|
372
|
-
v = v.map { |e| as_json(e, *argz) }
|
|
381
|
+
v = v.map { |e| as_json(e, method, *argz) }
|
|
373
382
|
elsif v.respond_to?(:as_json)
|
|
374
383
|
v = v.as_json
|
|
375
384
|
end
|
|
376
385
|
|
|
377
|
-
hash[k.to_s
|
|
386
|
+
hash[Inflector.public_send(method, k.to_s, *argz)] = v
|
|
378
387
|
end
|
|
379
388
|
|
|
380
389
|
hash
|
metadata
CHANGED
|
@@ -1,35 +1,29 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: class2
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.7.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Skye Shaw
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
14
|
+
name: dry-inflector
|
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
|
16
16
|
requirements:
|
|
17
|
-
- - "
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '3.2'
|
|
20
|
-
- - "<"
|
|
17
|
+
- - "~>"
|
|
21
18
|
- !ruby/object:Gem::Version
|
|
22
|
-
version:
|
|
19
|
+
version: 1.1.0
|
|
23
20
|
type: :runtime
|
|
24
21
|
prerelease: false
|
|
25
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
26
23
|
requirements:
|
|
27
|
-
- - "
|
|
28
|
-
- !ruby/object:Gem::Version
|
|
29
|
-
version: '3.2'
|
|
30
|
-
- - "<"
|
|
24
|
+
- - "~>"
|
|
31
25
|
- !ruby/object:Gem::Version
|
|
32
|
-
version:
|
|
26
|
+
version: 1.1.0
|
|
33
27
|
- !ruby/object:Gem::Dependency
|
|
34
28
|
name: bundler
|
|
35
29
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -50,28 +44,14 @@ dependencies:
|
|
|
50
44
|
requirements:
|
|
51
45
|
- - "~>"
|
|
52
46
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '
|
|
47
|
+
version: '13'
|
|
54
48
|
type: :development
|
|
55
49
|
prerelease: false
|
|
56
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
57
51
|
requirements:
|
|
58
52
|
- - "~>"
|
|
59
53
|
- !ruby/object:Gem::Version
|
|
60
|
-
version: '
|
|
61
|
-
- !ruby/object:Gem::Dependency
|
|
62
|
-
name: appraisal
|
|
63
|
-
requirement: !ruby/object:Gem::Requirement
|
|
64
|
-
requirements:
|
|
65
|
-
- - ">="
|
|
66
|
-
- !ruby/object:Gem::Version
|
|
67
|
-
version: '0'
|
|
68
|
-
type: :development
|
|
69
|
-
prerelease: false
|
|
70
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
71
|
-
requirements:
|
|
72
|
-
- - ">="
|
|
73
|
-
- !ruby/object:Gem::Version
|
|
74
|
-
version: '0'
|
|
54
|
+
version: '13'
|
|
75
55
|
- !ruby/object:Gem::Dependency
|
|
76
56
|
name: minitest
|
|
77
57
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -93,20 +73,18 @@ executables: []
|
|
|
93
73
|
extensions: []
|
|
94
74
|
extra_rdoc_files: []
|
|
95
75
|
files:
|
|
76
|
+
- ".github/workflows/ci.yml"
|
|
96
77
|
- ".gitignore"
|
|
97
|
-
- Appraisals
|
|
98
78
|
- Changes
|
|
99
79
|
- Gemfile
|
|
100
80
|
- LICENSE.txt
|
|
101
81
|
- README.md
|
|
102
82
|
- Rakefile
|
|
103
83
|
- class2.gemspec
|
|
104
|
-
- gemfiles/as4.gemfile
|
|
105
|
-
- gemfiles/as5.gemfile
|
|
106
|
-
- gemfiles/as6.gemfile
|
|
107
84
|
- lib/class2.rb
|
|
108
85
|
- lib/class2/autoload.rb
|
|
109
86
|
- lib/class2/autoload/namespaced.rb
|
|
87
|
+
- lib/class2/inflector.rb
|
|
110
88
|
- lib/class2/version.rb
|
|
111
89
|
homepage: https://github.com/sshaw/class2
|
|
112
90
|
licenses:
|
|
@@ -120,7 +98,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
120
98
|
requirements:
|
|
121
99
|
- - ">="
|
|
122
100
|
- !ruby/object:Gem::Version
|
|
123
|
-
version: '
|
|
101
|
+
version: '2.7'
|
|
124
102
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
103
|
requirements:
|
|
126
104
|
- - ">="
|
data/Appraisals
DELETED
data/gemfiles/as4.gemfile
DELETED
data/gemfiles/as5.gemfile
DELETED