object_identifier 0.4.1 → 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 +6 -1
- data/Gemfile.lock +8 -4
- data/README.md +32 -1
- data/lib/core_ext/object.rb +1 -1
- 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 +3 -0
- data/scripts/benchmarking/formatters.rb +77 -0
- metadata +35 -3
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,4 +1,9 @@
|
|
1
|
-
### 0.
|
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
|
2
7
|
- Make compatible with Ruby 3.2 (and likely Ruby 3.0 and 3.1 as well).
|
3
8
|
- Update development dependencies.
|
4
9
|
|
data/Gemfile.lock
CHANGED
@@ -1,13 +1,14 @@
|
|
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
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)
|
@@ -15,12 +16,13 @@ GEM
|
|
15
16
|
json (2.6.3)
|
16
17
|
kwalify (0.7.2)
|
17
18
|
method_source (1.0.0)
|
18
|
-
minitest (5.
|
19
|
+
minitest (5.17.0)
|
19
20
|
minitest-reporters (1.5.0)
|
20
21
|
ansi
|
21
22
|
builder
|
22
23
|
minitest (>= 5.0)
|
23
24
|
ruby-progressbar
|
25
|
+
much-stub (0.1.10)
|
24
26
|
parallel (1.22.1)
|
25
27
|
parser (3.1.3.0)
|
26
28
|
ast (~> 2.4.1)
|
@@ -38,14 +40,14 @@ GEM
|
|
38
40
|
rainbow (>= 2.0, < 4.0)
|
39
41
|
regexp_parser (2.6.1)
|
40
42
|
rexml (3.2.5)
|
41
|
-
rubocop (1.
|
43
|
+
rubocop (1.42.0)
|
42
44
|
json (~> 2.3)
|
43
45
|
parallel (~> 1.10)
|
44
46
|
parser (>= 3.1.2.1)
|
45
47
|
rainbow (>= 2.2.2, < 4.0)
|
46
48
|
regexp_parser (>= 1.8, < 3.0)
|
47
49
|
rexml (>= 3.2.5, < 4.0)
|
48
|
-
rubocop-ast (>= 1.
|
50
|
+
rubocop-ast (>= 1.24.1, < 2.0)
|
49
51
|
ruby-progressbar (~> 1.7)
|
50
52
|
unicode-display_width (>= 1.4.0, < 3.0)
|
51
53
|
rubocop-ast (1.24.1)
|
@@ -63,10 +65,12 @@ PLATFORMS
|
|
63
65
|
ruby
|
64
66
|
|
65
67
|
DEPENDENCIES
|
68
|
+
benchmark-ips
|
66
69
|
bundler
|
67
70
|
byebug
|
68
71
|
minitest
|
69
72
|
minitest-reporters
|
73
|
+
much-stub
|
70
74
|
object_identifier!
|
71
75
|
pry
|
72
76
|
pry-byebug
|
data/README.md
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# Object Identifier
|
2
2
|
|
3
3
|
[](https://badge.fury.io/rb/object_identifier)
|
4
|
-
[](https://codeclimate.com/github/pdobb/object_identifier/test_coverage)
|
5
4
|
[](https://codeclimate.com/github/pdobb/object_identifier/maintainability)
|
6
5
|
|
7
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.
|
@@ -45,6 +44,21 @@ Tested MRI Ruby Versions:
|
|
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,6 +166,23 @@ 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.
|
data/lib/core_ext/object.rb
CHANGED
@@ -40,6 +40,6 @@ class Object
|
|
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
42
|
def identify(*args, **kargs)
|
43
|
-
ObjectIdentifier::Identifier.
|
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(*args, **kargs)
|
43
|
-
new(*args, **kargs).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
@@ -10,6 +10,7 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.authors = ["Paul DobbinSchmaltz", "Evan Sherwood"]
|
11
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,7 +1,7 @@
|
|
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
7
|
- Paul DobbinSchmaltz
|
@@ -9,8 +9,22 @@ authors:
|
|
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
|
@@ -160,6 +188,7 @@ 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"
|
@@ -176,13 +205,16 @@ files:
|
|
176
205
|
- lib/core_ext/string.rb
|
177
206
|
- lib/core_ext/symbol.rb
|
178
207
|
- lib/object_identifier.rb
|
208
|
+
- lib/object_identifier/formatters/string_formatter.rb
|
179
209
|
- lib/object_identifier/identifier.rb
|
180
210
|
- lib/object_identifier/version.rb
|
181
211
|
- object_identifier.gemspec
|
212
|
+
- scripts/benchmarking/formatters.rb
|
182
213
|
homepage: https://github.com/pdobb/object_identifier
|
183
214
|
licenses:
|
184
215
|
- MIT
|
185
|
-
metadata:
|
216
|
+
metadata:
|
217
|
+
rubygems_mfa_required: 'true'
|
186
218
|
post_install_message:
|
187
219
|
rdoc_options: []
|
188
220
|
require_paths:
|