Must exist, right?
I assume there's a module that works like this? package MyFoo; use Data::Auto::Objectify::Thing qw( my_data_field ); sub new { bless { my_data_field = { foo = [ 1, 2, 3 ], bar = { eat = 'drink', sleep = 'wake' }, } }, shift; } package main; my $my_foo = MyFoo-new; my $two = $my_foo-foo( 1 ); my $eat = $my_foo-bar-eat; In other words I want it to synthesise accessors based on the contents of a complex data structure. For bonus points it might also allow # Can use subscript notation for hashes my $eat = $my_foo-bar('eat'); And maybe allow multiple subscripts so that, for example, if I had a field called 'grid' that contained a rectangular array I could then do my $point = $my_bar-grid(3, 5); It's got to exist, right? I had a look but there are so many ^(?:Object|Data)::.* modules that it's a bit hard to see the wood for the trees :) -- Andy Armstrong, Hexten
Re: Must exist, right?
On 19 Jun 2008, at 00:35, Andy Armstrong wrote: It's got to exist, right? I had a look but there are so many ^(?:Object|Data)::.* modules that it's a bit hard to see the wood for the trees :) Ah. I see: http://search.cpan.org/dist/Class-AutoAccess/ But that creates accessor/mutators. I want to generate read only accessors and have any arguments to them interpreted as a path into the data structure below the point referred to by the accessor. -- Andy Armstrong, Hexten
Re: Must exist, right?
# from Andy Armstrong # on Wednesday 18 June 2008 16:35: use Data::Auto::Objectify::Thing qw( my_data_field ); sub new { bless { my_data_field = { foo = [ 1, 2, 3 ], bar = { eat = 'drink', sleep = 'wake' }, ... my $two = $my_foo-foo( 1 ); my $eat = $my_foo-bar-eat; You want something like Object::Accessor, but without needing to actually create the object? Assuming you could be bothered to call new() for the sub-object (passing it to the contructor, etc), just about anything including Moose would work ;-) I'll let the more Moose-enabled folks comment on how a 'default' sub (or some other setup) would do exactly what you want. --Eric -- It ain't those parts of the Bible that I can't understand that bother me, it's the parts that I do understand. --Mark Twain --- http://scratchcomputing.com ---
Re: Must exist, right?
Considering that Data::Dumper is entirely regular in how it will present a deconstruction of the data structure, and you have a clear concept of how you wish the example structure === accessor set thing to work, it's certainly possible, and would take maybe two hacking sessions to get right (provided you don't get swept into the lake by a mini-tsunami.) It seems like something that might not exist already, as the need to share that kind of meta-abstraction is felt by only extreme show-offs (such as various people on this list) To make your example DWITYM, Data::Auto::Objectify::Thing would presume that the structure returned by its caller has a Cnew that will return a fully populated example object when called with no arguments, and would postpone defining the accessors until the INIT phase, because the {caller()::'new'} does not exist yet at Cimport time. I would have Data::Auto::Objectify::Thing take an optional C example = ... import arg in addition to the wait-until-INIT trick, and would bless the substructures into constructed-named packages under Data::Auto::Objectify::Thing::Packages::... for example. Tipjar::fields creates accessors for named fields by imposing array object semantics on its calling package; recently I prefer to use :lvalue subroutines for accessors rather than defining both get* and put* s. In your example, you are planning on subclassing MyFoo into a whole family of kinds of hash-based objects that share this accessor set, or something. It's not clear if the elements are supposed to be extensible or not. If they aren't extensible, and there is a fixed set of them, using an array-based object and naming all the slots with lvalue accessors is the way I would do it sub new { bless [], shift }; BEGIN{ my $counter = -1; eval join '' map {$counter++; ACCESSOR} (qw/one two three eat sleep/) sub $_ : lvalue { shift()-[$counter] } ACCESSOR }; but of course I don't know exactly what you are trying to do.
Re: Must exist, right?
On 19 Jun 2008, at 01:14, Eric Wilhelm wrote: You want something like Object::Accessor, but without needing to actually create the object? Yup. I want a read-only object with dynamically generated per object methods that reflect the internal data structure. Assuming you could be bothered to call new() for the sub-object (passing it to the contructor, etc), just about anything including Moose would work ;-) I'll let the more Moose-enabled folks comment on how a 'default' sub (or some other setup) would do exactly what you want. I really /must/ look at Moose properly soon :) -- Andy Armstrong, Hexten
Re: Must exist, right?
On 19 Jun 2008, at 01:18, David Nicol wrote: Considering that Data::Dumper is entirely regular in how it will present a deconstruction of the data structure, and you have a clear concept of how you wish the example structure === accessor set thing to work, it's certainly possible, and would take maybe two hacking sessions to get right (provided you don't get swept into the lake by a mini-tsunami.) Yeah, I've been sort of passively thinking about how to do it for a little while. I don't think it's too hard but I don't want to reinvent the wheel. It seems like something that might not exist already, as the need to share that kind of meta-abstraction is felt by only extreme show-offs (such as various people on this list) :) To make your example DWITYM, Data::Auto::Objectify::Thing would presume that the structure returned by its caller has a Cnew that will return a fully populated example object when called with no arguments, and would postpone defining the accessors until the INIT phase, because the {caller()::'new'} does not exist yet at Cimport time. I was thinking the accessors would be completely dynamic - AUTOLOADed. So creation of the data structure can happen at any time before you start calling accessors. I would have Data::Auto::Objectify::Thing take an optional C example = ... import arg in addition to the wait-until-INIT trick, and would bless the substructures into constructed-named packages under Data::Auto::Objectify::Thing::Packages::... for example. I wasn't thinking of making dynamic packages at all. See above. Tipjar::fields creates accessors for named fields by imposing array object semantics on its calling package; recently I prefer to use :lvalue subroutines for accessors rather than defining both get* and put* s. In your example, you are planning on subclassing MyFoo into a whole family of kinds of hash-based objects that share this accessor set, or something. It's not clear if the elements are supposed to be extensible or not. If they aren't extensible, and there is a fixed set of them, using an array-based object and naming all the slots with lvalue accessors is the way I would do it I basically want to be able to create read only 'value' objects that wrap a data structure that's created dynamically at runtime. A concrete example: I've just written some code that parses a formatted summary of memory usage for a server product. There are five named metrics for each of around 30 named categories of memory usage. My code just parses what it finds - it doesn't know any of the names ahead of time and the names might change as the product evolves. I'd like the object that falls out of the end to let me do e.g.: print $mem-relay_buffer-allocations; instead of print $mem-get('relay_buffer', 'allocations'); which is how it looks at the moment. -- Andy Armstrong, Hexten
Re: Must exist, right?
On Jun 18, 2008, at 6:35 PM, Andy Armstrong wrote: I assume there's a module that works like this? [snip] It's got to exist, right? I had a look but there are so many ^ (?:Object|Data)::.* modules that it's a bit hard to see the wood for the trees :) -- Andy Armstrong, Hexten Implemented! See the attached .pm file and the test.pl file that verifies the four snipped use cases. It could certainly be made more readable, but my interest is waning quickly. :-) I hereby grant anyone permission to use/extend this crappy code under the same license terms as Perl itself. Chris package MyFoo; use Data::Auto::Objectify::Thing qw( my_data_field ); sub new { bless { my_data_field = { foo = [ 1, 2, 3 ], bar = { eat = 'drink', sleep = 'wake' }, grid = [ [1,2,3,4,5,6], [7,8,9,10,11,12], [13,14,15,16,17,18], [19,20,21,22,23,24], ], } }, shift; } package main; use Test::More tests = 4; my $my_foo = MyFoo-new; is($my_foo-foo( 1 ), 2); is($my_foo-bar-eat, 'drink'); is($my_foo-bar('eat'), 'drink'); is($my_foo-grid(3, 5), 24); package Data::Auto::Objectify::Thing; use warnings; use strict; use Carp; use Data::Dumper; sub import { my ($pkg, $fieldname) = @_; my $caller_pkg = caller(0); no strict 'refs'; *{$caller_pkg . '::AUTOLOAD'} = sub { my ($self, @args) = @_; my $autoload = do { no strict 'vars'; $AUTOLOAD; }; return if $autoload =~ /::DESTROY$/; my ($fn) = $autoload =~ m/([^:]+)\z/xms; if (defined $fieldname) { $self = $self-{$fieldname}; } if (!exists $self-{$fn}) { croak 'No such field ' . $fn; } my $field = $self-{$fn}; if (!ref $field) { return $field; } if ('ARRAY' eq ref $field) { $field = $field-[$_] for @args; return $field; } my $obj = bless {xyzzy = $field}, 'Data::Auto::Objectify::Thing::anon'; $obj = $obj-$_() for @args; return $obj; }; return; } package Data::Auto::Objectify::Thing::anon; Data::Auto::Objectify::Thing-import('xyzzy'); 1;