README.md in object_inspector-0.2.0 vs README.md in object_inspector-0.3.0

- old
+ new

@@ -1,9 +1,9 @@ # ObjectInspector [![Gem Version](https://badge.fury.io/rb/object_inspector.svg)](https://badge.fury.io/rb/object_inspector) -[![Build Status](https://travis-ci.org/objects-on-rails/display-case.svg?branch=master)](https://travis-ci.org/objects-on-rails/display-case) +[![Build Status](https://travis-ci.org/pdobb/object_inspector.svg?branch=master)](https://travis-ci.org/pdobb/object_inspector) [![Test Coverage](https://api.codeclimate.com/v1/badges/34e821263d9e0c33d536/test_coverage)](https://codeclimate.com/github/pdobb/object_inspector/test_coverage) [![Maintainability](https://api.codeclimate.com/v1/badges/34e821263d9e0c33d536/maintainability)](https://codeclimate.com/github/pdobb/object_inspector/maintainability) ObjectInspector takes Object#inspect to the next level. Specify any combination of identification attributes, flags, info, and/or a name along with an optional self-definable scope option to represent an object in the console, in logging, etc. @@ -30,12 +30,30 @@ Tested MRI Ruby Versions: * 2.2.10 * 2.3.7 * 2.4.4 * 2.5.1 +* edge +## Configuration + +Global/default values for ObjectInspector can be configured via the ObjectInspector::Configuration object. + +_Note: In a Rails app, the following would go in e.g. `config/initializers/object_inspector.rb`_ + +```ruby +# Default values are shown. +ObjectInspector.configure do |config| + config.wild_card_scope = "all" + config.out_of_scope_placeholder = "*" + config.flags_separator = " / " + config.info_separator = " | " +end +``` + + ## Usage Given, an object of any type, call ObjectInspector::Inspector.inspect. ```ruby @@ -58,17 +76,17 @@ ```ruby class MyObject def inspect ObjectInspector::Inspector.inspect(self, identification: "My Object", - flags: "FLAG1", + flags: "FLAG1 / FLAG2", info: "INFO", name: "NAME") end end -MyObject.new.inspect # => "<My Object(FLAG1) INFO :: NAME>" +MyObject.new.inspect # => "<My Object(FLAG1 / FLAG2) INFO :: NAME>" ``` Or, define `inspect_identification`, `inspect_flags`, `inspect_info`, and `inspect_name` as either public or private methods on Object. ```ruby @@ -78,16 +96,16 @@ end private def inspect_identification; "My Object" end - def inspect_flags; "FLAG1" end + def inspect_flags; "FLAG1 / FLAG2" end def inspect_info; "INFO" end def inspect_name; "NAME" end end -MyObject.new.inspect # => "<My Object(FLAG1) INFO :: NAME>" +MyObject.new.inspect # => "<My Object(FLAG1 / FLAG2) INFO :: NAME>" ``` ## Helper Usage @@ -116,74 +134,194 @@ end MyObject.new.inspect # => "<My Object(FLAG1) INFO :: NAME>" ``` -Or, define `inspect_identification`, `inspect_flags`, `inspect_info`, and `inspect_name` in Object. +Or, define `inspect_identification`, `inspect_flags`, `inspect_info`, and `inspect_name` (or `display_name`) in Object. ```ruby class MyObject include ObjectInspector::InspectorsHelper private def inspect_identification; "My Object" end - def inspect_flags; "FLAG1" end + def inspect_flags; "FLAG1 / FLAG2" end def inspect_info; "INFO" end - def inspect_name; "NAME" end + def inspect_name; "NAME" end # Or: def display_name; "NAME" end end -MyObject.new.inspect # => "<My Object(FLAG1) INFO :: NAME>" +MyObject.new.inspect # => "<My Object(FLAG1 / FLAG2) INFO :: NAME>" ``` -## On-the-fly Inspect Methods +## Scopes -When passed as an option (as opposed to being called via an Object-defined method) symbols will be called/evaluated on Object on the fly. +Use the `scope` option to define the scope of the `inspect_*` methods. The supplied value will be wrapped by the ObjectInspector::Scope helper object. +The default value is `ObjectInspector::Scope.new(:self)`. + +### Scope Names + +ObjectInspector::Scope acts like [ActiveSupport::StringInquirer](http://api.rubyonrails.org/classes/ActiveSupport/StringInquirer.html). This is a prettier way to test for a given type of "scope" within objects. + +The ObjectInspector::Scope objects in these examples are the same as specifying `<scope_name>` like this: + ```ruby -class MyObject - include ObjectInspector::InspectorsHelper +my_object.inspect(scope: <scope_name>) +``` - def my_method1; "Result1" end - def my_method2; "Result2" end - def inspect_info; :my_method2 end -end +Options: +- `:self` (Default) -- Is meant to confine object interrogation to self (don't interrogate neighboring objects). +- `:all` -- Is meant to match on all scopes, regardless of their name. +- `<custom>` -- Anything else that makes sense for the object to key on. -MyObject.new.inspect(info: "my_method1") # => "<MyObject my_method1>" -MyObject.new.inspect(info: :my_method2) # => "<MyObject Result2>" -MyObject.new.inspect # => "<MyObject my_method2>" +```ruby +scope = ObjectInspector::Scope.new +scope.self? # => true +scope.verbose? # => false +scope.complex? # => false ``` -#### Scope +#### Multiple Scope Names -Use the `scope` option to define the scope of the `inspect_*` methods. +It is also possible to pass in multiple scope names to match on. -If ActiveSupport::StringInquirer is defined then the default `scope` is `"self".inquiry`. -The default value is `:self` if ActiveSupport::StringInquirer is not defined. +```ruby +scope = ObjectInspector::Scope.new(%i[verbose complex]) +scope.self? # => false +scope.verbose? # => true +scope.complex? # => true +``` + +#### The "Wild Card" Scope + +Finally, `:all` is a "wild card" scope name, and will match on all scope names. + ```ruby +scope = ObjectInspector::Scope.new(:all) +scope.self? # => true +scope.verbose? # => true +scope.complex? # => true +``` + + +### Scope blocks + +Passing a block to a scope predicate falls back to the out-of-scope placeholder (`*` by default) if the scope does not match. + +```ruby +scope = ObjectInspector::Scope.new(:verbose) +scope.verbose? { "MATCH" } # => "MATCH" +scope.complex? { "MATCH" } # => "*" +``` + + +### Scope Joiners + +ObjectInspector::Scope also offers helper methods for uniformly joining inspect elements: +- `join_flags` -- Joins flags with ` / ` by default +- `join_info` -- Joins info items with ` | ` by default + +```ruby +scope = ObjectInspector::Scope.new(:verbose) +scope.join_flags([1, 2, 3]) # => "1 / 2 / 3" +scope.join_info([1, 2, 3]) # => "1 | 2 | 3" +``` + + +### Conversion to ObjectInspector::Scope + +ObjectInspector::Conversions.Scope() is available for proper conversion to ObjectInspector::Scope objects. Though this should rarely be necessary as conversion is performed automatically when calling `<my_object>.inspect(scope: <scope_name>)`. + +```ruby +ObjectInspector::Conversions.Scope(:self) +# => #<ObjectInspector::Scope:0x007ff78ab8e7f8 @name="self"> + +scope = ObjectInspector::Scope.new(:verbose) +result = ObjectInspector::Conversions.Scope(scope) +# => #<ObjectInspector::Scope:0x007ff78ac9c140 @name="verbose"> + +scope.object_id == result.object_id # => true +``` + + +## Full Example + +```ruby class MyObject include ObjectInspector::InspectorsHelper - def inspect_flags(scope:, separator: " / ".freeze) - flags = ["FLAG1"] + attr_reader :name, + :a2 - # If ActiveSupport::StringInquirer is defined, use this: - # flags << "FLAG2" if scope.all? + def initialize(name, a2 = 2) + @name = name + @a2 = a2 + end - # If ActiveSupport::StringInquirer is not defined, use this: - flags << "FLAG2" if scope == :all + def associated_object1 + OpenStruct.new(flags: "AO1_FLAG1") + end - flags.join(separator) + def associated_object2 + OpenStruct.new(flags: "AO2_FLAG1") end + +private + + def inspect_identification + identify(:a2) + end + + def inspect_flags(scope:) + flags = ["DEFAULT_FLAG"] + + flags << + scope.verbose? { + [ + associated_object1.flags, + associated_object2.flags, + ] + } + + scope.join_flags(flags) + end + + def inspect_info(scope:) + info = ["Default Info"] + info << "Complex Info" if scope.complex? + info << scope.verbose? { "Verbose Info" } + + scope.join_info(info) + end + + # Or `def inspect_name` + def display_name + name + end end -MyObject.new.inspect # => "<MyObject(FLAG1)>" -MyObject.new.inspect(scope: :all) # => "<MyObject(FLAG1 / FLAG2)>" +my_object = MyObject.new("Name") + +my_object.inspect +# => "<MyObject[a2:2](DEFAULT_FLAG / *) Default Info | * :: Name>" + +my_object.inspect(scope: :complex) +# => "<MyObject[a2:2](DEFAULT_FLAG / *) Default Info | Complex Info | * :: Name>" + +my_object.inspect(scope: :verbose) +# => "<MyObject[a2:2](DEFAULT_FLAG / AO1_FLAG1 / AO2_FLAG1) Default Info | Verbose Info :: Name>" + +my_object.inspect(scope: %i[self complex verbose]) +# => "<MyObject[a2:2](DEFAULT_FLAG / AO1_FLAG1 / AO2_FLAG1) Default Info | Complex Info | Verbose Info :: Name>" + +my_object.inspect(scope: :all) +# => "<MyObject[a2:2](DEFAULT_FLAG / AO1_FLAG1 / AO2_FLAG1) Default Info | Complex Info | Verbose Info :: Name>" ``` ## Wrapped Objects @@ -205,21 +343,41 @@ class MyWrappedObject include ObjectInspector::InspectorsHelper private - def inspect_flags; "FLAG1" end + def inspect_flags; "FLAG1 / FLAG2" end def inspect_info; "INFO" end end MyWrapperObject.new.inspect -# => "<MyWrapperObject(WRAPPER_FLAG1)> ⇨ <MyWrappedObject(FLAG1) INFO>" +# => "<MyWrapperObject(WRAPPER_FLAG1)> ⇨ <MyWrappedObject(FLAG1 / FLAG2) INFO>" ``` This feature is recursive. +## On-the-fly Inspect Methods + +When passed as an option (as opposed to being called via an Object-defined method) symbols will be called/evaluated on Object on the fly. + +```ruby +class MyObject + include ObjectInspector::InspectorsHelper + + def my_method1; "Result1" end + def my_method2; "Result2" end + + def inspect_info; :my_method2 end +end + +MyObject.new.inspect(info: "my_method1") # => "<MyObject my_method1>" +MyObject.new.inspect(info: :my_method2) # => "<MyObject Result2>" +MyObject.new.inspect # => "<MyObject my_method2>" +``` + + ## Custom Formatters A custom inspect formatter can be defined by implementing the interface defined by [ObjectInspector::BaseFormatter](https://github.com/pdobb/object_inspector/blob/master/lib/object_inspector/formatters/base_formatter.rb) and then passing that into ObjectInspector::Inspector.new. ```ruby @@ -233,22 +391,23 @@ include ObjectInspector::InspectorsHelper def inspect super(formatter: MyCustomFormatter, identification: "IDENTIFICATION", - flags: "FLAG1 | FLAG2", + flags: "FLAG1 / FLAG2", info: "INFO", name: "NAME") end end MyObject.new.inspect -# => "[IDENTIFICATION Flags: FLAG1 | FLAG2 -- Info: INFO -- Name: NAME]" +# => "[IDENTIFICATION Flags: FLAG1 / FLAG2 -- Info: INFO -- Name: NAME]" ``` -See also: [ObjectInspector::TemplatingFormatter]. -See also: [ObjectInspector::CombiningFormatter]. +See examples: +- [ObjectInspector::TemplatingFormatter] +- [ObjectInspector::CombiningFormatter] ## Performance ### Benchmarking ObjectInspector @@ -281,10 +440,11 @@ # ObjectInspector::CombiningFormatter: 34973.9 i/s - 1.31x slower # # == Done ``` + #### Benchmarking Custom Formatters Custom Formatters may be similarly gauged for comparison by adding them to the `custom_formatter_klasses` array before playing the script. ```ruby @@ -310,26 +470,29 @@ ```ruby class MyObject include ObjectInspector::InspectorsHelper def my_method1 - "Result1" + 1 end def my_method2 - "Result2" + 2 end private - def inspect_identification; identify(:my_method1, :my_method2) end - def inspect_flags; "FLAG1" end + def inspect_identification + identify(:my_method1, :my_method2) + end + + def inspect_flags; "FLAG1 / FLAG2" end def inspect_info; "INFO" end def inspect_name; "NAME" end end MyObject.new.inspect -# => "<MyObject[my_method1:Result1, my_method2:Result2](FLAG1) INFO :: NAME>" +# => "<MyObject[my_method1:1, my_method2:2](FLAG1 / FLAG2) INFO :: NAME>" ``` ## Development