In my last question I asked many unrelated things, and can't accept multiple answers what answers only some questions, so here is clearly (i hope) defined question about the (Moo) attributes.
use 5.010;
use strict;
use warnings;
package SomeMoo;
use Moo;
has $_ => ( is => 'rw', predicate => 1) for (qw(a1 a2 nn xx));
package SomeMoose;
use Moose;
has $_ => ( is => 'rw', predicate => "has_".$_) for (qw(a1 a2 nn xx));
package main;
use Data::Dumper;
my $omoo = SomeMoo->new(a1 => "a1val", a2 => "a2val", xx=>"xxval");
say Dumper $omoo;
# $VAR1 = bless( {
# 'a2' => 'a2val',
# 'a1' => 'a1val',
# 'xx' => 'xxval'
# }, 'SomeMoo' );
#for Moose:
my $omoose = SomeMoose->new(a1 => "a1val", a2 => "a2val", xx=>"xxval");
say Dumper $omoose;
#as above, only blessed to package 'SomeMoose'.
#in both cases can do the next, for getting an (partial) list "attributes"
say $_ for keys (%$omoose); #or %$omoo
#anyway, in Moose i can
say "all attributes";
say $_->name for $omoose->meta->get_all_attributes();
#what prints
# all attributes
# nn
# a2
# a1
# xx
So the blessed object refences an object what contains only attributes what are set.
Question:
why the $self references, (so the %$self conatains) only the
attributes what are set, and not all, e.g the nn too from the example
code? (When the bless only associates the reference with an package why the $omoo isn't contains all package variables?) And from where the Moose knows it?)
how to get all_attributes in case of Moo?
Clearly I missing some basic knowledge.. :(
why the $self references, (so the %$self conatains) only the attributes what are set, and not all, e.g the nn too from the example code? (When the bless only associates the reference with an package why the $omoo isn't contains all package variables?) And from where the Moose knows it?)
Each time you create a new object, such as SomeMoo->new, you create a new reference which is made of two parts, the first is the data, which is usually a hashref, that hashref is blessed by the second part, which is the class, SomeMoo. The hash is used to store data associated with this instance of the new object, while SomeMoo is the class which provides the methods associated with with accessing/manipulating/doing stuff with the data.
Inside your class definitions you call a function called has. This function extends the class with new methods. In the case of Moo, you could make a poor man's version, by writing something like:
package SimpleMoo;
no strict 'refs';
sub new {bless {}, $_[0]} # very very simple constructor (oldskool)
*{__PACKAGE__."::$_"} = sub { # fyi: __PACKAGE__ eq "SimpleMoo"
$_[0]->{$_} = $_[1] if exists $_[1];
return $_[0]->{$_};
} for qw(a1 a2 nn xx);
The above add all our methods to the SimpleMoo class, it does so via manipulating the symbol table, and is probably quite close to what Moo does (except Moo/Moose extends a meta class which your class inherits from), an even simpler example would be to define the accessors manually:
package SimpleMoo;
sub new {bless {}, $_[0]} # very very simple constructor (oldskool)
sub a1 {
my ($self) = shift;
$self->{a1} = $_[0] if exists $_[0]; # set value if provided
return $self->{a1}; # return the value
}
sub a2 {
my ($self) = shift;
$self->{a2} = $_[0] if exists $_[0]; # set value if provided
return $self->{a2}; # return the value
}
# ... copy and paste the for nn and xx ...
As you can see from the examples above, I am extending the class with methods that allow you to set and get values from the hash, but not implicitly setting up the hash structure as part of the object's constructor (which should take place inside new if you want to do this) - just like Moo and Moose.
This means that inspecting the object via something like keys %$omoo doesn't show any keys until they've been set (either via the call to ->new or by setting them with $omoo->a1("someValue").
As mentioned by ikegami, adding something like: default => undef to your has statements will cause Moo/Moose to instantiate the value when the object is first constructed.
I hope this explains how $self contains the object data, which contains only keys previously set by methods or constructors.
how to get all_attributes in case of Moo?
Unfortunately the Moo api provides no method to list all defined attributes so I cannot offer a supported solution, but may I offer one of many (non-perfect highly un-recommended) solutions:
package SimpleMoo;
use Moo;
our #attr_list;
sub my_has {push #attr_list, $_[0]; has #_}
sub get_all_attributes {#attr_list}
my_has $_ => ( is => 'rw', predicate => 1) for (qw(a1 a2 nn xx));
package main;
my $omoo = SimpleMoo->new();
say $_ for $omoo->get_all_attributes;
I'm sure someone from Moo, will be able to provide a better way of accessing the meta class used in Moo to get this list. But I'm sure either solution is not recommended.
Ultimately, you are using Moose and Moo, you shouldn't be inspecting the underlying object, both modules use magic (symbol table modification and class inheritance, and all sorts) and you cannot rely on the underlying object nor the class itself to have the attributes and methods you expect them to have. if you have a use case where you want to list every attribute defined on a class, use Moose and inspect the meta class, or take control of you class by defining it yourself by hand.
You should also consider why you need a list of attributes from an object, perhaps you could list them yourself? what if someone inherits from your class? or you decide to mixin a role? would this break whatever code was trying to utilise a list of attributes on your object?
Related
I am new to perl and still learning oop in perl. I usually code in C, C++. It is required to bless an object to notify perl to search for methods in that package first. That's what bless does. And then every function call made with help of -> passes the instance itself as first parameter. Now I have a doubt in writing the constructor for a new object. Normally a constructor would normally look like:
sub new {
my %hash = {};
return bless {%hash}; #will automatically take this package as the class
}
Now I want to have two data members in my class so I can do something like this:
sub new {
my %hash = {};
$hash->{"table_header"} = shift #_; #add element to hash
$hash->{"body_content"} = shift #_;
return bless {%hash}; #will automatically take this package as the class
}
My question is that is this the only possible way. Can't we have multiple data members like in C and C++ and we do have to use strings like "table_header" and "body_content".
EDIT:
In C or C++ we can directly reference the data member(assume its public for now). Here there is one extra reference which has to be made. I wanted to know if there is any way we can have a C like object.
sub new {
my $table_header = shift #_;
my $body_content = shift #_;
#bless somehow
}
Hope this clears some confusion.
There are modules that make OOP in Perl easier. The most important is Moose:
use strict; use warnings;
package SomeObject;
use Moose; # this is now a Moose class
# declare some members. Note that everything is "public"
has table_header => (
is => 'ro', # read-only access
);
has body_content => (
is => 'rw', # read-write access
);
# a "new" method is autogenerated
# some method that uses these fields.
# Note that the members can only be accessed via methods.
# This guards against typos that can't be easily caught with hashes.
sub display {
my ($self) = #_;
my $underline = "=" x (length $self->table_header);
return $self->table_header . "\n" . $underline . "\n\n" . $self->body_content . "\n";
}
package main;
# the "new" takes keyword arguments
my $instance = SomeObject->new(
table_header => "This is a header",
body_content => "Some body content",
);
$instance->body_content("Different content"); # set a member
print $instance->display;
# This is a header
# ================
#
# Different content
If you get to know Moose, you will find an object system that is far more flexible than that in Java or C++, as it takes ideas from Perl6 and the Common Lisp Object System. Of course, this is fairly ugly, but it works well in practice.
Because of the way Perl OOP works, it isn't possible to have the instance members accessible as variables on their own. Well, almost. There is the experimental mop module which does exactly that.
use strict; use warnings;
use mop;
class SomeObject {
# Instance variables start with $!..., and behave like ordinary variables
# If you make them externally accessible with "is ro" or "is rw", then
# appropriate accessor methods are additionally generated.
# a private member with public read-only accessor,
# which has to be initialized in the constructor.
has $!table_header is ro = die 'Please specify a "table_header"!';
# a private member with public read-write accessor,
# which is optional.
has $!body_content is rw = "";
# new is autogenerated, as in Moose
method display() {
# arguments are handled automatically, so we could also do $self->table_header.
my $underline = "=" x (length $!table_header);
return "$!table_header\n$underline\n\n$!body_content\n";
}
}
# as seen in Moose
my $instance = SomeObject->new(
table_header => "This is a header",
body_content => "Some body content",
);
$instance->body_content("Different content"); # set a member, as in Moose
print $instance->display;
# This is a header
# ================
#
# Different content
Although it has pretty syntax, don't use mop right now for serious projects and stick to Moose instead. If Moose is too heavyweight for you, then you might enjoy lighter alternatives like Mouse or Moo (these three object systems are mostly compatible with each other).
You are getting confused between hashes and hash references. You are also forgetting that the first parameter to any method is the object reference or the name of the package. Perl constructors are inherited like any other method, so you must bless the new object into the correct package for polymorphism to work properly. This code is what you intended
sub new {
my $package = shift;
my %self;
$self{table_header} = shift;
$self{body_content} = shift;
bless \%self, $package;
}
I am not clear what you mean by “directly reference the data member”, but if you hoped that you could avoid writing $self everywhere so that every variable was implicitly an element of the hash then you cannot. Perl is far more flexible than most languages, and can use any blessed reference as an object instance. It is most common to use a hash, but occasionally a reference to an array, a scalar, or even a file handle is more appropriate. The cost of this flexibility is specifying exactly when you are referring to a member of the blessed hash. I don't see that it's too great a burden.
You can always write your code more concisely. The method above can be written
sub new {
my $package = shift;
my %self;
#self{qw/ table_header body_content /} = #_;
bless \%self, $package;
}
I have a class built with Moose that's essentially a data container for an article list. All the attributes - like name, number, price, quantity - are data. "Well, what else?", I can hear you say. So what else?
An evil conspiration of unfortunate circumstances now forces external functionality into that package: Tax calculation of the data in this class has to be performed by an external component. This external component is tightly coupled to an entire application including database and dependencies that ruin the component's testability, dragging it into the everything-coupled-together stew. (Even thinking about refactoring the tax component out of the stew is completely out of the question.)
So my idea is to have the class accept a coderef wrapping the tax calculation component. The class would then remain independent of the tax calculation implementation (and its possible nightmare of dependencies), and at the same time it would allow integration with the application environment.
has 'tax_calculator', is => 'ro', isa => 'CodeRef';
But then, I'd have added a non-data component to my class. Why is that a problem? Because I'm (ab)using $self->meta->get_attribute_list to assemble a data export for my class:
my %data; # need a plain hash, no objects
my #attrs = $self->meta->get_attribute_list;
$data{ $_ } = $self->$_ for #attrs;
return %data;
Now the coderef is part of the attribute list. I could filter it out, of course. But I'm unsure any of what I'm doing here is a sound way to proceed. So how would you handle this problem, perceived as the need to separate data attributes and behaviour attributes?
A possible half thought out solution: use inheritance. Create your class as you do today but with a calculate_tax method that dies if called (i.e. a virtual function). Then create subclass that overrides that method to call into the external system. You can test the base class and use the child class.
Alternate solution: use a role to add the calculate_tax method. You can create two roles: Calculate::Simple::Tax and Calculate::Real::Tax. When testing you add the simple role, in production you add the real role.
I whipped up this example, but I don't use Moose, so I may be crazy with respect to how to apply the role to the class. There may be some more Moosey way of doing this:
#!/usr/bin/perl
use warnings;
{
package Simple::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
use Moose::Util qw( apply_all_roles );
has price => ( is => "rw", isa => 'Int' ); #price in pennies
sub new_with_simple_tax {
my $class = shift;
my $obj = $class->new(#_);
apply_all_roles( $obj, "Simple::Tax" );
}
}
my $o = A->new_with_simple_tax(price => 100);
print $o->calculate_tax, " cents\n";
It appears as if the right way to do it in Moose is to use two roles. The first is applied to the class and contains the production code. The second is applied to an object you want to use in testing. It subverts the first method using an around method and never calls the original method:
#!/usr/bin/perl
use warnings;
{
package Complex::Tax;
use Moose::Role;
requires 'price';
sub calculate_tax {
my $self = shift;
print "complex was called\n";
#pretend this is more complex
return int($self->price * 0.15);
}
}
{
package Simple::Tax;
use Moose::Role;
requires 'price';
around calculate_tax => sub {
my ($orig_method, $self) = #_;
return int($self->price * 0.05);
}
}
{
package A;
use Moose;
has price => ( is => "rw", isa => 'Int' ); #price in pennies
with "Complex::Tax";
}
my $prod = A->new(price => 100);
print $prod->calculate_tax, " cents\n";
use Moose::Util qw/ apply_all_roles /;
my $test = A->new(price => 100);
apply_all_roles($test, 'Simple::Tax');
print $test->calculate_tax, " cents\n";
A couple of things come to mind:
Implement the tax calculation logic in a separate TaxCalculation class that has the article list and the tax calculator as attributes.
Use a mock object as the tax calculator when you test. The tax calculator could be stored in an attribute that by default creates the real tax calculator. The test passes in a mock object that has the same interface but doesn't do anything.
Actually that's not really an abuse of get_attribute_list since that's rather exactly how MooseX::Storage works[^1]. IF you are going to continue to use get_attribute_list to build your straight data you'll want to do what MooseX::Storage does and set up an attribute trait for "DoNotSerialize"[^2]:
package MyApp::Meta::Attribute::Trait::DoNotSerialize;
use Moose::Role;
# register this alias ...
package Moose::Meta::Attribute::Custom::Trait::DoNotSerialize;
sub register_implementation { 'MyApp::Meta::Attribute::Trait::DoNotSerialize' }
1;
__END__
You then can use this in your class like so:
has 'tax_calculator' => ( is => 'ro', isa => 'CodeRef', traits => ['DoNotSerialize'] );
and in your serialization code like so:
my %data; # need a plain hash, no objects
my #attrs = grep { !$_->does('MyApp::Meta::Attribute::Trait::DoNotSerialize') } $self->meta->get_all_attributes; # note the change from get_attribute_list
$data{ $_ } = $_->get_value($self) for #attrs; # note the inversion here too
return %data;
Ultimately though you will end up in a solution similar to the Role one that Chas proposes, and I just answered his follow up question regarding it here: How to handle mocking roles in Moose?.
Hope this helps.
[^1]: And since the most basic use-case for MooseX::Storage is doing exactly what you describe, I highly suggest looking at it to do what you're doing by hand here.
[^2]: Or simply re-use the one from MooseX::Storage creates.
I've been playing around with this code:
package Foo;
use Moose;
package main;
my $PACKAGE = "Foo";
{
no strict 'refs';
my $has = *{"${PACKAGE}::has"}{CODE};
my $with = *{"${PACKAGE}::with"}{CODE};
# Add a instance member to class $PACKAGE
$has->("bar", is => "rw", required => 1);
# Add a role to class $PACKAGE
$with->("some::role");
}
# Create an instance of $PACKAGE:
$PACKAGE->new(); # error: attribute 'bar' is required means we were successful
This allows me to create a Moose class at run-time, i.e. add instance members to a class, add roles, etc.
My question is: how can I import Moose into package $PACKAGE?
I know I can do this with eval: eval "package $PACKAGE; use Moose"; but I'm wondering if there is a solution along the lines of Moose->import(... $PACKAGE ...).
i.e., a way without using eval. Or is there a completely different way of creating and modifying Moose classes at run time?
You probably want to take a look at Moose::Meta::Class and its create method:
my $class = Moose::Meta::Class->create('Foo',
attributes => [attr => Moose::Meta::Attribute->new(is => 'ro'), ...],
roles => [...],
methods => {...},
superclasses => [...],
);
# Edit: Adding an attribute and method modifiers:
$class->add_attribute(otherattr => (is => 'ro'));
$class->add_around_method_modifier(methodname => sub { ... });
Moose::Meta::Class is a subclass of Class::MOP::Class, so you might want to peek into that one as well. With the above, you can specify roles, superclasses, attributes and methods, or you can first create and then add them via the MOP; whatever fits best.
For the attributes you'll want the Moose kind, which means Moose::Meta::Attribute objects. The constructor to that object is basically the same as using has.
You may want to use Class::MOP, see for example https://metacpan.org/module/Moose::Manual::MOP#ALTERING-CLASSES-WITH-THE-MOP
or
https://metacpan.org/module/Class::MOP::Class#SYNOPSIS
Call extends, with, has, before, after, around, override and augment in the Moose package instead of the ones exported by Moose, and pass the meta object of the class you are creating as an additional first argument.
use strict;
use warnings;
use feature qw( say );
use Moose qw( );
{ # Create MyClass on the fly.
my $meta = Moose->init_meta( for_class => 'MyClass' );
# Moose::with( $meta, 'MyRole' );
Moose::has( $meta, foo => (
is => 'ro',
));
}
say MyClass->new( foo => "Foo" )->foo; # Foo
I'm writing a module for a moose object. I would like to allow a user using this object (or myself...) add some fields on the fly as he/she desires. I can't define these fields a priori since I simply don't know what they will be.
I currently simply added a single field called extra of type hashref which is is set to rw, so users can simply put stuff in that hash:
# $obj is a ref to my Moose object
$obj->extra()->{new_thingie}="abc123"; # adds some arbitrary stuff to the object
say $obj->extra()->{new_thingie};
This works. But... is this a common practice? Any other (possibly more elegant) ideas?
Note I do not wish to create another module the extends this one, this really just for on-the-fly stuff I'd like to add.
I would probably do this via native traits:
has custom_fields => (
traits => [qw( Hash )],
isa => 'HashRef',
builder => '_build_custom_fields',
handles => {
custom_field => 'accessor',
has_custom_field => 'exists',
custom_fields => 'keys',
has_custom_fields => 'count',
delete_custom_field => 'delete',
},
);
sub _build_custom_fields { {} }
On an object you'd use this like the following:
my $val = $obj->custom_field('foo'); # get field value
$obj->custom_field('foo', 23); # set field to value
$obj->has_custom_field('foo'); # does a specific field exist?
$obj->has_custom_fields; # are there any fields?
my #names = $obj->custom_fields; # what fields are there?
my $value = $obj->delete_custom_field('foo'); # remove field value
A common use-case for stuff like this is adding optional introspectable data to exception and message classes.
If you haven't made the class immutable (there is a performance penalty for not doing that, in addition to my concerns about changing class definitions on the fly), you should be able to do that by getting the meta class for the object (using $meta = $object->meta) and using the add_attribute method in Class::MOP::Class.
#!/usr/bin/perl
package My::Class;
use Moose;
use namespace::autoclean;
package main;
my $x = My::Class->new;
my $meta = $x->meta;
$meta->add_attribute(
foo => (
accessor => 'foo',
)
);
$x->foo(42);
print $x->foo, "\n";
my $y = My::Class->new({ foo => 5 });
print $y->foo, "\n";
Output:
42
5
Just in case you want to add a method to an object and not to the whole class then have a look at something like MooseX::SingletonMethod.
E.g.
use 5.012;
use warnings;
{
package Foo;
use MooseX::SingletonMethod;
sub bar { 'bar' } # method available to all objects
}
my $foo = Foo->new;
$foo->add_singleton_method( baz => sub { 'baz!' } );
$foo->baz; # => baz!
So in above the method baz is only added to the object $foo and not to class Foo.
Hmmm... I wonder if I could implement a MooseX::SingletonAttribute?
Some previous SO answer using MooseX::SingletonMethod:
How do you replace a method of a Moose object at runtime?
How do I make a new Moose class and instantiate an object of that class at runtime?
And also this blog post maybe of use and/or interest: Easy Anonymous Objects
/I3az/
Even if it's not a good pratice to modify a class at runtime, you can simply make the meta-class mutable, add the attribute(s) and make class immutable again:
$ref->meta->make_mutable ;
$ref->meta->add_attribute($attr_name,%cfg) ;
$ref->meta->make_immmutable ;
I'm having a little trouble getting my head around the conceptual difference between an object and a class. I don't really understand the distinction between the two in any programming language, but currently I'm working with Perl, and Moose, so I'd prefer an explanation using those things.
Cheers
There are lots of "a class is a blueprint, an object is something built from that blueprint", but since you've asked for a specific example using Moose and Perl, I thought I'd provide one.
In this following example, we're going have a class named 'Hacker'. The class (like a blueprint) describes what hackers are (their attributes) and what they can do (their methods):
package Hacker; # Perl 5 spells 'class' as 'package'
use Moose; # Also enables strict and warnings;
# Attributes in Moose are declared with 'has'. So a hacker
# 'has' a given_name, a surname, a login name (which they can't change)
# and a list of languages they know.
has 'given_name' => (is => 'rw', isa => 'Str');
has 'surname' => (is => 'rw', isa => 'Str');
has 'login' => (is => 'ro', isa => 'Str');
has 'languages' => (is => 'rw', isa => 'ArrayRef[Str]');
# Methods are what a hacker can *do*, and are declared in basic Moose
# with subroutine declarations.
# As a simple method, hackers can return their full name when asked.
sub full_name {
my ($self) = #_; # $self is my specific hacker.
# Attributes in Moose are automatically given 'accessor' methods, so
# it's easy to query what they are for a specific ($self) hacker.
return join(" ", $self->given_name, $self->surname);
}
# Hackers can also say hello.
sub say_hello {
my ($self) = #_;
print "Hello, my name is ", $self->full_name, "\n";
return;
}
# Hackers can say which languages they like best.
sub praise_languages {
my ($self) = #_;
my $languages = $self->languages;
print "I enjoy programming in: #$languages\n";
return;
}
1; # Perl likes files to end in a true value for historical reasons.
Now that we've got our Hacker class, we can start making Hacker objects:
#!/usr/bin/perl
use strict;
use warnings;
use autodie;
use Hacker; # Assuming the above is in Hacker.pm
# $pjf is a Hacker object
my $pjf = Hacker->new(
given_name => "Paul",
surname => "Fenwick",
login => "pjf",
languages => [ qw( Perl C JavaScript) ],
);
# So is $jarich
my $jarich = Hacker->new(
given_name => "Jacinta",
surname => "Richardson",
login => "jarich",
languages => [ qw( Perl C Haskell ) ],
);
# $pjf can introduce themselves.
$pjf->say_hello;
$pjf->praise_languages;
print "\n----\n\n";
# So can $jarich
$jarich->say_hello;
$jarich->praise_languages;
This results in the following output:
Hello, my name is Paul Fenwick
I enjoy programming in: Perl C JavaScript
----
Hello, my name is Jacinta Richardson
I enjoy programming in: Perl C Haskell
If I want I can have as many Hacker objects as I like, but there's still only one Hacker class that describes how all of these work.
All the best,
Paul
A class is a type (like "SUV"). An object is an instance of a class ("David's SUV").
Perl-wise:
A class is a package--a specification. A set of behaviors and data mainly to aid those behaviors.
An object is typically a "hashref", that is a collection of specific data allowed by the behavior specification in the package (and inherited behaviors).
Now, a hashref might hold a code reference. In most cases, that's behavior. But the only way the object could use that specific behavior is for that to be specified by some class behavior inherited (or mixed in) that expects that there might be a coderef sitting at that location and invoke it.
Another way to think of it is a class is a blueprint for how an object will be built.
Objects are single instances of a Class.
You are an object of class Human
(Classes in Perl are modules with some special qualities, you should better first understand only the general case).
In perl class is nothing but it is a package name.
It has a common code for the objects.
object is a instance that has access the class's properties
and methods.
package vehicle;
sub vehicle_detail
{
($number,$model,$num_of_wheel)=#_;
print "My car Details:\n#_";
}
The above class vehicle can be used by any vehicle such as bike,car,van..etc.
The object is created by the operator bless.
$bike_name='honda';
$ref_bike=\$bike_name;
bless $ref_bike,'vehicle';
Now the bless creates the object honda for the class vehicle.
I don't see people using the terms the same way in other languages. That may be one reason for the question. I think maybe PHP users say "class" when they should say "object", a lot of the time?
Anyway, what about this example -- imagine you had to create two different database connections for two different databases:
my $oracle_database_handle = DBI->connect( <oracle connection details here> );
my $mysql_database_handle = DBI->connect( <mysql connection details here> );
you would have created two objects for doing two different things, but they're both the same kind of thing -- DBI database connections.