object_identifier 0.4.0 → 0.5.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 +38 -0
- data/.rubocop.yml +28 -6
- data/CHANGELOG.md +9 -0
- data/Gemfile.lock +37 -31
- data/LICENSE.txt +1 -1
- data/README.md +36 -3
- data/lib/core_ext/object.rb +2 -2
- data/lib/object_identifier/formatters/string_formatter.rb +103 -0
- data/lib/object_identifier/identifier.rb +16 -155
- data/lib/object_identifier/version.rb +1 -1
- data/lib/object_identifier.rb +53 -0
- data/object_identifier.gemspec +5 -2
- data/scripts/benchmarking/formatters.rb +77 -0
- metadata +38 -7
- data/.travis.yml +0 -19
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4553678db77484aa04ce8bf00e3238310bb253632fb8867cae4e948a35bfda6c
|
4
|
+
data.tar.gz: afae645239815d3a9a011b56892127eccbf0dbc9558fc59ed6d7c15349fdbbe7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 639af898d48d62cfdf69c325986fab12147ddad81219b0e60557a9e52288597d5dddd3f4473ac94df3bbfd5ade304bb55c3f1a49b9918f4c959941ba4899f773
|
7
|
+
data.tar.gz: 9f715f65e2afa4ac783db81cd8cab09b137b206dae15d4b05e757dc2d0c82e04848dbd2551e02b8a7c6127aed3b5cb5257c920e8d79f027cf42d1b61ae238679
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# This workflow uses actions that are not certified by GitHub.
|
2
|
+
# They are provided by a third-party and are governed by
|
3
|
+
# separate terms of service, privacy policy, and support
|
4
|
+
# documentation.
|
5
|
+
# This workflow will download a prebuilt Ruby version, install dependencies and run tests with Rake
|
6
|
+
# For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
|
7
|
+
|
8
|
+
name: Ruby
|
9
|
+
|
10
|
+
on:
|
11
|
+
push:
|
12
|
+
branches: [ "master" ]
|
13
|
+
pull_request:
|
14
|
+
branches: [ "master" ]
|
15
|
+
|
16
|
+
permissions:
|
17
|
+
contents: read
|
18
|
+
|
19
|
+
jobs:
|
20
|
+
test:
|
21
|
+
runs-on: ubuntu-latest
|
22
|
+
strategy:
|
23
|
+
matrix:
|
24
|
+
ruby-version: ['2.7', '3.0', '3.1', '3.2']
|
25
|
+
|
26
|
+
steps:
|
27
|
+
- uses: actions/checkout@v3
|
28
|
+
- name: Set up Ruby
|
29
|
+
# To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
|
30
|
+
# change this to (see https://github.com/ruby/setup-ruby#versioning):
|
31
|
+
# uses: ruby/setup-ruby@v1
|
32
|
+
uses: ruby/setup-ruby@ee2113536afb7f793eed4ce60e8d3b26db912da4 # v1.127.0
|
33
|
+
with:
|
34
|
+
ruby-version: ${{ matrix.ruby-version }}
|
35
|
+
bundler-cache: true # runs 'bundle install' and caches installed gems automatically
|
36
|
+
|
37
|
+
- name: Run tests
|
38
|
+
run: bundle exec rake
|
data/.rubocop.yml
CHANGED
@@ -24,6 +24,18 @@ Layout/FirstHashElementIndentation:
|
|
24
24
|
Layout/FirstParameterIndentation:
|
25
25
|
Enabled: false # Revisit if more settings become available.
|
26
26
|
|
27
|
+
Layout/LineContinuationSpacing:
|
28
|
+
EnforcedStyle: no_space
|
29
|
+
|
30
|
+
Layout/LineEndStringConcatenationIndentation:
|
31
|
+
EnforcedStyle: aligned
|
32
|
+
|
33
|
+
Layout/LineLength:
|
34
|
+
Max: 80
|
35
|
+
Exclude:
|
36
|
+
- "object_identifier.gemspec"
|
37
|
+
- "scripts/benchmarking/**/*"
|
38
|
+
|
27
39
|
Layout/MultilineMethodCallBraceLayout:
|
28
40
|
EnforcedStyle: same_line
|
29
41
|
|
@@ -42,11 +54,15 @@ Lint/AmbiguousOperator:
|
|
42
54
|
Lint/AmbiguousRegexpLiteral:
|
43
55
|
Enabled: false # Conflicts with other rules.
|
44
56
|
|
57
|
+
Lint/OrAssignmentToConstant:
|
58
|
+
Exclude:
|
59
|
+
- "scripts/**/*"
|
60
|
+
|
45
61
|
Lint/Void:
|
46
62
|
CheckForMethodsWithNoSideEffects: true
|
47
63
|
|
48
64
|
Metrics/BlockLength:
|
49
|
-
|
65
|
+
AllowedMethods:
|
50
66
|
- describe
|
51
67
|
- context
|
52
68
|
- ips # Benchmarking
|
@@ -55,11 +71,6 @@ Metrics/ClassLength:
|
|
55
71
|
Exclude:
|
56
72
|
- "test/**/*"
|
57
73
|
|
58
|
-
Metrics/LineLength:
|
59
|
-
Max: 80
|
60
|
-
Exclude:
|
61
|
-
- "object_identifier.gemspec"
|
62
|
-
|
63
74
|
Naming/MethodParameterName:
|
64
75
|
AllowedNames:
|
65
76
|
- a
|
@@ -71,6 +82,10 @@ Style/Alias:
|
|
71
82
|
Style/BlockDelimiters:
|
72
83
|
Enabled: false # Reconsider later.
|
73
84
|
|
85
|
+
Style/ClassAndModuleChildren:
|
86
|
+
AutoCorrect: true
|
87
|
+
EnforcedStyle: compact
|
88
|
+
|
74
89
|
Style/EmptyElse:
|
75
90
|
# It"s helpful to show intent by including a comment in an else block.
|
76
91
|
Enabled: false
|
@@ -88,6 +103,13 @@ Style/FormatString:
|
|
88
103
|
Style/Lambda:
|
89
104
|
EnforcedStyle: literal
|
90
105
|
|
106
|
+
Style/LambdaCall:
|
107
|
+
Enabled: false # Allow ServiceObject.(*). Only use on classes, not instances.
|
108
|
+
|
109
|
+
Style/OpenStructUse:
|
110
|
+
Exclude:
|
111
|
+
- "test/**/*"
|
112
|
+
|
91
113
|
Style/RegexpLiteral:
|
92
114
|
EnforcedStyle: mixed
|
93
115
|
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
### 0.5.0 - 2023-01-04
|
2
|
+
- Add support for defining customer Formatters.
|
3
|
+
- Add ObjectInspector::Configuration#formatter_class setting for overriding the default Formatter. See the README for more.
|
4
|
+
- Add a benchmarking script for comparing performance of formatters. See the README for more.
|
5
|
+
|
6
|
+
### 0.4.1 - 2022-12-30
|
7
|
+
- Make compatible with Ruby 3.2 (and likely Ruby 3.0 and 3.1 as well).
|
8
|
+
- Update development dependencies.
|
9
|
+
|
1
10
|
### 0.4.0 - 2020-09-01
|
2
11
|
- [#4](https://github.com/pdobb/object_identifier/pull/4) Only show attribute names if identifying more than one attribute.
|
3
12
|
- Update development dependencies.
|
data/Gemfile.lock
CHANGED
@@ -1,70 +1,76 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
object_identifier (0.
|
4
|
+
object_identifier (0.5.0)
|
5
5
|
|
6
6
|
GEM
|
7
7
|
remote: https://rubygems.org/
|
8
8
|
specs:
|
9
9
|
ansi (1.5.0)
|
10
|
-
ast (2.4.
|
10
|
+
ast (2.4.2)
|
11
|
+
benchmark-ips (2.10.0)
|
11
12
|
builder (3.2.4)
|
12
13
|
byebug (11.1.3)
|
13
14
|
coderay (1.1.3)
|
14
|
-
docile (1.
|
15
|
+
docile (1.4.0)
|
16
|
+
json (2.6.3)
|
15
17
|
kwalify (0.7.2)
|
16
18
|
method_source (1.0.0)
|
17
|
-
minitest (5.
|
18
|
-
minitest-reporters (1.
|
19
|
+
minitest (5.17.0)
|
20
|
+
minitest-reporters (1.5.0)
|
19
21
|
ansi
|
20
22
|
builder
|
21
23
|
minitest (>= 5.0)
|
22
24
|
ruby-progressbar
|
23
|
-
|
24
|
-
|
25
|
+
much-stub (0.1.10)
|
26
|
+
parallel (1.22.1)
|
27
|
+
parser (3.1.3.0)
|
25
28
|
ast (~> 2.4.1)
|
26
|
-
pry (0.
|
29
|
+
pry (0.14.1)
|
27
30
|
coderay (~> 1.1)
|
28
31
|
method_source (~> 1.0)
|
29
|
-
pry-byebug (3.
|
32
|
+
pry-byebug (3.10.1)
|
30
33
|
byebug (~> 11.0)
|
31
|
-
pry (
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
reek (6.0.1)
|
34
|
+
pry (>= 0.13, < 0.15)
|
35
|
+
rainbow (3.1.1)
|
36
|
+
rake (13.0.6)
|
37
|
+
reek (6.1.3)
|
36
38
|
kwalify (~> 0.7.0)
|
37
|
-
parser (
|
38
|
-
psych (~> 3.1.0)
|
39
|
+
parser (~> 3.1.0)
|
39
40
|
rainbow (>= 2.0, < 4.0)
|
40
|
-
regexp_parser (
|
41
|
-
rexml (3.2.
|
42
|
-
rubocop (
|
41
|
+
regexp_parser (2.6.1)
|
42
|
+
rexml (3.2.5)
|
43
|
+
rubocop (1.42.0)
|
44
|
+
json (~> 2.3)
|
43
45
|
parallel (~> 1.10)
|
44
|
-
parser (>=
|
46
|
+
parser (>= 3.1.2.1)
|
45
47
|
rainbow (>= 2.2.2, < 4.0)
|
46
|
-
regexp_parser (>= 1.
|
47
|
-
rexml
|
48
|
-
rubocop-ast (>=
|
48
|
+
regexp_parser (>= 1.8, < 3.0)
|
49
|
+
rexml (>= 3.2.5, < 4.0)
|
50
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
49
51
|
ruby-progressbar (~> 1.7)
|
50
|
-
unicode-display_width (>= 1.4.0, <
|
51
|
-
rubocop-ast (
|
52
|
-
parser (>=
|
53
|
-
ruby-progressbar (1.
|
54
|
-
simplecov (0.
|
52
|
+
unicode-display_width (>= 1.4.0, < 3.0)
|
53
|
+
rubocop-ast (1.24.1)
|
54
|
+
parser (>= 3.1.1.0)
|
55
|
+
ruby-progressbar (1.11.0)
|
56
|
+
simplecov (0.22.0)
|
55
57
|
docile (~> 1.1)
|
56
58
|
simplecov-html (~> 0.11)
|
57
|
-
|
58
|
-
|
59
|
+
simplecov_json_formatter (~> 0.1)
|
60
|
+
simplecov-html (0.12.3)
|
61
|
+
simplecov_json_formatter (0.1.4)
|
62
|
+
unicode-display_width (2.3.0)
|
59
63
|
|
60
64
|
PLATFORMS
|
61
65
|
ruby
|
62
66
|
|
63
67
|
DEPENDENCIES
|
68
|
+
benchmark-ips
|
64
69
|
bundler
|
65
70
|
byebug
|
66
71
|
minitest
|
67
72
|
minitest-reporters
|
73
|
+
much-stub
|
68
74
|
object_identifier!
|
69
75
|
pry
|
70
76
|
pry-byebug
|
@@ -74,4 +80,4 @@ DEPENDENCIES
|
|
74
80
|
simplecov
|
75
81
|
|
76
82
|
BUNDLED WITH
|
77
|
-
2.1
|
83
|
+
2.4.1
|
data/LICENSE.txt
CHANGED
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Object Identifier
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/object_identifier)
|
4
|
-
[](https://travis-ci.org/pdobb/object_identifier)
|
5
|
-
[](https://codeclimate.com/github/pdobb/object_identifier/test_coverage)
|
6
4
|
[](https://codeclimate.com/github/pdobb/object_identifier/maintainability)
|
7
5
|
|
8
6
|
Object Identifier allows quick, easy, and uniform identification of an object by inspecting its class name and outputting any desirable attributes/methods. It is great for logging, sending descriptive notification messages, etc.
|
@@ -40,11 +38,27 @@ Or install it yourself as:
|
|
40
38
|
## Compatibility
|
41
39
|
|
42
40
|
Tested MRI Ruby Versions:
|
41
|
+
* 3.2.0
|
43
42
|
* 2.4.9
|
44
43
|
* 2.5.8
|
45
44
|
* 2.6.6
|
46
45
|
|
47
46
|
|
47
|
+
## Configuration
|
48
|
+
|
49
|
+
Global/default values for ObjectIdentifier can be configured via the ObjectIdentifier::Configuration object.
|
50
|
+
|
51
|
+
_Note: In a Rails app, the following would go in e.g. `config/initializers/object_identifier.rb`_
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
# Default values are shown.
|
55
|
+
ObjectIdentifier.configure do |config|
|
56
|
+
config.formatter_class = ObjectIdentifier::StringFormatter
|
57
|
+
config.default_attributes = %i[id]
|
58
|
+
end
|
59
|
+
```
|
60
|
+
|
61
|
+
|
48
62
|
## Usage
|
49
63
|
|
50
64
|
### Defaults
|
@@ -152,11 +166,30 @@ OpenStruct.new(my_value: my_value_object).identify(:my_value)
|
|
152
166
|
ObjectIdentifier works great with the [ObjectInspector](https://github.com/pdobb/object_inspector) gem.
|
153
167
|
|
154
168
|
|
169
|
+
### Benchmarking Formatters
|
170
|
+
|
171
|
+
Performance of Formatters can be tested by playing the [Formatters Benchmarking Scripts](https://github.com/pdobb/object_identifier/tree/master/scripts/benchmarking) in the pry console for this gem.
|
172
|
+
|
173
|
+
Custom Formatters may be similarly gauged for comparison by adding them to the `custom_formatter_klasses` array before playing the script.
|
174
|
+
|
175
|
+
```ruby
|
176
|
+
custom_formatter_klasses = [MyCustomFormatter]
|
177
|
+
|
178
|
+
play scripts/benchmarking/formatters.rb
|
179
|
+
# ObjectIdentifier::StringFormatter
|
180
|
+
# 58.478k (± 0.8%) i/s - 295.776k in 5.058178s
|
181
|
+
# MyCustomFormatter
|
182
|
+
# ...
|
183
|
+
```
|
184
|
+
|
185
|
+
|
155
186
|
## Development
|
156
187
|
|
157
188
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
158
189
|
|
159
|
-
To install this gem onto your local machine, run `bundle exec rake install`.
|
190
|
+
To install this gem onto your local machine, run `bundle exec rake install`.
|
191
|
+
|
192
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
160
193
|
|
161
194
|
|
162
195
|
## Contributing
|
data/lib/core_ext/object.rb
CHANGED
@@ -39,7 +39,7 @@ class Object
|
|
39
39
|
#
|
40
40
|
# (1..10).to_a.identify(:to_f, limit: 2)
|
41
41
|
# # => "Integer[to_f:1.0], Integer[to_f:2.0], ... (8 more)"
|
42
|
-
def identify(*args)
|
43
|
-
ObjectIdentifier::Identifier.
|
42
|
+
def identify(*args, **kargs)
|
43
|
+
ObjectIdentifier::Identifier.(self, *args, **kargs)
|
44
44
|
end
|
45
45
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# ObjectIdentifier::StringFormatter builds a String to identify the
|
4
|
+
# given object(s).
|
5
|
+
class ObjectIdentifier::StringFormatter
|
6
|
+
NO_OBJECTS_INDICATOR = "[no objects]"
|
7
|
+
KLASS_NOT_GIVEN = "NOT_GIVEN"
|
8
|
+
|
9
|
+
def self.call(objects, *attributes, **kargs)
|
10
|
+
new(objects, *attributes, **kargs).call
|
11
|
+
end
|
12
|
+
|
13
|
+
# @param objects [Object, [Object, ...]] the object(s) to be interrogated for
|
14
|
+
# String values to be added to the output String
|
15
|
+
# @param attributes [Array, *args] a list of method calls to interrogate the
|
16
|
+
# given object(s) with
|
17
|
+
# @param limit [Integer, nil] a given limit on the number of objects to
|
18
|
+
# interrogate
|
19
|
+
# @param klass [String, Symbol] a preferred type name for identifying the
|
20
|
+
# given object(s) as
|
21
|
+
def initialize(
|
22
|
+
objects,
|
23
|
+
attributes = ObjectIdentifier::Identifier.default_attributes,
|
24
|
+
limit: nil,
|
25
|
+
klass: KLASS_NOT_GIVEN)
|
26
|
+
@objects = ObjectIdentifier::ArrayWrap.(objects)
|
27
|
+
@attributes = ObjectIdentifier::ArrayWrap.(attributes)
|
28
|
+
@limit = (limit || @objects.size).to_i
|
29
|
+
@klass = klass.to_s
|
30
|
+
end
|
31
|
+
|
32
|
+
# Output the self-identifying string for the given object(s). Will either
|
33
|
+
# return a single object representation or a list of object
|
34
|
+
# representations, based on the number of objects we're identifying.
|
35
|
+
#
|
36
|
+
# @return [String] a string that identifies the object(s)
|
37
|
+
def call
|
38
|
+
return NO_OBJECTS_INDICATOR if @objects.empty?
|
39
|
+
|
40
|
+
output_strings = @objects.first(@limit).map { |obj| format(obj) }
|
41
|
+
output_strings << "... (#{truncated_objects_count} more)" if truncated?
|
42
|
+
output_strings.join(", ")
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def format(object)
|
48
|
+
return NO_OBJECTS_INDICATOR if blank?(object)
|
49
|
+
|
50
|
+
"#{class_name(object)}[#{format_attributes(evaluate_attributes(object))}]"
|
51
|
+
end
|
52
|
+
|
53
|
+
# Simple version of Rails' Object#blank? method.
|
54
|
+
def blank?(object)
|
55
|
+
object.nil? || object == [] || object == {}
|
56
|
+
end
|
57
|
+
|
58
|
+
def class_name(object)
|
59
|
+
klass_given? ? @klass : object.class.name
|
60
|
+
end
|
61
|
+
|
62
|
+
def klass_given?
|
63
|
+
@klass != KLASS_NOT_GIVEN
|
64
|
+
end
|
65
|
+
|
66
|
+
def format_attributes(attributes_hash)
|
67
|
+
return if attributes_hash.empty?
|
68
|
+
|
69
|
+
attributes_formatter = determine_attributes_formatter(attributes_hash)
|
70
|
+
attributes_hash.map(&attributes_formatter).join(", ")
|
71
|
+
end
|
72
|
+
|
73
|
+
def determine_attributes_formatter(attributes_hash)
|
74
|
+
if attributes_hash.one?
|
75
|
+
->(_key, value) { value.inspect_lit }
|
76
|
+
else
|
77
|
+
->(key, value) { "#{key}:#{value.inspect_lit}" }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
# @return [Hash]
|
82
|
+
def evaluate_attributes(object)
|
83
|
+
@attributes.each_with_object({}) { |key, acc|
|
84
|
+
if object.respond_to?(key, :include_private)
|
85
|
+
acc[key] = object.send(key)
|
86
|
+
elsif key.to_s.start_with?("@")
|
87
|
+
acc[key] = object.instance_variable_get(key)
|
88
|
+
end
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def truncated_objects_count
|
93
|
+
@truncated_objects_count ||= objects_count - @limit
|
94
|
+
end
|
95
|
+
|
96
|
+
def objects_count
|
97
|
+
@objects_count ||= @objects.size
|
98
|
+
end
|
99
|
+
|
100
|
+
def truncated?
|
101
|
+
truncated_objects_count.positive?
|
102
|
+
end
|
103
|
+
end
|
@@ -1,161 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
# object or for each object in this collection
|
15
|
-
# @overload self.identify(obj, *args, options)
|
16
|
-
# @param obj [Object] the object to identify
|
17
|
-
# @param args [*] (optional) (default :id) a list of arguments to identify
|
18
|
-
# for this object
|
19
|
-
# @param [Hash] options the options for building a customized
|
20
|
-
# self-identifier
|
21
|
-
# @option options [String, nil] :klass object class name override
|
22
|
-
# @option options [Integer] :limit maximum number of objects to display
|
23
|
-
# from a collection
|
24
|
-
#
|
25
|
-
# @return [String] a self-identifying string like `Class[id:1, name:'temp']`
|
26
|
-
#
|
27
|
-
# @example
|
28
|
-
# ObjectIdentifier::Identifier.identify(
|
29
|
-
# OpenStruct.new(a: 1, b: '2', c: :"3"), :a, :b, :c)
|
30
|
-
# # => "OpenStruct[a:1, b:\"2\", c::\"3\"]"
|
31
|
-
#
|
32
|
-
# ObjectIdentifier::Identifier.identify(1, :to_s)
|
33
|
-
# # => "Integer[to_s:\"1\"]"
|
34
|
-
# ObjectIdentifier::Identifier.identify(nil)
|
35
|
-
# # => "[no objects]"
|
36
|
-
#
|
37
|
-
# ObjectIdentifier::Identifier.identify(%w(1 2), :to_i, :to_f)
|
38
|
-
# # => "String[to_i:1, to_f:1.0], String[to_i:2, to_f:2.0]"
|
39
|
-
#
|
40
|
-
# ObjectIdentifier::Identifier.identify((1..10).to_a, :to_f, limit: 2)
|
41
|
-
# # => "Integer[to_f:1.0], Integer[to_f:2.0], ... (8 more)"
|
42
|
-
def self.identify(object, *args)
|
43
|
-
new(object, *args).to_s
|
44
|
-
end
|
45
|
-
|
46
|
-
def initialize(objects, *args, limit: nil, klass: :not_given)
|
47
|
-
@objects = ArrayWrap.call(objects)
|
48
|
-
@attributes = args.empty? ? [:id] : args
|
49
|
-
@limit = (limit || @objects.size).to_i
|
50
|
-
@klass = klass
|
51
|
-
end
|
52
|
-
|
53
|
-
# Output the self-identifying string for an instance of
|
54
|
-
# ObjectIdentifier::Identifier. Will either return a single object
|
55
|
-
# representation or a list of object representations, based on the number of
|
56
|
-
# objects we're identifying.
|
57
|
-
#
|
58
|
-
# @return [String] a string representing the object or list of objects
|
59
|
-
def to_s
|
60
|
-
if many?
|
61
|
-
format_multiple_objects
|
62
|
-
else
|
63
|
-
format_single_object
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
private
|
68
|
-
|
69
|
-
def format_multiple_objects
|
70
|
-
objects =
|
71
|
-
@objects.first(@limit).map { |obj| format(obj) }
|
72
|
-
|
73
|
-
objects << "... (#{truncated_objects_count} more)" if truncated?
|
74
|
-
|
75
|
-
objects.join(", ")
|
76
|
-
end
|
77
|
-
|
78
|
-
def format_single_object
|
79
|
-
object = @objects.first if @objects.respond_to?(:first)
|
80
|
-
|
81
|
-
format(object)
|
82
|
-
end
|
83
|
-
|
84
|
-
def format(object)
|
85
|
-
return NO_OBJECTS_INDICATOR if blank?(object)
|
86
|
-
|
87
|
-
"#{class_name(object)}[#{format_attributes(evaluate_attributes(object))}]"
|
88
|
-
end
|
89
|
-
|
90
|
-
def format_attributes(attributes_hash)
|
91
|
-
return if attributes_hash.empty?
|
92
|
-
|
93
|
-
attributes_hash.
|
94
|
-
map(&format_attributes_map_block(attributes_hash)).
|
95
|
-
join(", ")
|
96
|
-
end
|
97
|
-
|
98
|
-
def format_attributes_map_block(attributes_hash)
|
99
|
-
if attributes_hash.one?
|
100
|
-
->(_key, value) { value.inspect_lit }
|
101
|
-
else
|
102
|
-
->(key, value) { "#{key}:#{value.inspect_lit}" }
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
# @return [Hash]
|
107
|
-
def evaluate_attributes(object)
|
108
|
-
@attributes.each_with_object({}) { |key, acc|
|
109
|
-
if object.respond_to?(key, :include_private)
|
110
|
-
acc[key] = object.send(key)
|
111
|
-
elsif key.to_s.start_with?("@")
|
112
|
-
acc[key] = object.instance_variable_get(key)
|
113
|
-
end
|
114
|
-
}
|
115
|
-
end
|
116
|
-
|
117
|
-
def class_name(object)
|
118
|
-
klass_given? ? @klass : object.class.name
|
119
|
-
end
|
120
|
-
|
121
|
-
def truncated_objects_count
|
122
|
-
objects_count - @limit
|
123
|
-
end
|
124
|
-
|
125
|
-
def objects_count
|
126
|
-
@objects_count ||= @objects.size
|
127
|
-
end
|
128
|
-
|
129
|
-
def klass_given?
|
130
|
-
@klass != :not_given
|
131
|
-
end
|
132
|
-
|
133
|
-
def blank?(object)
|
134
|
-
object.nil? || object == [] || object == {}
|
135
|
-
end
|
136
|
-
|
137
|
-
def many?
|
138
|
-
objects_count > 1
|
139
|
-
end
|
3
|
+
# ObjectIdentifier::Identifier manages construction of identification outputs
|
4
|
+
# using the passed in formatter_class.
|
5
|
+
class ObjectIdentifier::Identifier
|
6
|
+
# NOTE: `kargs` may be specific to the Formatter being called.
|
7
|
+
def self.call(
|
8
|
+
objects,
|
9
|
+
*attributes,
|
10
|
+
formatter_class: default_formatter_class,
|
11
|
+
**formatter_options)
|
12
|
+
formatter_class.(objects, *attributes, **formatter_options)
|
13
|
+
end
|
140
14
|
|
141
|
-
|
142
|
-
|
143
|
-
|
15
|
+
def self.default_formatter_class
|
16
|
+
ObjectIdentifier.configuration.formatter_class
|
17
|
+
end
|
144
18
|
|
145
|
-
|
146
|
-
|
147
|
-
# respond to `to_a` (such as Struct) and, instead, utilize either `to_ary`
|
148
|
-
# or just actually wrapping the object in an Array.
|
149
|
-
class ArrayWrap
|
150
|
-
def self.call(object)
|
151
|
-
if object.nil?
|
152
|
-
[]
|
153
|
-
elsif object.respond_to?(:to_ary)
|
154
|
-
object.to_ary || [object]
|
155
|
-
else
|
156
|
-
[object]
|
157
|
-
end
|
158
|
-
end
|
159
|
-
end
|
19
|
+
def self.default_attributes
|
20
|
+
ObjectIdentifier.configuration.default_attributes
|
160
21
|
end
|
161
22
|
end
|
data/lib/object_identifier.rb
CHANGED
@@ -3,10 +3,63 @@
|
|
3
3
|
# ObjectIdentifier is the base namespace for all modules/classes related to the
|
4
4
|
# object_identifier gem.
|
5
5
|
module ObjectIdentifier
|
6
|
+
# ObjectIdentifier::ArrayWrap mirrors the implementation of Rails'
|
7
|
+
# {Array.wrap} method. This allows us to get around objects that respond to
|
8
|
+
# `to_a` (such as Struct) and, instead, either utilize `to_ary` or just
|
9
|
+
# actually wrap the object in an Array ourselves.
|
10
|
+
class ArrayWrap
|
11
|
+
def self.call(object)
|
12
|
+
if object.nil?
|
13
|
+
[]
|
14
|
+
elsif object.respond_to?(:to_ary)
|
15
|
+
object.to_ary || [object]
|
16
|
+
else
|
17
|
+
[object]
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.configuration
|
23
|
+
@configuration ||= Configuration.new
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.configure
|
27
|
+
yield(configuration)
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.reset_configuration
|
31
|
+
@configuration = Configuration.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# ObjectIdentifier::Configuration stores the default configuration options for
|
35
|
+
# the ObjectIdentifier gem. Modification of attributes is possible at any
|
36
|
+
# time, and values will persist for the duration of the running process.
|
37
|
+
class Configuration
|
38
|
+
attr_reader :formatter_class,
|
39
|
+
:default_attributes
|
40
|
+
|
41
|
+
def initialize
|
42
|
+
@formatter_class = ObjectIdentifier::StringFormatter
|
43
|
+
@default_attributes = %i[id]
|
44
|
+
end
|
45
|
+
|
46
|
+
def formatter_class=(value)
|
47
|
+
unless value.is_a?(Class)
|
48
|
+
raise TypeError, "Formatter must be a Class constant"
|
49
|
+
end
|
50
|
+
|
51
|
+
@formatter_class = value
|
52
|
+
end
|
53
|
+
|
54
|
+
def default_attributes=(value)
|
55
|
+
@default_attributes = value.to_a.map!(&:to_sym)
|
56
|
+
end
|
57
|
+
end
|
6
58
|
end
|
7
59
|
|
8
60
|
require "object_identifier/version"
|
9
61
|
require "object_identifier/identifier"
|
62
|
+
require "object_identifier/formatters/string_formatter"
|
10
63
|
require "core_ext/object"
|
11
64
|
require "core_ext/string"
|
12
65
|
require "core_ext/symbol"
|
data/object_identifier.gemspec
CHANGED
@@ -7,9 +7,10 @@ require "object_identifier/version"
|
|
7
7
|
Gem::Specification.new do |spec|
|
8
8
|
spec.name = "object_identifier"
|
9
9
|
spec.version = ObjectIdentifier::VERSION
|
10
|
-
spec.authors = ["Paul
|
11
|
-
spec.email = ["
|
10
|
+
spec.authors = ["Paul DobbinSchmaltz", "Evan Sherwood"]
|
11
|
+
spec.email = ["p.dobbinschmaltz@icloud.com"]
|
12
12
|
spec.required_ruby_version = ">= 2.4.0"
|
13
|
+
spec.metadata = { "rubygems_mfa_required" => "true" }
|
13
14
|
|
14
15
|
spec.summary = "ObjectIdentifier identifies an object by its class name and attributes."
|
15
16
|
spec.description = "Object Identifier allows quick, easy, and uniform identification of an object by inspecting its class name and outputting any desirable attributes/methods. It is great for logging, sending descriptive notification messages, etc."
|
@@ -32,10 +33,12 @@ Gem::Specification.new do |spec|
|
|
32
33
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
33
34
|
spec.require_paths = ["lib"]
|
34
35
|
|
36
|
+
spec.add_development_dependency "benchmark-ips"
|
35
37
|
spec.add_development_dependency "bundler"
|
36
38
|
spec.add_development_dependency "byebug"
|
37
39
|
spec.add_development_dependency "minitest"
|
38
40
|
spec.add_development_dependency "minitest-reporters"
|
41
|
+
spec.add_development_dependency "much-stub"
|
39
42
|
spec.add_development_dependency "pry"
|
40
43
|
spec.add_development_dependency "pry-byebug"
|
41
44
|
spec.add_development_dependency "rake"
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Play from the pry console with:
|
4
|
+
# play scripts/benchmarking/formatters.rb
|
5
|
+
|
6
|
+
require "benchmark/ips"
|
7
|
+
|
8
|
+
custom_formatter_klasses ||= []
|
9
|
+
|
10
|
+
formatter_klasses = [
|
11
|
+
ObjectIdentifier::StringFormatter,
|
12
|
+
*Array(custom_formatter_klasses)
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
MyObject ||= Struct.new(:id, :name)
|
16
|
+
|
17
|
+
objects = [
|
18
|
+
MyObject.new(id: 1, name: "NAME1"),
|
19
|
+
MyObject.new(id: 2, name: "NAME2"),
|
20
|
+
MyObject.new(id: 3, name: "NAME3")
|
21
|
+
].freeze
|
22
|
+
|
23
|
+
puts "== Averaged ============================================================="
|
24
|
+
Benchmark.ips { |x|
|
25
|
+
formatter_klasses.each do |formatter_klass|
|
26
|
+
x.report(formatter_klass) {
|
27
|
+
formatter_klass.new(objects[0]).call
|
28
|
+
formatter_klass.new(objects[0], %i[id name]).call
|
29
|
+
formatter_klass.new(objects[0], klass: "CustomClass").call
|
30
|
+
formatter_klass.new(objects[0], %i[id name], klass: "CustomClass").call
|
31
|
+
formatter_klass.new(objects, limit: 2).call
|
32
|
+
formatter_klass.new(objects, %i[id name], klass: "CustomClass", limit: 2).call
|
33
|
+
}
|
34
|
+
end
|
35
|
+
|
36
|
+
x.compare!
|
37
|
+
}
|
38
|
+
puts "== Done"
|
39
|
+
|
40
|
+
puts "== Individualized ======================================================="
|
41
|
+
Benchmark.ips { |x|
|
42
|
+
# rubocop:disable Style/CombinableLoops
|
43
|
+
formatter_klasses.each do |formatter_klass|
|
44
|
+
x.report("#{formatter_klass} - Default Attributes") {
|
45
|
+
formatter_klass.new(objects[0]).call
|
46
|
+
}
|
47
|
+
end
|
48
|
+
formatter_klasses.each do |formatter_klass|
|
49
|
+
x.report("#{formatter_klass} - Custom Attributes") {
|
50
|
+
formatter_klass.new(objects[0], %i[id name]).call
|
51
|
+
}
|
52
|
+
end
|
53
|
+
formatter_klasses.each do |formatter_klass|
|
54
|
+
x.report("#{formatter_klass} - Custom Class") {
|
55
|
+
formatter_klass.new(objects[0], klass: "CustomClass").call
|
56
|
+
}
|
57
|
+
end
|
58
|
+
formatter_klasses.each do |formatter_klass|
|
59
|
+
x.report("#{formatter_klass} - Custom Attributes & Custom Class") {
|
60
|
+
formatter_klass.new(objects[0], %i[id name], klass: "CustomClass").call
|
61
|
+
}
|
62
|
+
end
|
63
|
+
formatter_klasses.each do |formatter_klass|
|
64
|
+
x.report("#{formatter_klass} - Limit 2") {
|
65
|
+
formatter_klass.new(objects, limit: 2).call
|
66
|
+
}
|
67
|
+
end
|
68
|
+
formatter_klasses.each do |formatter_klass|
|
69
|
+
x.report("#{formatter_klass} - Custom Attributes & Custom Class & Limit 2") {
|
70
|
+
formatter_klass.new(objects, %i[id name], klass: "CustomClass", limit: 2).call
|
71
|
+
}
|
72
|
+
end
|
73
|
+
# rubocop:enable Style/CombinableLoops
|
74
|
+
|
75
|
+
x.compare!
|
76
|
+
}
|
77
|
+
puts "== Done"
|
metadata
CHANGED
@@ -1,16 +1,30 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: object_identifier
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Paul
|
7
|
+
- Paul DobbinSchmaltz
|
8
8
|
- Evan Sherwood
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2023-01-05 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: benchmark-ips
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: bundler
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,6 +81,20 @@ dependencies:
|
|
67
81
|
- - ">="
|
68
82
|
- !ruby/object:Gem::Version
|
69
83
|
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: much-stub
|
86
|
+
requirement: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
type: :development
|
92
|
+
prerelease: false
|
93
|
+
version_requirements: !ruby/object:Gem::Requirement
|
94
|
+
requirements:
|
95
|
+
- - ">="
|
96
|
+
- !ruby/object:Gem::Version
|
97
|
+
version: '0'
|
70
98
|
- !ruby/object:Gem::Dependency
|
71
99
|
name: pry
|
72
100
|
requirement: !ruby/object:Gem::Requirement
|
@@ -155,15 +183,15 @@ description: Object Identifier allows quick, easy, and uniform identification of
|
|
155
183
|
object by inspecting its class name and outputting any desirable attributes/methods.
|
156
184
|
It is great for logging, sending descriptive notification messages, etc.
|
157
185
|
email:
|
158
|
-
-
|
186
|
+
- p.dobbinschmaltz@icloud.com
|
159
187
|
executables: []
|
160
188
|
extensions: []
|
161
189
|
extra_rdoc_files: []
|
162
190
|
files:
|
191
|
+
- ".github/workflows/ci.yml"
|
163
192
|
- ".gitignore"
|
164
193
|
- ".rubocop"
|
165
194
|
- ".rubocop.yml"
|
166
|
-
- ".travis.yml"
|
167
195
|
- CHANGELOG.md
|
168
196
|
- Gemfile
|
169
197
|
- Gemfile.lock
|
@@ -177,13 +205,16 @@ files:
|
|
177
205
|
- lib/core_ext/string.rb
|
178
206
|
- lib/core_ext/symbol.rb
|
179
207
|
- lib/object_identifier.rb
|
208
|
+
- lib/object_identifier/formatters/string_formatter.rb
|
180
209
|
- lib/object_identifier/identifier.rb
|
181
210
|
- lib/object_identifier/version.rb
|
182
211
|
- object_identifier.gemspec
|
212
|
+
- scripts/benchmarking/formatters.rb
|
183
213
|
homepage: https://github.com/pdobb/object_identifier
|
184
214
|
licenses:
|
185
215
|
- MIT
|
186
|
-
metadata:
|
216
|
+
metadata:
|
217
|
+
rubygems_mfa_required: 'true'
|
187
218
|
post_install_message:
|
188
219
|
rdoc_options: []
|
189
220
|
require_paths:
|
@@ -199,7 +230,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
199
230
|
- !ruby/object:Gem::Version
|
200
231
|
version: '0'
|
201
232
|
requirements: []
|
202
|
-
rubygems_version: 3.1
|
233
|
+
rubygems_version: 3.4.1
|
203
234
|
signing_key:
|
204
235
|
specification_version: 4
|
205
236
|
summary: ObjectIdentifier identifies an object by its class name and attributes.
|
data/.travis.yml
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
env:
|
2
|
-
global:
|
3
|
-
- CC_TEST_REPORTER_ID=3b57e55dcd3cc40ee073715e7d75fa80ab7b7687cb8a420eac500d27b5cb28db
|
4
|
-
sudo: false
|
5
|
-
language: ruby
|
6
|
-
rvm:
|
7
|
-
- 2.4.9
|
8
|
-
- 2.5.8
|
9
|
-
- 2.6.6
|
10
|
-
notifications:
|
11
|
-
email: false
|
12
|
-
before_install: gem install bundler -v 2.1.4
|
13
|
-
cache: bundler
|
14
|
-
before_script:
|
15
|
-
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
16
|
-
- chmod +x ./cc-test-reporter
|
17
|
-
- ./cc-test-reporter before-build
|
18
|
-
after_script:
|
19
|
-
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|