{"id":21651798,"url":"https://github.com/hoytech/object-sub","last_synced_at":"2025-03-20T04:00:13.674Z","repository":{"id":30871138,"uuid":"34428762","full_name":"hoytech/Object-Sub","owner":"hoytech","description":"Create objects without those pesky classes","archived":false,"fork":false,"pushed_at":"2017-03-06T17:03:22.000Z","size":14,"stargazers_count":2,"open_issues_count":0,"forks_count":2,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-01-25T05:43:02.483Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Perl","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/hoytech.png","metadata":{"files":{"readme":"README.pod","changelog":"Changes","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-23T02:25:16.000Z","updated_at":"2021-06-18T22:45:38.000Z","dependencies_parsed_at":"2022-09-07T01:50:45.278Z","dependency_job_id":null,"html_url":"https://github.com/hoytech/Object-Sub","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoytech%2FObject-Sub","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoytech%2FObject-Sub/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoytech%2FObject-Sub/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/hoytech%2FObject-Sub/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/hoytech","download_url":"https://codeload.github.com/hoytech/Object-Sub/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244547602,"owners_count":20470103,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-25T07:49:35.854Z","updated_at":"2025-03-20T04:00:13.616Z","avatar_url":"https://github.com/hoytech.png","language":"Perl","funding_links":[],"categories":[],"sub_categories":[],"readme":"package Object::Sub;\n\nuse strict;\n\nour $VERSION = '0.103';\n\nour $AUTOLOAD;\n\nuse overload fallback =\u003e 1,\n             '\u0026{}' =\u003e sub {\n                        my $self_ref = \\$_[0];\n\n                        return sub {\n                          return $$self_ref-\u003e{__object_sub_internal_cb}-\u003e($$self_ref, undef, @_);\n                        };\n                      };\n\nsub AUTOLOAD {\n  die \"$_[0] is not an object\" if !ref $_[0];\n\n  my $name = $AUTOLOAD;\n  $name =~ s/.*://;\n\n  return $_[0]-\u003e{__object_sub_internal_cb}-\u003e($_[0], $name, @_[1 .. $#_]);\n}\n\nsub new {\n  my ($class, $cb) = @_;\n\n  if (ref $cb eq 'HASH') {\n    my $orig_cb = $cb;\n    $cb = sub {\n      ($orig_cb-\u003e{$_[1]} || die \"unable to find method $_[1]\")-\u003e($_[0], @_[2 .. $#_]);\n    };\n  }\n\n  die \"need a callback\" if ref $cb ne 'CODE';\n\n  my $self = { __object_sub_internal_cb =\u003e $cb, };\n  bless $self, $class;\n\n  return $self;\n}\n\n## Prevent DESTROY method from being handled by AUTOLOAD\n\nsub DESTROY {\n}\n\n\n1;\n\n\n\n__END__\n\n=encoding utf-8\n\n=head1 NAME\n\nObject::Sub - Create objects without those pesky classes\n\n=head1 SYNOPSIS\n\n    use Object::Sub;\n\n    my $obj = Object::Sub-\u003enew(sub {\n                  my ($self, $method, @args) = @_;\n\n                  print \"self: $self, method name: $method, first arg: $args[0]\\n\";\n              });\n\n    $obj-\u003ewhatever(123);\n    ## self: Object::Sub=HASH(0xc78eb0), method name: whatever, first arg: 123\n\n    $obj-\u003e(123);\n    ## self: Object::Sub=HASH(0xc78eb0), method name: , first arg: 123\n    ##   ($method is undef)\n\nAlternatively, you can use a hash of subs:\n\n    my $obj = Object::Sub-\u003enew({\n        add =\u003e sub {\n            my ($self, $num1, $num2) = @_;\n            return $num1 + $num2;\n        },\n        mul =\u003e sub {\n            my ($self, $num1, $num2) = @_;\n            return $num1 * $num2;\n        },\n    });\n\n    $obj-\u003eadd(2, 3);\n    ## =\u003e 5\n\n=head1 DESCRIPTION\n\nSometimes you want something that acts like an object but you don't want to go to all the trouble of creating a new package, with constructor and methods and so on. This module is a trivial wrapper around perl's L\u003cAUTOLOAD\u003e functionality which intercepts method calls and lets you handle them in a single C\u003csub\u003e. It also uses L\u003coverload\u003e so that you can additionally treat the object as a C\u003csub\u003e if you desire.\n\n=head1 USE-CASES\n\n=head2 AUTOLOAD SYNTACTIC SUGAR\n\nL\u003cAUTOLOAD\u003e allows you to dispatch on method names at run-time which can sometimes be useful, for example in RPC protocols where you transmit method call messages to another process for them to be executed remotely. Unfortunately, using L\u003cAUTOLOAD\u003e is a bit annoying since the interface is somewhat arcane. L\u003cObject::Sub\u003e is a nicer interface to the most commonly used AUTOLOAD functionality:\n\n    my $obj = Object::Sub-\u003enew(sub {\n                my ($self, $method, @args) = @_;\n\n                my $rpc_input = encode_json({ method =\u003e $method, args =\u003e [ @args ] });\n\n                my $rpc_output = do_rpc_call($rpc_input);\n\n                return decode_json($rpc_output);\n              });\n\nBecause C\u003cObject::Sub\u003e objects can also be treated as subs, your RPC interface can support sub-routine calls on the objects as well as method calls, even on the same object.\n\n=head2 PLACE-HOLDER OBJECTS\n\nSome APIs require you to pass in or provide an object but then don't actually end up using it. Instead of passing in undef and getting a weird C\u003cCan't call method \"XYZ\" on an undefined value\u003e error, you can pass in an L\u003cObject::Sub\u003e which will throw a \"helpful\" exception instead:\n\n    my $obj = Some::API-\u003enew(\n                logger =\u003e Object::Sub-\u003enew(sub { die \"FIXME: add logger\" }),\n              );\n\nAlternatively, you may choose to minimally implement the API \"inline\" in your program:\n\n    my $obj = Some::API-\u003enew(\n                logger =\u003e Object::Sub-\u003enew(sub {\n                            my ($self, $method, @args) = @_;\n\n                            return if $method eq 'debug';\n\n                            say STDERR \"Some::API $method: \" . join(' ', @args);\n                          })\n              );\n\n=head2 LAZY OBJECT CREATION\n\nAgain, some APIs may never end up using an object so you may wish to \"lazily\" defer the creation of that object until a method is actually called on it. This module can help you make the cases where it doesn't use it more efficient.\n\nFor example, suppose you have a large L\u003cCGI\u003e script which always opens a L\u003cDBI\u003e connection but only actually accesses this connection for a small portion of runs. You can prevent the script from accessing the database on the majority of runs with L\u003cObject::Sub\u003e:\n\n    my $dbh = Object::Sub-\u003enew(sub {\n                require DBI;\n                $_[0] = DBI-\u003econnect($dsn, $user, $pass, { RaiseError =\u003e 1 })\n                    || die \"Unable to connect to database: $DBI::errstr\";\n\n                my ($self, $method, @args) = @_;\n                return $self-\u003e$method(@args);\n              });\n\nNote how we don't even load or compile the module until the first method is called. After you call a method on C\u003c$dbh\u003e it changes from a C\u003cObject::Sub\u003e object into a C\u003cDBI\u003e object (assuming the C\u003c\u003c DBI-\u003econnect \u003e\u003e constructor succeeds). This works because the C\u003c$_[0]\u003e argument is actually an alias to C\u003c$dbh\u003e and can be modified.\n\nTo demonstrate this, here is an example with L\u003cSession::Token\u003e:\n\n    my $o = Object::Sub-\u003enew(sub {\n              require Session::Token;\n              $_[0] = Session::Token-\u003enew;\n\n              my ($self, $method, @args) = @_;\n              return $self-\u003e$method(@args);\n            });\n\n    say ref $o;\n    ## Object::Sub\n\n    say $o-\u003eget;\n    ## mhDPtfLlFMGl5kyNcJgFt7\n\n    say ref $o;\n    ## Session::Token\n\n    say $o-\u003eget;\n    ## 4JYkGgwWbYWGleU7Qk912P\n\nWith L\u003cObject::Sub\u003e you can lazily \"create\" and pass around objects before their constructor code has even been loaded.\n\n\n=head1 BUGS\n\nAlthough not really a bug in this module, common perl code tends to copy references of objects. Any code that overwrites the caller object (for example in the L\u003cLAZY OBJECT CREATION\u003e section) will only update one of the copies.\n\n\n=head1 SEE ALSO\n\nL\u003cObject-Sub github repo|https://github.com/hoytech/Object-Sub\u003e\n\n=head1 AUTHOR\n\nDoug Hoyte, C\u003c\u003c \u003cdoug@hcsw.org\u003e \u003e\u003e\n\n=head1 COPYRIGHT \u0026 LICENSE\n\nCopyright 2015-2016 Doug Hoyte.\n\nThis module is licensed under the same terms as perl itself.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoytech%2Fobject-sub","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fhoytech%2Fobject-sub","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fhoytech%2Fobject-sub/lists"}