Merge branch 'blead'
gfx [Mon, 12 Oct 2009 07:51:11 +0000 (16:51 +0900)]
228 files changed:
.shipit
Changes
MANIFEST.SKIP
Makefile.PL
TODO [deleted file]
author/generate-mouse-tiny.pl
author/test-externals.pl
benchmarks/basic.pl
benchmarks/coercion.pl [new file with mode: 0755]
benchmarks/load_class.pl [new file with mode: 0755]
benchmarks/subtype.pl [new file with mode: 0755]
lib/Mouse.pm
lib/Mouse/Exporter.pm [new file with mode: 0644]
lib/Mouse/Meta/Attribute.pm
lib/Mouse/Meta/Class.pm
lib/Mouse/Meta/Method.pm
lib/Mouse/Meta/Method/Accessor.pm
lib/Mouse/Meta/Method/Constructor.pm
lib/Mouse/Meta/Method/Destructor.pm
lib/Mouse/Meta/Module.pm
lib/Mouse/Meta/Role.pm
lib/Mouse/Meta/Role/Composite.pm [new file with mode: 0644]
lib/Mouse/Meta/Role/Method.pm
lib/Mouse/Meta/TypeConstraint.pm
lib/Mouse/Object.pm
lib/Mouse/Role.pm
lib/Mouse/Spec.pm
lib/Mouse/Util.pm
lib/Mouse/Util/TypeConstraints.pm
t/000-recipes/001_point.t [deleted file]
t/000_recipes/002_schwartz_tutorial.t [moved from t/000-recipes/002_schwartz_tutorial.t with 100% similarity]
t/000_recipes/basics-recipe10.t [moved from t/000-recipes/basics-recipe10.t with 100% similarity]
t/000_recipes/moose_cookbook_basics_recipe1.t [new file with mode: 0644]
t/000_recipes/moose_cookbook_basics_recipe2.t [moved from t/000-recipes/moose_cookbook_basics_recipe2.t with 100% similarity]
t/000_recipes/moose_cookbook_basics_recipe3.t [moved from t/000-recipes/moose_cookbook_basics_recipe3.t with 100% similarity]
t/000_recipes/moose_cookbook_basics_recipe4.t [moved from t/000-recipes/moose_cookbook_basics_recipe4.t with 100% similarity]
t/000_recipes/moose_cookbook_basics_recipe5.t [moved from t/000-recipes/moose_cookbook_basics_recipe5.t with 100% similarity]
t/000_recipes/moose_cookbook_basics_recipe6.t [new file with mode: 0644]
t/000_recipes/moose_cookbook_extending_recipe3.t [new file with mode: 0644]
t/000_recipes/moose_cookbook_meta_recipe2.t [copied from t/000-recipes/moose_cookbook_meta_recipe3.t with 58% similarity]
t/000_recipes/moose_cookbook_meta_recipe3.t [moved from t/000-recipes/moose_cookbook_meta_recipe3.t with 83% similarity]
t/000_recipes/moose_cookbook_roles_recipe1.t [new file with mode: 0644]
t/000_recipes/moose_cookbook_roles_recipe2.t [moved from t/000-recipes/moose_cookbook_roles_recipe2.t with 100% similarity]
t/000_recipes/moose_cookbook_roles_recipe3.t [new file with mode: 0644]
t/001_mouse/001-strict.t [moved from t/001-strict.t with 100% similarity]
t/001_mouse/002-warnings.t [moved from t/002-warnings.t with 100% similarity]
t/001_mouse/003-mouse-object.t [moved from t/003-mouse-object.t with 100% similarity]
t/001_mouse/004-auto-subclass.t [moved from t/004-auto-subclass.t with 100% similarity]
t/001_mouse/005-extends.t [moved from t/005-extends.t with 100% similarity]
t/001_mouse/006-unimport.t [moved from t/006-unimport.t with 100% similarity]
t/001_mouse/007-attributes.t [moved from t/007-attributes.t with 86% similarity]
t/001_mouse/008-default.t [new file with mode: 0644]
t/001_mouse/009-default-code.t [moved from t/009-default-code.t with 100% similarity]
t/001_mouse/010-required.t [moved from t/010-required.t with 100% similarity]
t/001_mouse/011-lazy.t [moved from t/011-lazy.t with 100% similarity]
t/001_mouse/012-predicate.t [moved from t/012-predicate.t with 100% similarity]
t/001_mouse/013-clearer.t [moved from t/013-clearer.t with 100% similarity]
t/001_mouse/014-build.t [moved from t/014-build.t with 100% similarity]
t/001_mouse/015-demolish.t [copied from t/015-demolish.t with 100% similarity]
t/001_mouse/016-trigger.t [moved from t/016-trigger.t with 100% similarity]
t/001_mouse/017-default-reference.t [moved from t/017-default-reference.t with 100% similarity]
t/001_mouse/018-multiattr-has.t [moved from t/018-multiattr-has.t with 81% similarity]
t/001_mouse/019-handles.t [moved from t/019-handles.t with 100% similarity]
t/001_mouse/020-load-class.t [moved from t/020-load-class.t with 100% similarity]
t/001_mouse/021-weak-ref.t [moved from t/021-weak-ref.t with 100% similarity]
t/001_mouse/022-init-arg.t [new file with mode: 0644]
t/001_mouse/023-builder.t [moved from t/023-builder.t with 100% similarity]
t/001_mouse/024-isa.t [moved from t/024-isa.t with 100% similarity]
t/001_mouse/025-more-isa.t [moved from t/025-more-isa.t with 100% similarity, mode: 0644]
t/001_mouse/026-auto-deref.t [moved from t/026-auto-deref.t with 100% similarity]
t/001_mouse/027-modifiers.t [moved from t/027-modifiers.t with 100% similarity]
t/001_mouse/028-subclass-attr.t [moved from t/028-subclass-attr.t with 100% similarity]
t/001_mouse/029-new.t [moved from t/029-new.t with 100% similarity]
t/001_mouse/030-has-plus.t [moved from t/030-has-plus.t with 100% similarity]
t/001_mouse/031-clone.t [moved from t/031-clone.t with 80% similarity]
t/001_mouse/032-buildargs.t [moved from t/032-buildargs.t with 100% similarity]
t/001_mouse/033-requires.t [moved from t/033-requires.t with 100% similarity]
t/001_mouse/034-apply_all_roles.t [moved from t/034-apply_all_roles.t with 100% similarity]
t/001_mouse/035-apply-roles-to-roles.t [moved from t/035-apply-roles-to-roles.t with 100% similarity]
t/001_mouse/036-with-method-alias.t [moved from t/036-with-method-alias.t with 100% similarity]
t/001_mouse/037-dont-load-test-exception.t [moved from t/037-dont-load-test-exception.t with 100% similarity]
t/001_mouse/038-main.t [moved from t/038-main.t with 100% similarity]
t/001_mouse/039-subtype.t [moved from t/039-subtype.t with 68% similarity]
t/001_mouse/040-existing-subclass.t [moved from t/040-existing-subclass.t with 100% similarity]
t/001_mouse/041-enum.t [moved from t/041-enum.t with 100% similarity]
t/001_mouse/042-override.t [moved from t/042-override.t with 100% similarity]
t/001_mouse/043-parameterized-type.t [moved from t/043-parameterized-type.t with 72% similarity]
t/001_mouse/044-attribute-metaclass.t [moved from t/044-attribute-metaclass.t with 81% similarity]
t/001_mouse/045-import-into_level.t [moved from t/045-import-into_level.t with 100% similarity]
t/001_mouse/046-meta-add_attribute.t [moved from t/046-meta-add_attribute.t with 100% similarity]
t/001_mouse/047-attribute-metaclass-role.t [moved from t/047-attribute-metaclass-role.t with 100% similarity]
t/001_mouse/049-coercion-application-order.t [moved from t/049-coercion-application-order.t with 100% similarity, mode: 0644]
t/001_mouse/050-inherited-immutable-constructor-bug.t [moved from t/050-inherited-immutable-constructor-bug.t with 100% similarity, mode: 0644]
t/001_mouse/051_throw_error.t [moved from t/051_throw_error.t with 100% similarity, mode: 0644]
t/001_mouse/052-undefined-type-in-union.t [new file with mode: 0644]
t/001_mouse/053-extends-meta.t [new file with mode: 0644]
t/001_mouse/054-anon-leak.t [new file with mode: 0644]
t/001_mouse/055-exporter.t [new file with mode: 0644]
t/001_mouse/056-role-combine.t [new file with mode: 0644]
t/001_mouse/100-meta-class.t [moved from t/100-meta-class.t with 86% similarity]
t/001_mouse/101-meta-attribute.t [moved from t/101-meta-attribute.t with 100% similarity]
t/001_mouse/301-bugs-non-mouse.t [moved from t/301-bugs-non-mouse.t with 100% similarity]
t/001_mouse/400-define-role.t [moved from t/400-define-role.t with 100% similarity]
t/001_mouse/401-meta-role.t [moved from t/401-meta-role.t with 100% similarity]
t/001_mouse/402-attribute-application.t [moved from t/402-attribute-application.t with 100% similarity]
t/001_mouse/403-method-modifiers.t [moved from t/403-method-modifiers.t with 100% similarity]
t/001_mouse/404-role-overrides.t [moved from t/404-role-overrides.t with 100% similarity]
t/001_mouse/600-tiny-tiny.t [moved from t/600-tiny-tiny.t with 100% similarity]
t/001_mouse/601-tiny-mouse.t [moved from t/601-tiny-mouse.t with 100% similarity]
t/001_mouse/602-mouse-tiny.t [moved from t/602-mouse-tiny.t with 100% similarity]
t/001_mouse/603-octal-defaults.t [copied from t/603-octal-defaults.t with 100% similarity]
t/008-default.t [deleted file]
t/010_basics/001_basic_class_setup.t [new file with mode: 0755]
t/010_basics/002_require_superclasses.t [new file with mode: 0755]
t/010_basics/003_super_and_override.t [new file with mode: 0755]
t/010_basics/004_inner_and_augment.t [new file with mode: 0755]
t/010_basics/005_override_augment_inner_super.t [new file with mode: 0755]
t/010_basics/006_override_and_foreign_classes.t [new file with mode: 0755]
t/010_basics/007_always_strict_warnings.t [new file with mode: 0755]
t/010_basics/008_wrapped_method_cxt_propagation.t [new file with mode: 0755]
t/010_basics/009_import_unimport.t [new file with mode: 0755]
t/010_basics/011_moose_respects_type_constraints.t [new file with mode: 0755]
t/010_basics/013_create.t [new file with mode: 0755]
t/010_basics/014_create_anon.t [new file with mode: 0755]
t/010_basics/015_buildargs.t [new file with mode: 0755]
t/010_basics/016_load_into_main.t [new file with mode: 0755]
t/010_basics/017_error_handling.t [new file with mode: 0755]
t/010_basics/019-destruction.t [new file with mode: 0755]
t/010_basics/failing/010_method_modifier_with_regexp.t [new file with mode: 0755]
t/010_basics/failing/012_rebless.t [new file with mode: 0755]
t/010_basics/failing/018_methods.t [new file with mode: 0755]
t/010_basics/failing/020-global-destruction-helper.pl [new file with mode: 0755]
t/010_basics/failing/020-global-destruction.t [new file with mode: 0755]
t/010_basics/failing/021-instance-new.t [new file with mode: 0755]
t/020_attributes/002_attribute_writer_generation.t [new file with mode: 0644]
t/020_attributes/003_attribute_accessor_generation.t [new file with mode: 0644]
t/020_attributes/005_attribute_does.t [new file with mode: 0644]
t/020_attributes/006_attribute_required.t [new file with mode: 0644]
t/020_attributes/007_attribute_custom_metaclass.t [new file with mode: 0644]
t/020_attributes/008_attribute_type_unions.t [new file with mode: 0644]
t/020_attributes/011_more_attr_delegation.t [new file with mode: 0644]
t/020_attributes/012_misc_attribute_tests.t [new file with mode: 0644]
t/020_attributes/013_attr_dereference_test.t [new file with mode: 0644]
t/020_attributes/014_misc_attribute_coerce_lazy.t [new file with mode: 0644]
t/020_attributes/015_attribute_traits.t
t/020_attributes/016_attribute_traits_registered.t [new file with mode: 0755]
t/020_attributes/017_attribute_traits_n_meta.t [new file with mode: 0755]
t/020_attributes/018_no_init_arg.t [new file with mode: 0644]
t/020_attributes/020_trigger_and_coerce.t [new file with mode: 0644]
t/020_attributes/024_attribute_traits_parameterized.t [new file with mode: 0644]
t/020_attributes/025_chained_coercion.t [new file with mode: 0644]
t/020_attributes/026_attribute_without_any_methods.t [new file with mode: 0644]
t/020_attributes/029_accessor_context.t [new file with mode: 0644]
t/020_attributes/030_non_alpha_attr_names.t [new file with mode: 0644]
t/020_attributes/failing/001_attribute_reader_generation.t [new file with mode: 0644]
t/020_attributes/failing/004_attribute_triggers.t [new file with mode: 0644]
t/020_attributes/failing/009_attribute_inherited_slot_specs.t [new file with mode: 0644]
t/020_attributes/failing/010_attribute_delegation.t [new file with mode: 0644]
t/020_attributes/failing/019_attribute_lazy_initializer.t [new file with mode: 0644]
t/020_attributes/failing/021_method_generation_rules.t [new file with mode: 0644]
t/020_attributes/failing/022_legal_options_for_inheritance.t [new file with mode: 0644]
t/020_attributes/failing/023_attribute_names.t [new file with mode: 0644]
t/020_attributes/failing/027_accessor_override_method.t [new file with mode: 0644]
t/020_attributes/failing/028_no_slot_access.t [new file with mode: 0644]
t/020_attributes/failing/031_delegation_and_modifiers.t [new file with mode: 0644]
t/022-init-arg.t [deleted file]
t/030_roles/001_meta_role.t
t/030_roles/002_role.t
t/030_roles/008_role_conflict_edge_cases.t [moved from t/030_roles/failing/008_role_conflict_edge_cases.t with 95% similarity]
t/030_roles/010_run_time_role_composition.t [moved from t/030_roles/failing/010_run_time_role_composition.t with 100% similarity]
t/030_roles/013_method_aliasing_in_composition.t [moved from t/030_roles/failing/013_method_aliasing_in_composition.t with 95% similarity]
t/030_roles/014_more_alias_and_exclude.t [moved from t/030_roles/failing/014_more_alias_and_exclude.t with 100% similarity]
t/030_roles/015_runtime_roles_and_attrs.t [moved from t/030_roles/failing/015_runtime_roles_and_attrs.t with 100% similarity]
t/030_roles/016_runtime_roles_and_nonmoose.t [moved from t/030_roles/failing/016_runtime_roles_and_nonmoose.t with 100% similarity]
t/030_roles/017_extending_role_attrs.t [moved from t/030_roles/failing/017_extending_role_attrs.t with 100% similarity]
t/030_roles/018_runtime_roles_w_params.t [moved from t/030_roles/failing/018_runtime_roles_w_params.t with 78% similarity]
t/030_roles/033_role_exclusion_and_alias_bug.t [moved from t/030_roles/failing/033_role_exclusion_and_alias_bug.t with 98% similarity]
t/030_roles/037_create_role_subclass.t [moved from t/030_roles/failing/037_create_role_subclass.t with 100% similarity]
t/030_roles/failing/012_method_exclusion_in_composition.t
t/030_roles/failing/020_role_composite.t
t/030_roles/failing/021_role_composite_exclusion.t
t/040_type_constraints/009_union_types_and_coercions.t [new file with mode: 0755]
t/040_type_constraints/015_enum.t [new file with mode: 0755]
t/040_type_constraints/017_subtyping_union_types.t [new file with mode: 0755]
t/040_type_constraints/025_type_coersion_on_lazy_attributes.t [new file with mode: 0755]
t/050_metaclasses/001_custom_attr_meta_with_roles.t [new file with mode: 0644]
t/050_metaclasses/002_custom_attr_meta_as_role.t [new file with mode: 0644]
t/100_bugs/001_subtype_quote_bug.t [new file with mode: 0644]
t/100_bugs/002_subtype_conflict_bug.t [new file with mode: 0644]
t/100_bugs/003_Moose_Object_error.t [new file with mode: 0644]
t/100_bugs/004_subclass_use_base_bug.t [new file with mode: 0644]
t/100_bugs/005_inline_reader_bug.t [new file with mode: 0644]
t/100_bugs/007_reader_precedence_bug.t [new file with mode: 0644]
t/100_bugs/009_augment_recursion_bug.t [new file with mode: 0644]
t/100_bugs/010_immutable_n_default_x2.t [new file with mode: 0644]
t/100_bugs/011_DEMOLISH_eats_exceptions.t [new file with mode: 0644]
t/100_bugs/012_DEMOLISH_eats_mini.t [new file with mode: 0644]
t/100_bugs/013_lazybuild_required_undef.t [new file with mode: 0644]
t/100_bugs/014_DEMOLISHALL.t [moved from t/015-demolish.t with 99% similarity]
t/100_bugs/016_inheriting_from_roles.t [new file with mode: 0644]
t/100_bugs/017_type_constraint_messages.t [new file with mode: 0644]
t/100_bugs/019_moose_octal_defaults.t [moved from t/603-octal-defaults.t with 91% similarity]
t/100_bugs/020_super_recursion.t [new file with mode: 0644]
t/100_bugs/021_DEMOLISHALL_shortcutted.t [new file with mode: 0644]
t/100_bugs/022_role_caller.t [new file with mode: 0644]
t/100_bugs/025_universal_methods_wrappable.t [new file with mode: 0644]
t/100_bugs/026_create_anon_recursion.t [new file with mode: 0644]
t/100_bugs/027_constructor_object_overload.t [new file with mode: 0644]
t/100_bugs/failing/006_handles_foreign_class_bug.t [new file with mode: 0644]
t/100_bugs/failing/018_immutable_metaclass_does_role.t [new file with mode: 0644]
t/100_bugs/failing/023_DEMOLISH_fails_without_metaclass.t [new file with mode: 0644]
t/100_bugs/failing/024_anon_method_metaclass.t [new file with mode: 0644]
t/300_immutable/001_immutable_moose.t
t/300_immutable/004_inlined_constructors_n_types.t [new file with mode: 0755]
t/300_immutable/005_multiple_demolish_inline.t [new file with mode: 0755]
t/800_shikabased/008-create_class.t
t/800_shikabased/010-isa-or.t
t/810_with_moose/201-squirrel.t [moved from t/100_with_moose/201-squirrel.t with 100% similarity]
t/810_with_moose/202-squirrel-role.t [moved from t/100_with_moose/202-squirrel-role.t with 100% similarity]
t/810_with_moose/500_moose_extends_mouse.t [moved from t/100_with_moose/500_moose_extends_mouse.t with 100% similarity]
t/810_with_moose/501_moose_coerce_mouse.t [moved from t/100_with_moose/501_moose_coerce_mouse.t with 100% similarity]
t/990_deprecated/001-export_to_level.t [new file with mode: 0644]
t/lib/Bar.pm [new file with mode: 0755]
t/lib/Foo.pm [new file with mode: 0755]
t/lib/MyMouseA.pm [new file with mode: 0644]
t/lib/MyMouseB.pm [new file with mode: 0644]
t/lib/MyMouseObject.pm [new file with mode: 0644]
t/lib/Test/Mouse.pm

diff --git a/.shipit b/.shipit
index 910158d..e389c24 100644 (file)
--- a/.shipit
+++ b/.shipit
@@ -1,5 +1,5 @@
 # auto-generated shipit config file.
-steps = FindVersion, ChangeVersion, CheckVersionsMatch, CheckChangeLog, DistTest, Commit, Tag, MakeDist
+steps = FindVersion, ChangeAllVersions, CheckVersionsMatch, CheckChangeLog, DistTest, Commit, Tag, MakeDist
 
 git.tagpattern = %v
 git.push_to = origin
diff --git a/Changes b/Changes
index 2d63deb..adce3a8 100644 (file)
--- a/Changes
+++ b/Changes
@@ -1,5 +1,71 @@
 Revision history for Mouse
 
+0.37_06 Mon Oct 12 16:34:18 2009
+    * Mouse::Meta::Attribute
+        - Support handles => qr/pattern/ in has() (gfx)
+
+    * Mouse::Meta::Method::Destructor
+        - Locallize $@ and $? in DESTROY as Moose does (gfx)
+
+    * Mouse::Meta::Role
+        - Fix role application to instances (gfx)
+
+    * Tests
+        - Move t/*.t to t/001_moose/
+
+0.37_05 Fri Oct  9 15:21:43 2009
+    * Mouse::Exporter
+        - Add build_import_methods() (gfx)
+
+    * Mouse::Spec
+        - Add notes about Moose::Cookbook (gfx)
+
+    * Fix some minor bugs (gfx)
+
+0.37_04 Thu Oct  8 20:49:11 2009
+    * Mouse::Meta::Role::Composite
+        - Fix and improve role composition mechanism (gfx)
+
+    * Import a number of tests from Moose, and fix various bugs (gfx)
+
+    * Mouse::Tiny is always generated in Makefile.PL (gfx)
+
+0.37_03 Wed Oct  7 21:10:05 2009
+    * Mouse::Exporter
+        - Add Mouse::Exporter (gfx)
+    * Mouse::Meta::Method::Constructor
+        - Optimize generated constructors (gfx)
+    * Mouse::Meta::Role
+        - Implement role application to instances (gfx)
+
+0.37_02 Sun Oct  4 17:29:15 2009
+    * Mouse
+        - Implement the argument/inner keywords
+    * Mouse::Meta::Attribute
+        - Add get_read_method_ref() and get_write_method_ref() (gfx)
+        - Add find_attribute_by_name() (gfx)
+        - Fix clone_and_inherit_options() to deal with 'traits' (gfx)
+    * Mouse::Util
+        - Fix meta() method, which was not tested (gfx)
+    * Tests
+        - Port t/010_basics/*.t from Moose
+
+0.37_01 Thu Oct  1 15:32:58 2009
+    * Type coercions are stored to type constraints (gfx)
+
+    * Refactor the type parser to parse 'ArrayRef[Object|Int]' (gfx)
+
+    * Remove Class::MOP specific subroutines from Mouse::Meta::Module (gfx)
+      (this change might be reverted in the release version)
+        - version, authority, identifier,
+          get_all_metaclasses, store_metaclass_by_name,
+          weaken_metaclass, does_metaclass_exist, remove_metaclass_by_name
+
+    * Add new public utilities to Mouse::Util (gfx)
+        - class_of, the counterpart for Class::MOP::class_of
+        - get_metaclass_by_name for Class::MOP::get_metaclass_by_name
+
+
 0.37 Mon Sep 28 10:48:27 2009
     * Ensure backward compatibility by author/test-externa.pl (gfx)
 
index 9ba4f9d..8d65183 100644 (file)
@@ -45,3 +45,8 @@ nytprof
 
 author/benchmarks
 author/externals
+
+lib/Mouse/Tiny\.pm$
+
+# Mouse distributions
+Mouse-
index 9d8bc43..f9c1517 100755 (executable)
@@ -2,6 +2,9 @@ use strict;
 use warnings;
 use inc::Module::Install;
 
+system($^X, 'author/generate-mouse-tiny.pl', 'lib/Mouse/Tiny.pm') == 0
+    or warn "Cannot generate Mouse::Tiny: $!";
+
 name     'Mouse';
 all_from 'lib/Mouse.pm';
 
@@ -34,7 +37,6 @@ if ($Module::Install::AUTHOR) {
     } else {
         print "you don't have Moose $require_version. skipping moose compatibility test\n";
     }
-    system("author/generate-mouse-tiny.pl");
 }
 
 WriteAll check_nmake => 0;
@@ -51,7 +53,10 @@ sub create_moose_compatibility_test {
     # some test does not pass... currently skip it.
     my %SKIP_TEST = (
         '016-trigger.t'    => "trigger's argument is incompatble :(",
-        '010-isa-or.t'     => 'Mouse has a [BUG]',
+        '010-isa-or.t'     => "Mouse has a [BUG]",
+
+        '052-undefined-type-in-union.t' => "Mouse accepts undefined type as a member of union types",
+        '054-anon-leak.t'     => 'Moose has memory leaks',
 
         '600-tiny-tiny.t'     => "Moose doesn't support ::Tiny",
         '601-tiny-mouse.t'    => "Moose doesn't support ::Tiny",
@@ -69,7 +74,8 @@ sub create_moose_compatibility_test {
 
                 return if /failing/; # skip tests in failing/ directories which  are Moose specific
 
-                return if /100_with_moose/; # tests with Moose
+                return if /with_moose/; # tests with Moose
+                return if /100_bugs/;   # some tests require Mouse specific files
                 return if /deprecated/;
 
                 my $basename = File::Basename::basename($_);
diff --git a/TODO b/TODO
deleted file mode 100644 (file)
index 037cae5..0000000
--- a/TODO
+++ /dev/null
@@ -1,10 +0,0 @@
-TODO:
-
-Mouse
-
-* smart exporters
-
-MouseX
-
-* MouseX::Role::Parameterized
-
index 0484cdb..42dd7cb 100755 (executable)
@@ -2,12 +2,26 @@
 use strict;
 use warnings;
 use File::Find;
-use File::Slurp 'slurp';
-use List::MoreUtils 'uniq';
-use autodie;
+use Fatal qw(open close);
+#use File::Slurp 'slurp';
+#use List::MoreUtils 'uniq';
+#use autodie;
 
-unlink 'lib/Mouse/Tiny.pm'
-    if -e 'lib/Mouse/Tiny.pm';
+print "Generate Mouse::Tiny ...\n";
+
+sub slurp {
+    open my $in, '<', $_[0];
+    local $/;
+    return scalar <$in>;
+}
+sub uniq{
+    my %seen;
+    return grep{ !$seen{$_}++ } @_;
+}
+
+require 'lib/Mouse/Spec.pm';
+
+my $MouseTinyFile = shift || 'lib/Mouse/Tiny.pm';
 
 my @files;
 
@@ -15,56 +29,78 @@ find({
     wanted => sub {
         push @files, $_
             if -f $_
+            &&  /\.pm$/
             && !/Squirrel/
-            && !/TypeRegistory/
-            && !/\bouse/
-            && !/\.sw[po]$/
+            && !/Tiny/
+            && !/Spec/         # has no functionality
+            && !/TypeRegistry/ # deprecated
+            && !/\bouse/       # ouse.pm
     },
     no_chdir => 1,
 }, 'lib');
 
 my $mouse_tiny = '';
 
-for my $file (uniq 'lib/Mouse/Util.pm', sort @files) {
+for my $file (uniq
+        'lib/Mouse/Exporter.pm',
+        'lib/Mouse/Util.pm',
+        'lib/Mouse/Meta/TypeConstraint.pm',
+        'lib/Mouse/Util/TypeConstraints.pm',
+            sort @files) {
+
     my $contents = slurp $file;
 
     $contents =~ s/__END__\b.*//s;          # remove documentation
     $contents =~ s/1;\n*$//;                # remove success indicator
 
-    $contents =~ s/^use Mouse\S*\s*\n//mg;  # we're already loading everything
-    $contents =~ s/^use (Mouse\S*)\s*(.+);/BEGIN { $1->import($2) }/mg;
-
+    $mouse_tiny .= "BEGIN{ # #file\n";
     $mouse_tiny .= $contents;
+    $mouse_tiny .= "}\n";
 }
 
-open my $handle, '>lib/Mouse/Tiny.pm' or die "Can't write lib/Mouse/Tiny.pm: $!";
+open my $handle, ">$MouseTinyFile";
 
-print { $handle } << 'EOF';
-# THIS FILE IS AUTOGENERATED!
+print { $handle } << "EOF";
+# This file was generated by $0 from Mouse $Mouse::Spec::VERSION.
+#
+# ANY CHANGES MADE HERE WILL BE LOST!
 
+EOF
+
+print { $handle } << 'EOF';
 # if regular Mouse is loaded, bail out
 unless ($INC{'Mouse.pm'}) {
-eval <<'END_OF_TINY';
-
-# tell Perl we already have all of the Mouse files loaded:
 EOF
 
 for my $file (@files) {
     (my $inc = $file) =~ s{^lib/}{};
-    print { $handle } "\$INC{'$inc'} = __FILE__;\n";
+    printf { $handle } "%-45s = __FILE__;\n", "\$INC{'$inc'}";
 }
 
+print { $handle } << 'EOF';
+eval sprintf("#line %d %s\n", __LINE__, __FILE__) . <<'END_OF_TINY';
+
+# tell Perl we already have all of the Mouse files loaded:
+EOF
+
 print { $handle } "\n# and now their contents\n\n";
 
 print { $handle } $mouse_tiny;
 
-print { $handle } "END_OF_TINY\n} #unless\n\n";
+print { $handle } << 'EOF';
+END_OF_TINY
+    die $@ if $@;
+} # unless Mouse.pm is loaded
+EOF
 
 print { $handle } << 'EOF';
 package Mouse::Tiny;
-use base 'Mouse';
 
+Mouse::Exporter->setup_import_methods(also => 'Mouse');
+
+1;
 EOF
 
-print { $handle } "1;\n\n";
+close $handle;
 
+print "done.\n";
index 576883d..d5b3df4 100755 (executable)
@@ -16,8 +16,8 @@ my %dist = (
 
     'Data-Localize' => q{git://github.com/lestrrat/Data-Localize.git},
 
-    'AnyEvent-ReverseHTTP'
-                    => q{git://github.com/miyagawa/AnyEvent-ReverseHTTP.git},
+    'MouseX-AttributeHelpers'
+                    => q{git://github.com/masaki/mousex-attributehelpers.git},
 
     'HTML-Shakan'   => q{git://github.com/tokuhirom/html-shakan.git},
 );
index e53c836..34a4f09 100644 (file)
@@ -8,8 +8,13 @@ for my $klass (qw/Moose Mouse/) {
         package ${klass}One;
         use $klass;
         has n => (
-            is  => 'rw',
-            isa => 'Int',
+            is     => 'rw',
+            isa    => 'Int',
+        );
+        has m => (
+            is      => 'rw',
+            isa     => 'Int',
+            default => 42,
         );
         no $klass;
         __PACKAGE__->meta->make_immutable;
diff --git a/benchmarks/coercion.pl b/benchmarks/coercion.pl
new file mode 100755 (executable)
index 0000000..a473303
--- /dev/null
@@ -0,0 +1,58 @@
+#!perl
+use strict;
+use warnings;
+use Benchmark qw/cmpthese/;
+
+
+for my $klass (qw/Moose Mouse/) {
+    eval qq{
+        package ${klass}One;
+        use $klass;\r
+        use ${klass}::Util::TypeConstraints;
+\r
+        subtype 'NaturalNumber', as 'Int', where { \$_ > 0 };
+
+        coerce 'NaturalNumber',
+            from 'Str', via { 42 },
+        ;\r
+\r
+        has n => (
+            is     => 'rw',
+            isa    => 'NaturalNumber',
+            coerce => 1,
+        );
+        no $klass;
+        __PACKAGE__->meta->make_immutable;
+    };
+    die $@ if $@;
+}
+
+print "Class::MOP: $Class::MOP::VERSION\n";
+print "Moose:      $Moose::VERSION\n";
+print "Mouse:      $Mouse::VERSION\n";
+print "---- new\n";
+cmpthese(
+    -1 => {
+        map { my $x = $_; $_ => sub { $x->new(n => 'foo') } }
+        map { "${_}One" }
+        qw/Moose Mouse/
+    }
+);
+
+print "---- new,set\n";
+cmpthese(
+    -1 => {
+        map { my $y = $_; $_ => sub { $y->new(n => 'foo')->n('bar') } }
+        map { "${_}One" }
+        qw/Moose Mouse/
+    }
+);
+
+print "---- set\n";
+my %c = map { $_ => "${_}One"->new(n => 'foo') } qw/Moose Mouse/;
+cmpthese(
+    -1 => {
+        map { my $y = $_; $_ => sub { $c{$y}->n('bar') } }
+        qw/Moose Mouse/
+    }
+);
diff --git a/benchmarks/load_class.pl b/benchmarks/load_class.pl
new file mode 100755 (executable)
index 0000000..1c11228
--- /dev/null
@@ -0,0 +1,19 @@
+#!perl
+use strict;
+use warnings;
+use Benchmark qw/cmpthese/;
+
+use Class::MOP;
+use Mouse();
+
+print "Class::MOP $Class::MOP::VERSION\n";
+print "Mouse      $Mouse::VERSION\n";
+
+cmpthese -1 => {
+    'Class::MOP::load_class' => sub{
+        Class::MOP::load_class('Class::MOP::Class');
+    },
+    'Mouse::Util::load_class' => sub{
+        Mouse::Util::load_class('Class::MOP::Class');
+    },
+};
diff --git a/benchmarks/subtype.pl b/benchmarks/subtype.pl
new file mode 100755 (executable)
index 0000000..5c53508
--- /dev/null
@@ -0,0 +1,58 @@
+#!perl
+use strict;
+use warnings;
+use Benchmark qw/cmpthese/;
+
+for my $klass (qw/Moose Mouse/) {
+    eval qq{
+        package ${klass}One;
+        use $klass;\r
+        use ${klass}::Util::TypeConstraints;
+\r
+        subtype 'NaturalNumber', as 'Int', where { \$_ > 0 };\r
+\r
+        has n => (
+            is  => 'rw',
+            isa => 'NaturalNumber',
+        );
+        no $klass;
+        __PACKAGE__->meta->make_immutable;
+    };
+    die $@ if $@;
+}
+
+use Data::Dumper;
+$Data::Dumper::Deparse = 1;
+$Data::Dumper::Indent  = 1;
+print Mouse::Util::TypeConstraints::find_type_constraint('NaturalNumber')->dump(3);
+print Moose::Util::TypeConstraints::find_type_constraint('NaturalNumber')->dump(3);
+
+print "Class::MOP: $Class::MOP::VERSION\n";
+print "Moose:      $Moose::VERSION\n";
+print "Mouse:      $Mouse::VERSION\n";
+print "---- new\n";
+cmpthese(
+    -1 => {
+        map { my $x = $_; $_ => sub { $x->new(n => 3) } }
+        map { "${_}One" }
+        qw/Moose Mouse/
+    }
+);
+
+print "---- new,set\n";
+cmpthese(
+    -1 => {
+        map { my $y = $_; $_ => sub { $y->new(n => 3)->n(5) } }
+        map { "${_}One" }
+        qw/Moose Mouse/
+    }
+);
+
+print "---- set\n";
+my %c = map { $_ => "${_}One"->new(n => 3) } qw/Moose Mouse/;
+cmpthese(
+    -1 => {
+        map { my $y = $_; $_ => sub { $c{$y}->n(5) } }
+        qw/Moose Mouse/
+    }
+);
index b21d31e..12205ce 100644 (file)
@@ -1,15 +1,12 @@
 package Mouse;
 use 5.006_002;
 
-use strict;
-use warnings;
+use Mouse::Exporter; # enables strict and warnings
 
-our $VERSION = '0.37';
+our $VERSION = '0.37_06';
 
-use Exporter;
-
-use Carp 'confess';
-use Scalar::Util 'blessed';
+use Carp qw(confess);
+use Scalar::Util qw(blessed);
 
 use Mouse::Util qw(load_class is_class_loaded get_code_package not_supported);
 
@@ -20,28 +17,38 @@ use Mouse::Meta::Attribute;
 use Mouse::Object;
 use Mouse::Util::TypeConstraints ();
 
-our @ISA = qw(Exporter);
+Mouse::Exporter->setup_import_methods(
+    as_is => [qw(
+        extends with
+        has
+        before after around
+        override super
+        augment  inner
+    ),
+        \&Scalar::Util::blessed,
+        \&Carp::confess,
+   ],
+);
 
+# XXX: for backward compatibility
 our @EXPORT = qw(
     extends with
     has
     before after around
     override super
     augment  inner
-
     blessed confess
 );
 
-our %is_removable = map{ $_ => undef } @EXPORT;
-delete $is_removable{blessed};
-delete $is_removable{confess};
-
 sub extends { Mouse::Meta::Class->initialize(scalar caller)->superclasses(@_) }
 
 sub has {
     my $meta = Mouse::Meta::Class->initialize(scalar caller);
     my $name = shift;
 
+    $meta->throw_error(q{Usage: has 'name' => ( key => value, ... )})\r
+        if @_ % 2; # odd number of arguments
+
     $meta->add_attribute($_ => @_) for ref($name) ? @{$name} : $name;
 }
 
@@ -86,31 +93,36 @@ our @SUPER_ARGS;
 sub super {
     # This check avoids a recursion loop - see
     # t/100_bugs/020_super_recursion.t
-    return if defined $SUPER_PACKAGE && $SUPER_PACKAGE ne caller();
-    return unless $SUPER_BODY; $SUPER_BODY->(@SUPER_ARGS);
+    return if  defined $SUPER_PACKAGE && $SUPER_PACKAGE ne caller();
+    return if !defined $SUPER_BODY;
+    $SUPER_BODY->(@SUPER_ARGS);
 }
 
 sub override {
-    my $meta = Mouse::Meta::Class->initialize(caller);
-    my $pkg = $meta->name;
-
-    my $name = shift;
-    my $code = shift;
-
-    my $body = $pkg->can($name)
-        or confess "You cannot override '$name' because it has no super method";
+    # my($name, $method) = @_;
+    Mouse::Meta::Class->initialize(scalar caller)->add_override_method_modifier(@_);
+}
 
-    $meta->add_method($name => sub {
-        local $SUPER_PACKAGE = $pkg;
-        local @SUPER_ARGS = @_;
-        local $SUPER_BODY = $body;
+our %INNER_BODY;
+our %INNER_ARGS;
 
-        $code->(@_);
-    });
+sub inner {
+    my $pkg = caller();
+    if ( my $body = $INNER_BODY{$pkg} ) {
+        my $args = $INNER_ARGS{$pkg};
+        local $INNER_ARGS{$pkg};
+        local $INNER_BODY{$pkg};
+        return $body->(@{$args});
+    }
+    else {
+        return;
+    }
 }
 
-sub inner  { not_supported }
-sub augment{ not_supported }
+sub augment {
+    #my($name, $method) = @_;
+    Mouse::Meta::Class->initialize(scalar caller)->add_augment_method_modifier(@_);
+}
 
 sub init_meta {
     shift;
@@ -118,16 +130,10 @@ sub init_meta {
 
     my $class = $args{for_class}
                     or confess("Cannot call init_meta without specifying a for_class");
+
     my $base_class = $args{base_class} || 'Mouse::Object';
     my $metaclass  = $args{metaclass}  || 'Mouse::Meta::Class';
 
-    confess("The Metaclass $metaclass must be a subclass of Mouse::Meta::Class.")
-            unless $metaclass->isa('Mouse::Meta::Class');
-
-    # make a subtype for each Mouse class
-    Mouse::Util::TypeConstraints::class_type($class)
-        unless Mouse::Util::TypeConstraints::find_type_constraint($class);
-
     my $meta = $metaclass->initialize($class);
 
     $meta->add_method(meta => sub{
@@ -137,74 +143,25 @@ sub init_meta {
     $meta->superclasses($base_class)
         unless $meta->superclasses;
 
-    return $meta;
-}
-
-sub import {
-    my $class = shift;
-
-    strict->import;
-    warnings->import;
-
-    my $opts = do {
-        if (ref($_[0]) && ref($_[0]) eq 'HASH') {
-            shift @_;
-        } else {
-            +{ };
-        }
-    };
-    my $level = delete $opts->{into_level};
-       $level = 0 unless defined $level;
-    my $caller = caller($level);
-
-    # we should never export to main
-    if ($caller eq 'main') {
-        warn qq{$class does not export its sugar to the 'main' package.\n};
-        return;
-    }
-
-    $class->init_meta(
-        for_class  => $caller,
-    );
+    # make a class type for each Mouse class
+    Mouse::Util::TypeConstraints::class_type($class)
+        unless Mouse::Util::TypeConstraints::find_type_constraint($class);
 
-    if (@_) {
-        __PACKAGE__->export_to_level( $level+1, $class, @_);
-    } else {
-        # shortcut for the common case of no type character
-        no strict 'refs';
-        for my $keyword (@EXPORT) {
-            *{ $caller . '::' . $keyword } = *{__PACKAGE__ . '::' . $keyword};
-        }
-    }
+    return $meta;
 }
 
-sub unimport {
-    my $caller = caller;
-
-    my $stash = do{
-        no strict 'refs';
-        \%{$caller . '::'}
-    };
-
-    for my $keyword (@EXPORT) {
-        my $code;
-        if(exists $is_removable{$keyword}
-            && ($code = $caller->can($keyword))
-            && get_code_package($code) eq __PACKAGE__){
-
-            delete $stash->{$keyword};
-        }
-    }
-}
 
 1;
-
 __END__
 
 =head1 NAME
 
 Mouse - Moose minus the antlers
 
+=head1 VERSION
+
+This document describes Mouse version 0.37_06
+
 =head1 SYNOPSIS
 
     package Point;
@@ -275,12 +232,6 @@ should upgrade to Moose. We don't need two parallel sets of extensions!
 If you really must write a Mouse extension, please contact the Moose mailing
 list or #moose on IRC beforehand.
 
-=head2 Maintenance
-
-The original author of this module has mostly stepped down from maintaining
-Mouse. See L<http://www.nntp.perl.org/group/perl.moose/2009/04/msg653.html>.
-If you would like to help maintain this module, please get in touch with us.
-
 =head1 KEYWORDS
 
 =head2 C<< $object->meta -> Mouse::Meta::Class >>
diff --git a/lib/Mouse/Exporter.pm b/lib/Mouse/Exporter.pm
new file mode 100644 (file)
index 0000000..690a1b6
--- /dev/null
@@ -0,0 +1,297 @@
+package Mouse::Exporter;
+use strict;
+use warnings;
+
+use Carp qw(confess);
+
+my %SPEC;
+
+use constant _strict_bits => strict::bits(qw(subs refs vars));
+
+# it must be "require", because Mouse::Util depends on Mouse::Exporter,
+# which depends on Mouse::Util::import()
+require Mouse::Util;
+
+sub import{
+    $^H              |= _strict_bits;         # strict->import;
+    ${^WARNING_BITS}  = $warnings::Bits{all}; # warnings->import;
+    return;
+}
+
+
+sub setup_import_methods{
+    my($class, %args) = @_;
+
+    my $exporting_package = $args{exporting_package} ||= caller();
+
+    my($import, $unimport) = $class->build_import_methods(%args);
+
+    no strict 'refs';
+
+    *{$exporting_package . '::import'}    = $import;
+    *{$exporting_package . '::unimport'}  = $unimport;
+
+    # for backward compatibility
+    *{$exporting_package . '::export_to_level'} = sub{
+        my($package, $level, undef, @args) = @_; # the third argument is redundant
+        $package->import({ into_level => $level + 1 }, @args);
+    };
+    *{$exporting_package . '::export'} = sub{
+        my($package, $into, @args) = @_;
+        $package->import({ into => $into }, @args);
+    };
+    return;
+}
+
+sub build_import_methods{
+    my($class, %args) = @_;
+
+    my $exporting_package = $args{exporting_package} ||= caller();
+
+    $SPEC{$exporting_package} = \%args;
+
+    # canonicalize args
+    my @export_from;
+    if($args{also}){
+        my %seen;
+        my @stack = ($exporting_package);
+
+        while(my $current = shift @stack){
+            push @export_from, $current;
+
+            my $also = $SPEC{$current}{also} or next;
+            push @stack, grep{ !$seen{$_}++ } ref($also) ? @{ $also } : $also;
+        }
+    }
+    else{
+        @export_from = ($exporting_package);
+    }
+
+    {
+        my %exports;
+        my @removables;
+        my @all;
+
+        my @init_meta_methods;
+
+        foreach my $package(@export_from){
+            my $spec = $SPEC{$package} or next;
+
+            if(my $as_is = $spec->{as_is}){
+                foreach my $thingy (@{$as_is}){
+                    my($code_package, $code_name, $code);
+
+                    if(ref($thingy)){
+                        $code = $thingy;
+                        ($code_package, $code_name) = Mouse::Util::get_code_info($code);
+                    }
+                    else{
+                        no strict 'refs';
+                        $code_package = $package;
+                        $code_name    = $thingy;
+                        $code         = \&{ $code_package . '::' . $code_name };
+                   }
+
+                    push @all, $code_name;
+                    $exports{$code_name} = $code;
+                    if($code_package eq $package){
+                        push @removables, $code_name;
+                    }
+                }
+            }
+
+            if(my $init_meta = $package->can('init_meta')){
+                if(!grep{ $_ == $init_meta } @init_meta_methods){
+                    push @init_meta_methods, $init_meta;
+                }
+            }
+        }
+        $args{EXPORTS}    = \%exports;
+        $args{REMOVABLES} = \@removables;
+
+        $args{groups}{all}     ||= \@all;
+
+        if(my $default_list = $args{groups}{default}){
+            my %default;
+            foreach my $keyword(@{$default_list}){
+                $default{$keyword} = $exports{$keyword}
+                    || confess(qq{The $exporting_package package does not export "$keyword"});
+            }
+            $args{DEFAULT} = \%default;
+        }
+        else{
+            $args{groups}{default} ||= \@all;
+            $args{DEFAULT}           = $args{EXPORTS};
+        }
+
+        if(@init_meta_methods){
+            $args{INIT_META} = \@init_meta_methods;
+        }
+    }
+
+    return (\&do_import, \&do_unimport);
+}
+
+
+# the entity of general import()
+sub do_import {
+    my($package, @args) = @_;
+
+    my $spec = $SPEC{$package}
+        || confess("The package $package package does not use Mouse::Exporter");
+
+    my $into = _get_caller_package(ref($args[0]) ? shift @args : undef);
+
+    my @exports;
+
+    foreach my $arg(@args){
+        if($arg =~ s/^-//){
+            Mouse::Util::not_supported("-$arg");
+        }
+        elsif($arg =~ s/^://){
+            my $group = $spec->{groups}{$arg}
+                || confess(qq{The $package package does not export the group "$arg"});
+            push @exports, @{$group};
+        }
+        else{
+            push @exports, $arg;
+        }
+    }
+
+    $^H              |= _strict_bits;         # strict->import;
+    ${^WARNING_BITS}  = $warnings::Bits{all}; # warnings->import;
+
+    if($into eq 'main' && !$spec->{_export_to_main}){
+        warn qq{$package does not export its sugar to the 'main' package.\n};
+        return;
+    }
+
+    if($spec->{INIT_META}){
+        foreach my $init_meta(@{$spec->{INIT_META}}){
+            $into->$init_meta(for_class => $into);
+        }
+
+        # _apply_meta_traits($into); # TODO
+    }
+
+    if(@exports){
+        foreach my $keyword(@exports){
+            no strict 'refs';
+            *{$into.'::'.$keyword} = $spec->{EXPORTS}{$keyword}
+                || confess(qq{The $package package does not export "$keyword"});
+        }
+    }
+    else{
+        my $default = $spec->{DEFAULT};
+        while(my($keyword, $code) = each %{$default}){
+            no strict 'refs';
+            *{$into.'::'.$keyword} = $code;
+        }
+    }
+    return;
+}
+
+# the entity of general unimport()
+sub do_unimport {
+    my($package, $arg) = @_;
+
+    my $spec = $SPEC{$package}
+        || confess("The package $package does not use Mouse::Exporter");
+
+    my $from = _get_caller_package($arg);
+
+    my $stash = do{
+        no strict 'refs';
+        \%{$from . '::'}
+    };
+
+    for my $keyword (@{ $spec->{REMOVABLES} }) {
+        my $gv = \$stash->{$keyword};
+        if(ref($gv) eq 'GLOB' && *{$gv}{CODE} == $spec->{EXPORTS}{$keyword}){ # make sure it is from us
+            delete $stash->{$keyword};
+        }
+    }
+    return;
+}
+
+# 1 extra level because it's called by import so there's a layer\r
+# of indirection\r
+sub _LEVEL(){ 1 }
+
+sub _get_caller_package {
+    my($arg) = @_;
+
+    if(ref $arg){
+        return defined($arg->{into})       ? $arg->{into}
+             : defined($arg->{into_level}) ? scalar caller(_LEVEL + $arg->{into_level})
+             :                               scalar caller(_LEVEL);
+    }
+    else{
+        return scalar caller(_LEVEL);
+    }
+}
+
+#sub _spec{ %SPEC }
+
+1;
+
+__END__
+
+=head1 NAME
+
+Mouse::Exporter - make an import() and unimport() just like Mouse.pm
+
+=head1 SYNOPSIS
+
+    package MyApp::Mouse;\r
+\r
+    use Mouse ();\r
+    use Mouse::Exporter;\r
+\r
+    Mouse::Exporter->setup_import_methods(\r
+      as_is     => [ 'has_rw', 'other_sugar', \&Some::Random::thing ],\r
+      also      => 'Mouse',\r
+    );\r
+\r
+    sub has_rw {
+        my $meta = caller->meta;\r
+        my ( $name, %options ) = @_;\r
+        $meta->add_attribute(\r
+          $name,\r
+          is => 'rw',\r
+          %options,\r
+        );\r
+    }\r
+\r
+    # then later ...\r
+    package MyApp::User;\r
+
+    use MyApp::Mouse;\r
+\r
+    has 'name';\r
+    has_rw 'size';\r
+    thing;\r
+\r
+    no MyApp::Mouse;
+
+=head1 DESCRIPTION
+
+This module encapsulates the exporting of sugar functions in a\r
+C<Mouse.pm>-like manner. It does this by building custom C<import>,\r
+C<unimport> methods for your module, based on a spec you provide.\r
+
+Note that C<Mouse::Exporter> does not provide the C<with_meta> option,
+but you can easily get the metaclass by C<< caller->meta >> as L</SYNOPSIS> shows.
+
+=head1 METHODS
+
+=head2 C<< setup_import_methods( ARGS ) >>
+
+=head2 C<< build_import_methods( ARGS ) -> (\&import, \&unimport) >>
+
+=head1 SEE ALSO
+
+L<Moose::Exporter>
+
+=cut
+
index 1ef8a77..415ad53 100644 (file)
@@ -1,14 +1,12 @@
 package Mouse::Meta::Attribute;
-use strict;
-use warnings;
+use Mouse::Util qw(:meta); # enables strict and warnings
 
 use Carp ();
 
-use Mouse::Util qw(:meta);
-
 use Mouse::Meta::TypeConstraint;
 use Mouse::Meta::Method::Accessor;
 
+
 sub _process_options{
     my($class, $name, $args) = @_;
 
@@ -82,8 +80,7 @@ sub _process_options{
         $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_isa_type_constraint($args->{isa});
     }
     elsif(exists $args->{does}){
-        # TODO
-        # $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_does_type_constraint($args->{does});
+        $args->{type_constraint} = Mouse::Util::TypeConstraints::find_or_create_does_type_constraint($args->{does});
     }
     $tc = $args->{type_constraint};
 
@@ -142,11 +139,11 @@ sub new {
 
     $args{name} = $name;
 
-    my $instance = bless \%args, $class;
+    my $self = bless \%args, $class;
 
     # extra attributes
     if($class ne __PACKAGE__){
-        $class->meta->_initialize_instance($instance,\%args);
+        $class->meta->_initialize_object($self, \%args);
     }
 
 # XXX: there is no fast way to check attribute validity
@@ -156,7 +153,7 @@ sub new {
 #        Carp::cluck("Found unknown argument(s) passed to '$name' attribute constructor in '$class': @bad");
 #    }
 
-    return $instance
+    return $self;
 }
 
 # readers
@@ -185,9 +182,6 @@ sub builder              { $_[0]->{builder}                }
 sub should_auto_deref    { $_[0]->{auto_deref}             }
 sub should_coerce        { $_[0]->{coerce}                 }
 
-sub get_read_method      { $_[0]->{reader} || $_[0]->{accessor} }
-sub get_write_method     { $_[0]->{writer} || $_[0]->{accessor} }
-
 # predicates
 
 sub has_accessor         { exists $_[0]->{accessor}        }
@@ -205,13 +199,13 @@ sub has_builder          { exists $_[0]->{builder}         }
 sub has_read_method      { exists $_[0]->{reader} || exists $_[0]->{accessor} }
 sub has_write_method     { exists $_[0]->{writer} || exists $_[0]->{accessor} }
 
-sub _create_args {
+sub _create_args { # DEPRECATED
     $_[0]->{_create_args} = $_[1] if @_ > 1;
     $_[0]->{_create_args}
 }
 
 sub interpolate_class{
-    my($class, $name, $args) = @_;
+    my($class, $args) = @_;
 
     if(my $metaclass = delete $args->{metaclass}){
         $class = Mouse::Util::resolve_metaclass_alias( Attribute => $metaclass );
@@ -244,7 +238,7 @@ sub interpolate_class{
     return( $class, @traits );
 }
 
-sub canonicalize_args{
+sub canonicalize_args{ # DEPRECATED
     my ($self, $name, %args) = @_;
 
     Carp::cluck("$self->canonicalize_args has been deprecated."
@@ -254,7 +248,7 @@ sub canonicalize_args{
     return %args;
 }
 
-sub create {
+sub create { # DEPRECATED
     my ($self, $class, $name, %args) = @_;
 
     Carp::cluck("$self->create has been deprecated."
@@ -265,51 +259,56 @@ sub create {
     return $self;
 }
 
+sub _coerce_and_verify {
+    my($self, $value, $instance) = @_;
+
+    my $type_constraint = $self->{type_constraint};
+    return $value if !defined $type_constraint;
+
+    if ($self->should_coerce && $type_constraint->has_coercion) {
+        $value = $type_constraint->coerce($value);
+    }
+
+    $self->verify_against_type_constraint($value);
+
+    return $value;
+}
+
 sub verify_against_type_constraint {
     my ($self, $value) = @_;
-    my $tc = $self->type_constraint;
-    return 1 unless $tc;
 
-    local $_ = $value;
-    return 1 if $tc->check($value);
+    my $type_constraint = $self->{type_constraint};
+    return 1 if !$type_constraint;
+    return 1 if $type_constraint->check($value);
 
-    $self->verify_type_constraint_error($self->name, $value, $tc);
+    $self->verify_type_constraint_error($self->name, $value, $type_constraint);
 }
 
 sub verify_type_constraint_error {
     my($self, $name, $value, $type) = @_;
-    $self->throw_error("Attribute ($name) does not pass the type constraint because: " . $type->get_message($value));
+    $self->throw_error("Attribute ($name) does not pass the type constraint because: "
+        . $type->get_message($value));
 }
 
-sub coerce_constraint { ## my($self, $value) = @_;
+sub coerce_constraint { # DEPRECATED
     my $type = $_[0]->{type_constraint}
         or return $_[1];
-    return Mouse::Util::TypeConstraints->typecast_constraints($_[0]->associated_class->name, $type, $_[1]);
-}
 
-sub _canonicalize_handles {
-    my $self    = shift;
-    my $handles = shift;
+    Carp::cluck("coerce_constraint() has been deprecated, which was an internal utility anyway");
 
-    if (ref($handles) eq 'HASH') {
-        return %$handles;
-    }
-    elsif (ref($handles) eq 'ARRAY') {
-        return map { $_ => $_ } @$handles;
-    }
-    else {
-        $self->throw_error("Unable to canonicalize the 'handles' option with $handles");
-    }
+    return Mouse::Util::TypeConstraints->typecast_constraints($_[0]->associated_class->name, $type, $_[1]);
 }
 
 sub clone_and_inherit_options{
-    my $self = shift;
-    my $name = shift;
+    my($self, %args) = @_;
 
-    return ref($self)->new($name, %{$self}, (@_ == 1) ? %{$_[0]} : @_);
+    my($attribute_class, @traits) = ref($self)->interpolate_class(\%args);
+
+    $args{traits} = \@traits if @traits;
+    return $attribute_class->new($self->name, %{$self}, %args);
 }
 
-sub clone_parent {
+sub clone_parent { # DEPRECATED
     my $self  = shift;
     my $class = shift;
     my $name  = shift;
@@ -322,7 +321,7 @@ sub clone_parent {
     $self->clone_and_inherited_args($class, $name, %args);
 }
 
-sub get_parent_args {
+sub get_parent_args { # DEPRECATED
     my $self  = shift;
     my $class = shift;
     my $name  = shift;
@@ -336,27 +335,110 @@ sub get_parent_args {
     $self->throw_error("Could not find an attribute by the name of '$name' to inherit from");
 }
 
+
+sub get_read_method {
+    $_[0]->{reader} || $_[0]->{accessor}
+}
+sub get_write_method {
+    $_[0]->{writer} || $_[0]->{accessor}
+}
+
+sub get_read_method_ref{
+    my($self) = @_;
+
+    $self->{_read_method_ref} ||= do{
+        my $metaclass = $self->associated_class
+            or $self->throw_error('No asocciated class for ' . $self->name);
+
+        my $reader = $self->{reader} || $self->{accessor};
+        if($reader){
+            $metaclass->name->can($reader);
+        }
+        else{
+            $self->accessor_metaclass->_generate_reader($self, $metaclass);
+        }
+    };
+}
+
+sub get_write_method_ref{
+    my($self) = @_;
+
+    $self->{_write_method_ref} ||= do{
+        my $metaclass = $self->associated_class
+            or $self->throw_error('No asocciated class for ' . $self->name);
+
+        my $reader = $self->{writer} || $self->{accessor};
+        if($reader){
+            $metaclass->name->can($reader);
+        }
+        else{
+            $self->accessor_metaclass->_generate_writer($self, $metaclass);
+        }
+    };
+}
+
+sub _canonicalize_handles {
+    my($self, $handles) = @_;
+
+    if (ref($handles) eq 'HASH') {
+        return %$handles;
+    }
+    elsif (ref($handles) eq 'ARRAY') {
+        return map { $_ => $_ } @$handles;
+    }
+    elsif (ref($handles) eq 'Regexp') {
+        my $class_or_role = ($self->{isa} || $self->{does})
+            || $self->throw_error("Cannot delegate methods based on a Regexp without a type constraint (isa)");
+
+        my $meta = Mouse::Meta::Class->initialize("$class_or_role"); # "" for stringify
+        return map  { $_ => $_ }
+               grep { $_ ne 'meta' && !Mouse::Object->can($_) && $_ =~ $handles }
+                   $meta->isa('Mouse::Meta::Class') ? $meta->get_all_method_names : $meta->get_method_list;
+    }
+    else {
+        $self->throw_error("Unable to canonicalize the 'handles' option with $handles");
+    }
+}
+
+
 sub associate_method{
     my ($attribute, $method) = @_;
     $attribute->{associated_methods}++;
     return;
 }
 
+sub accessor_metaclass(){ 'Mouse::Meta::Method::Accessor' }
+
 sub install_accessors{
     my($attribute) = @_;
 
-    my $metaclass       = $attribute->{associated_class};
+    my $metaclass      = $attribute->{associated_class};
+    my $accessor_class = $attribute->accessor_metaclass;
 
-    foreach my $type(qw(accessor reader writer predicate clearer handles)){
+    foreach my $type(qw(accessor reader writer predicate clearer)){
         if(exists $attribute->{$type}){
-            my $installer    = '_install_' . $type;
+            my $generator = '_generate_' . $type;
+            my $code      = $accessor_class->$generator($attribute, $metaclass);
+            $metaclass->add_method($attribute->{$type} => $code);
+            $attribute->associate_method($code);
+        }
+    }
+
+    # install delegation
+    if(exists $attribute->{handles}){
+        my %handles = $attribute->_canonicalize_handles($attribute->{handles});
+        my $reader  = $attribute->get_read_method_ref;
 
-            Mouse::Meta::Method::Accessor->$installer($attribute, $attribute->{$type}, $metaclass);
+        while(my($handle_name, $method_to_call) = each %handles){
+            my $code = $accessor_class->_generate_delegation($attribute, $metaclass,
+                $reader, $handle_name, $method_to_call);
 
-            $attribute->{associated_methods}++;
+            $metaclass->add_method($handle_name => $code);
+            $attribute->associate_method($code);
         }
     }
 
+
     if($attribute->can('create') != \&create){
         # backword compatibility
         $attribute->create($metaclass, $attribute->name, %{$attribute});
@@ -503,6 +585,15 @@ on success, otherwise C<confess>es.
 Creates a new attribute in the owner class, inheriting options from parent classes.
 Accessors and helper methods are installed. Some error checking is done.
 
+=head2 C<< get_read_method_ref >>
+
+=head2 C<< get_write_method_ref >>
+
+Returns the subroutine reference of a method suitable for reading or
+writing the attribute's value in the associated class. These methods
+always return a subroutine reference, regardless of whether or not the
+attribute is read- or write-only.
+
 =head1 SEE ALSO
 
 L<Moose::Meta::Attribute>
index c7c4433..563a208 100644 (file)
@@ -1,17 +1,18 @@
 package Mouse::Meta::Class;
-use strict;
-use warnings;
+use Mouse::Util qw/:meta get_linear_isa not_supported/; # enables strict and warnings
 
 use Scalar::Util qw/blessed weaken/;
 
-use Mouse::Util qw/:meta get_linear_isa not_supported/;
-
 use Mouse::Meta::Method::Constructor;
 use Mouse::Meta::Method::Destructor;
 use Mouse::Meta::Module;
 our @ISA = qw(Mouse::Meta::Module);
 
-sub method_metaclass(){ 'Mouse::Meta::Method' } # required for get_method()
+sub method_metaclass()    { 'Mouse::Meta::Method'    }
+sub attribute_metaclass() { 'Mouse::Meta::Attribute' }
+
+sub constructor_class()   { 'Mouse::Meta::Method::Constructor' }
+sub destructor_class()    { 'Mouse::Meta::Method::Destructor'  }
 
 sub _construct_meta {
     my($class, %args) = @_;
@@ -25,10 +26,11 @@ sub _construct_meta {
         \@{ $args{package} . '::ISA' };
     };
 
-    #return Mouse::Meta::Class->initialize($class)->new_object(%args)
-    #    if $class ne __PACKAGE__;
-
-    return bless \%args, ref($class) || $class;
+    my $self = bless \%args, ref($class) || $class;
+    if(ref($self) ne __PACKAGE__){
+        $self->meta->_initialize_object($self, \%args);
+    }
+    return $self;
 }
 
 sub create_anon_class{
@@ -46,7 +48,13 @@ sub superclasses {
     my $self = shift;
 
     if (@_) {
-        Mouse::load_class($_) for @_;
+        foreach my $super(@_){
+            Mouse::Util::load_class($super);
+            my $meta = Mouse::Util::get_metaclass_by_name($super);
+            if($meta && $meta->isa('Mouse::Meta::Role')){
+                $self->throw_error("You cannot inherit from a Mouse Role ($super)");
+            }
+        }
         @{ $self->{superclasses} } = @_;
     }
 
@@ -57,6 +65,7 @@ sub find_method_by_name{
     my($self, $method_name) = @_;
     defined($method_name)
         or $self->throw_error('You must define a method name to find');
+
     foreach my $class( $self->linearized_isa ){
         my $method = $self->initialize($class)->get_method($method_name);
         return $method if defined $method;
@@ -77,6 +86,16 @@ sub get_all_method_names {
             $self->linearized_isa;
 }
 
+sub find_attribute_by_name{
+    my($self, $name) = @_;
+    my $attr;
+    foreach my $class($self->linearized_isa){
+        my $meta = Mouse::Util::get_metaclass_by_name($class) or next;
+        $attr = $meta->get_attribute($name) and last;
+    }
+    return $attr;
+}
+
 sub add_attribute {
     my $self = shift;
 
@@ -100,23 +119,16 @@ sub add_attribute {
             or $self->throw_error('You must provide a name for the attribute');
 
         if ($name =~ s/^\+//) { # inherited attributes
-            my $inherited_attr;
-
-            foreach my $class($self->linearized_isa){
-                my $meta = Mouse::Meta::Module::get_metaclass_by_name($class) or next;
-                $inherited_attr = $meta->get_attribute($name) and last;
-            }
-
-            defined($inherited_attr)
+            my $inherited_attr = $self->find_attribute_by_name($name)
                 or $self->throw_error("Could not find an attribute by the name of '$name' to inherit from in ".$self->name);
 
-            $attr = $inherited_attr->clone_and_inherit_options($name, \%args);
+            $attr = $inherited_attr->clone_and_inherit_options(%args);
         }
         else{
-            my($attribute_class, @traits) = Mouse::Meta::Attribute->interpolate_class($name, \%args);
+            my($attribute_class, @traits) = $self->attribute_metaclass->interpolate_class(\%args);
             $args{traits} = \@traits if @traits;
 
-            $attr = $attribute_class->new($name, \%args);
+            $attr = $attribute_class->new($name, %args);
         }
     }
 
@@ -131,16 +143,21 @@ sub add_attribute {
     return $attr;
 }
 
-sub compute_all_applicable_attributes { shift->get_all_attributes(@_) }
+sub compute_all_applicable_attributes {
+    Carp::cluck('compute_all_applicable_attributes() has been deprecated')
+        if _MOUSE_VERBOSE;
+    return shift->get_all_attributes(@_)
+}
+
 sub get_all_attributes {
     my $self = shift;
     my (@attr, %seen);
 
     for my $class ($self->linearized_isa) {
-        my $meta = $self->_metaclass_cache($class)
+        my $meta = Mouse::Util::get_metaclass_by_name($class)
             or next;
 
-        for my $name (keys %{ $meta->get_attribute_map }) {
+        for my $name ($meta->get_attribute_list) {
             next if $seen{$name}++;
             push @attr, $meta->get_attribute($name);
         }
@@ -155,14 +172,14 @@ sub new_object {
     my $self = shift;
     my %args = (@_ == 1 ? %{$_[0]} : @_);
 
-    my $instance = bless {}, $self->name;
+    my $object = bless {}, $self->name;
 
-    $self->_initialize_instance($instance, \%args);
-    return $instance;
+    $self->_initialize_object($object, \%args);
+    return $object;
 }
 
-sub _initialize_instance{
-    my($self, $instance, $args) = @_;
+sub _initialize_object{
+    my($self, $object, $args) = @_;
 
     my @triggers_queue;
 
@@ -171,18 +188,13 @@ sub _initialize_instance{
         my $key  = $attribute->name;
 
         if (defined($from) && exists($args->{$from})) {
-            $args->{$from} = $attribute->coerce_constraint($args->{$from})
-                if $attribute->should_coerce;
-
-            $attribute->verify_against_type_constraint($args->{$from});
-
-            $instance->{$key} = $args->{$from};
+            $object->{$key} = $attribute->_coerce_and_verify($args->{$from}, $object);
 
-            weaken($instance->{$key})
-                if ref($instance->{$key}) && $attribute->is_weak_ref;
+            weaken($object->{$key})
+                if ref($object->{$key}) && $attribute->is_weak_ref;
 
             if ($attribute->has_trigger) {
-                push @triggers_queue, [ $attribute->trigger, $args->{$from} ];
+                push @triggers_queue, [ $attribute->trigger, $object->{$key} ];
             }
         }
         else {
@@ -190,20 +202,14 @@ sub _initialize_instance{
                 unless ($attribute->is_lazy) {
                     my $default = $attribute->default;
                     my $builder = $attribute->builder;
-                    my $value = $attribute->has_builder
-                              ? $instance->$builder
-                              : ref($default) eq 'CODE'
-                                  ? $default->($instance)
-                                  : $default;
+                    my $value =   $builder                ? $object->$builder()
+                                : ref($default) eq 'CODE' ? $object->$default()
+                                :                           $default;
 
-                    $value = $attribute->coerce_constraint($value)
-                        if $attribute->should_coerce;
-                    $attribute->verify_against_type_constraint($value);
+                    $object->{$key} = $attribute->_coerce_and_verify($value, $object);
 
-                    $instance->{$key} = $value;
-
-                    weaken($instance->{$key})
-                        if ref($instance->{$key}) && $attribute->is_weak_ref;
+                    weaken($object->{$key})
+                        if ref($object->{$key}) && $attribute->is_weak_ref;
                 }
             }
             else {
@@ -216,35 +222,28 @@ sub _initialize_instance{
 
     foreach my $trigger_and_value(@triggers_queue){
         my($trigger, $value) = @{$trigger_and_value};
-        $trigger->($instance, $value);
+        $trigger->($object, $value);
     }
 
     if($self->is_anon_class){
-        $instance->{__METACLASS__} = $self;
+        $object->{__METACLASS__} = $self;
     }
 
-    return $instance;
+    return $object;
 }
 
 sub clone_object {
-    my $class    = shift;
-    my $instance = shift;
-    my %params   = (@_ == 1) ? %{$_[0]} : @_;
-
-    (blessed($instance) && $instance->isa($class->name))
-        || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($instance)");
+    my $class  = shift;
+    my $object = shift;
+    my %params = (@_ == 1) ? %{$_[0]} : @_;
 
-    my $clone = bless { %$instance }, ref $instance;
+    (blessed($object) && $object->isa($class->name))
+        || $class->throw_error("You must pass an instance of the metaclass (" . $class->name . "), not ($object)");
 
-    foreach my $attr ($class->get_all_attributes()) {
-        if ( defined( my $init_arg = $attr->init_arg ) ) {
-            if (exists $params{$init_arg}) {
-                $clone->{ $attr->name } = $params{$init_arg};
-            }
-        }
-    }
+    my $cloned = bless { %$object }, ref $object;
+    $class->_initialize_object($cloned, \%params);
 
-    return $clone;
+    return $cloned;
 }
 
 sub clone_instance {
@@ -260,17 +259,20 @@ sub make_immutable {
     my %args = (
         inline_constructor => 1,
         inline_destructor  => 1,
+        constructor_name   => 'new',
         @_,
     );
 
     $self->{is_immutable}++;
 
     if ($args{inline_constructor}) {
-        $self->add_method('new' => Mouse::Meta::Method::Constructor->generate_constructor_method_inline( $self ));
+        $self->add_method($args{constructor_name} =>
+            $self->constructor_class->_generate_constructor($self, \%args));
     }
 
     if ($args{inline_destructor}) {
-        $self->add_method('DESTROY' => Mouse::Meta::Method::Destructor->generate_destructor_method_inline( $self ));
+        $self->add_method(DESTROY =>
+            $self->destructor_class->_generate_destructor($self, \%args));
     }
 
     # Moose's make_immutable returns true allowing calling code to skip setting an explicit true value
@@ -284,7 +286,8 @@ sub is_immutable {  $_[0]->{is_immutable} }
 sub is_mutable   { !$_[0]->{is_immutable} }
 
 sub _install_modifier_pp{
-    my( $self, $into, $type, $name, $code ) = @_;
+    my( $self, $type, $name, $code ) = @_;
+    my $into = $self->name;
 
     my $original = $into->can($name)
         or $self->throw_error("The method '$name' is not found in the inheritance hierarchy for class $into");
@@ -349,7 +352,7 @@ sub _install_modifier_pp{
 }
 
 sub _install_modifier {
-    my ( $self, $into, $type, $name, $code ) = @_;
+    my ( $self, $type, $name, $code ) = @_;
 
     # load Class::Method::Modifiers first
     my $no_cmm_fast = do{
@@ -365,14 +368,14 @@ sub _install_modifier {
     else{
         my $install_modifier = Class::Method::Modifiers::Fast->can('_install_modifier');
         $impl = sub {
-            my ( $self, $into, $type, $name, $code ) = @_;
-            $install_modifier->(
-                $into,
-                $type,
-                $name,
-                $code
-            );
-            $self->{methods}{$name}++; # register it to the method map
+            my ( $self, $type, $name, $code ) = @_;
+            my $into = $self->name;
+            $install_modifier->($into, $type, $name, $code);
+
+            $self->add_method($name => do{
+                no strict 'refs';
+                \&{ $into . '::' . $name };
+            });
             return;
         };
     }
@@ -383,33 +386,64 @@ sub _install_modifier {
         *_install_modifier = $impl;
     }
 
-    $self->$impl( $into, $type, $name, $code );
+    $self->$impl( $type, $name, $code );
 }
 
 sub add_before_method_modifier {
     my ( $self, $name, $code ) = @_;
-    $self->_install_modifier( $self->name, 'before', $name, $code );
+    $self->_install_modifier( 'before', $name, $code );
 }
 
 sub add_around_method_modifier {
     my ( $self, $name, $code ) = @_;
-    $self->_install_modifier( $self->name, 'around', $name, $code );
+    $self->_install_modifier( 'around', $name, $code );
 }
 
 sub add_after_method_modifier {
     my ( $self, $name, $code ) = @_;
-    $self->_install_modifier( $self->name, 'after', $name, $code );
+    $self->_install_modifier( 'after', $name, $code );
 }
 
 sub add_override_method_modifier {
     my ($self, $name, $code) = @_;
 
+    if($self->has_method($name)){
+        $self->throw_error("Cannot add an override method if a local method is already present");
+    }
+
     my $package = $self->name;
 
-    my $body = $package->can($name)
+    my $super_body = $package->can($name)
         or $self->throw_error("You cannot override '$name' because it has no super method");
 
-    $self->add_method($name => sub { $code->($package, $body, @_) });
+    $self->add_method($name => sub {
+        local $Mouse::SUPER_PACKAGE = $package;
+        local $Mouse::SUPER_BODY    = $super_body;
+        local @Mouse::SUPER_ARGS    = @_;
+
+        $code->(@_);
+    });
+    return;
+}
+
+sub add_augment_method_modifier {
+    my ($self, $name, $code) = @_;
+    if($self->has_method($name)){
+        $self->throw_error("Cannot add an augment method if a local method is already present");
+    }
+
+    my $super = $self->find_method_by_name($name)
+        or $self->throw_error("You cannot augment '$name' because it has no super method");
+
+    my $super_package = $super->package_name;
+    my $super_body    = $super->body;
+
+    $self->add_method($name => sub{
+        local $Mouse::INNER_BODY{$super_package} = $code;
+        local $Mouse::INNER_ARGS{$super_package} = [@_];
+        $super_body->(@_);
+    });
+    return;
 }
 
 sub does_role {
@@ -419,8 +453,8 @@ sub does_role {
         || $self->throw_error("You must supply a role name to look for");
 
     for my $class ($self->linearized_isa) {
-        my $meta = Mouse::Meta::Module::class_of($class);
-        next unless $meta && $meta->can('roles');
+        my $meta = Mouse::Util::get_metaclass_by_name($class)
+            or next;
 
         for my $role (@{ $meta->roles }) {
 
@@ -432,7 +466,6 @@ sub does_role {
 }
 
 1;
-
 __END__
 
 =head1 NAME
index a018662..9567a28 100755 (executable)
@@ -1,8 +1,5 @@
 package Mouse::Meta::Method;
-use strict;
-use warnings;
-
-use Mouse::Util qw(:meta);
+use Mouse::Util qw(:meta); # enables strict and warnings
 
 use overload
     '&{}' => 'body',
index 4d7e3a9..1e788c8 100755 (executable)
@@ -1,10 +1,9 @@
 package Mouse::Meta::Method::Accessor;
-use strict;
-use warnings;
+use Mouse::Util; # enables strict and warnings
 use Scalar::Util qw(blessed);
 
-sub _install_accessor{
-    my (undef, $attribute, $method_name, $class, $type) = @_;
+sub _generate_accessor{
+    my (undef, $attribute, $class, $type) = @_;
 
     my $name          = $attribute->name;
     my $default       = $attribute->default;
@@ -13,66 +12,53 @@ sub _install_accessor{
     my $trigger       = $attribute->trigger;
     my $is_weak       = $attribute->is_weak_ref;
     my $should_deref  = $attribute->should_auto_deref;
-    my $should_coerce = $attribute->should_coerce;
+    my $should_coerce = (defined($constraint) && $constraint->has_coercion && $attribute->should_coerce);
 
-    my $compiled_type_constraint    = $constraint ? $constraint->{_compiled_type_constraint} : undef;
+    my $compiled_type_constraint = defined($constraint) ? $constraint->_compiled_type_constraint : undef;
 
     my $self  = '$_[0]';
-    my $key   = sprintf q{"%s"}, quotemeta $name;
+    my $key   = "q{$name}";
+    my $slot  = "$self\->{$key}";
 
     $type ||= 'accessor';
 
-    my $accessor = 
-        '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
-        "sub {\n";
+    my $accessor = sprintf(qq{#line 1 "%s for %s (%s)"\n}, $type, $name, __FILE__)
+                 . "sub {\n";
+
     if ($type eq 'accessor' || $type eq 'writer') {
         if($type eq 'accessor'){
             $accessor .= 
-                '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
                 'if (scalar(@_) >= 2) {' . "\n";
         }
         else{ # writer
             $accessor .= 
-                '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                'if(@_ < 2){ Carp::confess("Not enough arguments for writer '.$method_name.'") }'.
+                'if(@_ < 2){ Carp::confess("Not enough arguments for the writer of '.$name.'") }'.
                 '{' . "\n";
         }
                 
         my $value = '$_[1]';
 
-        if ($constraint) {
+        if (defined $constraint) {
             if ($should_coerce) {
                 $accessor .=
                     "\n".
-                    '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                    'my $val = Mouse::Util::TypeConstraints->typecast_constraints("'.$attribute->associated_class->name.'", $attribute->{type_constraint}, '.$value.');';
+                    'my $val = $constraint->coerce('.$value.');';
                 $value = '$val';
             }
-            if ($compiled_type_constraint) {
-                $accessor .= 
-                    "\n".
-                    '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                    'unless ($compiled_type_constraint->('.$value.')) {
-                        $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint});
-                    }' . "\n";
-            } else {
-                $accessor .= 
-                    "\n".
-                    '#line ' . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                    'unless ($constraint->check('.$value.')) {
-                        $attribute->verify_type_constraint_error($name, '.$value.', $attribute->{type_constraint});
-                    }' . "\n";
-            }
+            $accessor .= 
+                "\n".
+                '$compiled_type_constraint->('.$value.') or
+                    $attribute->verify_type_constraint_error($name, '.$value.', $constraint);' . "\n";
         }
 
         # if there's nothing left to do for the attribute we can return during
         # this setter
         $accessor .= 'return ' if !$is_weak && !$trigger && !$should_deref;
 
-        $accessor .= $self.'->{'.$key.'} = '.$value.';' . "\n";
+        $accessor .= "$slot = $value;\n";
 
         if ($is_weak) {
-            $accessor .= 'Scalar::Util::weaken('.$self.'->{'.$key.'}) if ref('.$self.'->{'.$key.'});' . "\n";
+            $accessor .= "Scalar::Util::weaken($slot) if ref $slot;\n";
         }
 
         if ($trigger) {
@@ -89,109 +75,115 @@ sub _install_accessor{
     }
 
     if ($attribute->is_lazy) {
-        $accessor .= $self.'->{'.$key.'} = ';
-
-        $accessor .= $attribute->has_builder
-                ? $self.'->$builder'
-                    : ref($default) eq 'CODE'
-                    ? '$default->('.$self.')'
-                    : '$default';
-        $accessor .= ' if !exists '.$self.'->{'.$key.'};' . "\n";
+        my $value;
+
+        if (defined $builder){
+            $value = "$self->\$builder()";
+        }
+        elsif (ref($default) eq 'CODE'){
+            $value = "$self->\$default()";
+        }
+        else{
+            $value = '$default';
+        }
+
+        $accessor .= "if(!exists $slot){\n";
+        if($should_coerce){
+            $accessor .= "$slot = \$constraint->coerce($value)";
+        }
+        elsif(defined $constraint){
+            $accessor .= "my \$tmp = $value;\n";
+            #XXX: The following 'defined and' check is for backward compatibility
+            $accessor .= "defined(\$tmp) and ";
+
+            $accessor .= "\$compiled_type_constraint->(\$tmp)";
+            $accessor .= " || \$attribute->verify_type_constraint_error(\$name, \$tmp, \$constraint);\n";
+            $accessor .= "$slot = \$tmp;\n";
+        }
+        else{
+            $accessor .= "$slot = $value;\n";
+        }
+        $accessor .= "}\n";
     }
 
     if ($should_deref) {
         if ($constraint->is_a_type_of('ArrayRef')) {
-            $accessor .= 'if (wantarray) {
-                return @{ '.$self.'->{'.$key.'} || [] };
-            }';
+            $accessor .= "return \@{ $slot || [] } if wantarray;\n";
         }
         elsif($constraint->is_a_type_of('HashRef')){
-            $accessor .= 'if (wantarray) {
-                return %{ '.$self.'->{'.$key.'} || {} };
-            }';
+            $accessor .= "return \%{ $slot || {} } if wantarray;\n";
         }
         else{
             $class->throw_error("Can not auto de-reference the type constraint " . $constraint->name);
         }
     }
 
-    $accessor .= 'return '.$self.'->{'.$key."};\n}";
+    $accessor .= "return $slot;\n}\n";
 
-    #print $accessor, "\n";
-    my $code = eval $accessor;
-    $attribute->throw_error($@) if $@;
+    #print "# class ", $class->name, "\n", $accessor, "\n";
+    my $code;
+    my $e = do{
+        local $@;
+        $code = eval $accessor;
+        $@;
+    };
+    die $e if $e;
 
-    $class->add_method($method_name => $code);
-    return;
+    return $code;
 }
 
-sub _install_reader{
+sub _generate_reader{
     my $class = shift;
-    $class->_install_accessor(@_, 'reader');
-    return;
+    return $class->_generate_accessor(@_, 'reader');
 }
 
-sub _install_writer{
+sub _generate_writer{
     my $class = shift;
-    $class->_install_accessor(@_, 'writer');
-    return;
+    return $class->_generate_accessor(@_, 'writer');
 }
 
 
-sub _install_predicate {
-    my (undef, $attribute, $method_name, $class) = @_;
+sub _generate_predicate {
+    my (undef, $attribute, $class) = @_;
 
     my $slot = $attribute->name;
-
-    $class->add_method($method_name => sub{
+    return sub{
         return exists $_[0]->{$slot};
-    });
-    return;
+    };
 }
 
-sub _install_clearer {
-    my (undef, $attribute, $method_name, $class) = @_;
+sub _generate_clearer {
+    my (undef, $attribute, $class) = @_;
 
     my $slot = $attribute->name;
 
-    $class->add_method($method_name => sub{
+   return sub{
         delete $_[0]->{$slot};
-    });
-    return;
+    };
 }
 
-sub _install_handles {
-    my (undef, $attribute, $handles, $class) = @_;
-
-    my $reader  = $attribute->reader || $attribute->accessor
-        or $class->throw_error("You must pass a reader method for '".$attribute->name."'");
-
-    my %handles = $attribute->_canonicalize_handles($handles);
-
-    foreach my $handle_name (keys %handles) {
-        my $method_to_call = $handles{$handle_name};
-
-        my $code = sub {
-            my $instance = shift;
-            my $proxy    = $instance->$reader();
-
-            my $error = !defined($proxy)                ? ' is not defined'
-                      : ref($proxy) && !blessed($proxy) ? qq{ is not an object (got '$proxy')}
-                                                        : undef;
-            if ($error) {
-                $instance->meta->throw_error(
-                    "Cannot delegate $handle_name to $method_to_call because "
-                        . "the value of "
-                        . $attribute->name
-                        . $error
-                 );
-            }
-            $proxy->$method_to_call(@_);
-        };
-        $class->add_method($handle_name => $code);
-    }
-    return;
+sub _generate_delegation{
+    my (undef, $attribute, $class, $reader, $handle_name, $method_to_call) = @_;
+
+    return sub {
+        my $instance = shift;
+        my $proxy    = $instance->$reader();
+
+        my $error = !defined($proxy)                ? ' is not defined'
+                  : ref($proxy) && !blessed($proxy) ? qq{ is not an object (got '$proxy')}
+                                                    : undef;
+        if ($error) {
+            $instance->meta->throw_error(
+                "Cannot delegate $handle_name to $method_to_call because "
+                    . "the value of "
+                    . $attribute->name
+                    . $error
+             );
+        }
+        $proxy->$method_to_call(@_);
+    };
 }
 
 
 1;
+__END__
index 596dccf..315051f 100644 (file)
@@ -1,36 +1,44 @@
 package Mouse::Meta::Method::Constructor;
-use strict;
-use warnings;
+use Mouse::Util; # enables strict and warnings
 
-sub generate_constructor_method_inline {
-    my ($class, $metaclass) = @_;
+sub _generate_constructor {
+    my ($class, $metaclass, $args) = @_;
 
     my $associated_metaclass_name = $metaclass->name;
+
     my @attrs         = $metaclass->get_all_attributes;
 
     my $buildall      = $class->_generate_BUILDALL($metaclass);
     my $buildargs     = $class->_generate_BUILDARGS($metaclass);
     my $processattrs  = $class->_generate_processattrs($metaclass, \@attrs);
 
-    my @compiled_constraints = map { $_ ? $_->{_compiled_type_constraint} : undef } map { $_->{type_constraint} } @attrs;
-
-    my $code = <<"...";
-    sub {
-        my \$class = shift;
-        return \$class->Mouse::Object::new(\@_)
-            if \$class ne q{$associated_metaclass_name};
-        $buildargs;
-        my \$instance = bless {}, \$class;
-        $processattrs;
-        $buildall;
-        return \$instance;
-    }
+    my @checks = map { $_ && $_->_compiled_type_constraint }
+                 map { $_->type_constraint } @attrs;
+
+    my $source = sprintf("#line %d %s\n", __LINE__, __FILE__).<<"...";
+        sub \{
+            my \$class = shift;
+            return \$class->Mouse::Object::new(\@_)
+                if \$class ne q{$associated_metaclass_name};
+            # BUILDARGS
+            $buildargs;
+            my \$instance = bless {}, \$class;
+            # process attributes
+            $processattrs;
+            # BUILDALL
+            $buildall;
+            return \$instance;
+        }
 ...
-
-    local $@;
-    my $res = eval $code;
-    die $@ if $@;
-    $res;
+    #warn $source;
+    my $code;
+    my $e = do{
+        local $@;
+        $code = eval $source;
+        $@;
+    };
+    die $e if $e;
+    return $code;
 }
 
 sub _generate_processattrs {
@@ -40,45 +48,50 @@ sub _generate_processattrs {
     my $has_triggers;
 
     for my $index (0 .. @$attrs - 1) {
+        my $code = '';
+
         my $attr = $attrs->[$index];
         my $key  = $attr->name;
-        my $code = '';
 
-        if (defined $attr->init_arg) {
-            my $from = $attr->init_arg;
+        my $init_arg        = $attr->init_arg;
+        my $type_constraint = $attr->type_constraint;
+        my $need_coercion;
 
-            $code .= "if (exists \$args->{'$from'}) {\n";
+        my $instance_slot  = "\$instance->{q{$key}}";
+        my $attr_var       = "\$attrs[$index]";
+        my $constraint_var;
 
-            if ($attr->should_coerce && $attr->type_constraint) {
-                $code .= "my \$value = Mouse::Util::TypeConstraints->typecast_constraints('".$attr->associated_class->name."', \$attrs[$index]->{type_constraint}, \$args->{'$from'});\n";
-            }
-            else {
-                $code .= "my \$value = \$args->{'$from'};\n";
-            }
+        if(defined $type_constraint){
+             $constraint_var = "$attr_var\->{type_constraint}";
+             $need_coercion  = ($attr->should_coerce && $type_constraint->has_coercion);
+        }
 
-            if ($attr->has_type_constraint) {
-                if ($attr->type_constraint->{_compiled_type_constraint}) {
-                    $code .= "unless (\$compiled_constraints[$index](\$value)) {";
-                } else {
-                    $code .= "unless (\$attrs[$index]->{type_constraint}->check(\$value)) {";
-                }
-                $code .= "
-                        \$attrs[$index]->verify_type_constraint_error(
-                            q{$key}, \$value, \$attrs[$index]->type_constraint
-                        )
-                    }
-                ";
-            }
+        $code .= "# initialize $key\n";
+
+        my $post_process = '';
+        if(defined $type_constraint){
+            $post_process .= "\$checks[$index]->($instance_slot)";
+            $post_process .= "  or $attr_var->verify_type_constraint_error(q{$key}, $instance_slot, $constraint_var);\n";
+        }
+        if($attr->is_weak_ref){
+            $post_process .= "Scalar::Util::weaken($instance_slot) if ref $instance_slot;\n";
+        }
+
+        if (defined $init_arg) {
+            my $value = "\$args->{q{$init_arg}}";
 
-            $code .= "\$instance->{q{$key}} = \$value;\n";
+            $code .= "if (exists $value) {\n";
 
-            if ($attr->is_weak_ref) {
-                $code .= "Scalar::Util::weaken( \$instance->{q{$key}} ) if ref( \$value );\n";
+            if($need_coercion){
+                $value = "$constraint_var->coerce($value)";
             }
 
+            $code .= "$instance_slot = $value;\n";
+            $code .= $post_process;
+
             if ($attr->has_trigger) {
                 $has_triggers++;
-                $code .= "push \@triggers, [\$attrs[$index]->{trigger}, \$value];\n";
+                $code .= "push \@triggers, [$attr_var\->{trigger}, $instance_slot];\n";
             }
 
             $code .= "\n} else {\n";
@@ -89,61 +102,38 @@ sub _generate_processattrs {
                 my $default = $attr->default;
                 my $builder = $attr->builder;
 
-                $code .= "my \$value = ";
-
-                if ($attr->should_coerce && $attr->type_constraint) {
-                    $code .= "Mouse::Util::TypeConstraints->typecast_constraints('".$attr->associated_class->name."', \$attrs[$index]->{type_constraint}, ";
+                my $value;
+                if (defined($builder)) {
+                    $value = "\$instance->$builder()";
                 }
-
-                    if ($attr->has_builder) {
-                        $code .= "\$instance->$builder";
-                    }
-                    elsif (ref($default) eq 'CODE') {
-                        $code .= "\$attrs[$index]->{default}->(\$instance)";
-                    }
-                    elsif (!defined($default)) {
-                        $code .= 'undef';
-                    }
-                    elsif ($default =~ /^\-?[0-9]+(?:\.[0-9]+)$/) {
-                        $code .= $default;
-                    }
-                    else {
-                        $code .= "'$default'";
-                    }
-
-                if ($attr->should_coerce) {
-                    $code .= ");\n";
+                elsif (ref($default) eq 'CODE') {
+                    $value = "$attr_var\->{default}->(\$instance)";
+                }
+                elsif (defined($default)) {
+                    $value = "$attr_var\->{default}";
                 }
                 else {
-                    $code .= ";\n";
+                    $value = 'undef';
                 }
 
-                if ($attr->has_type_constraint) {
-                    $code .= "{
-                        unless (\$attrs[$index]->{type_constraint}->check(\$value)) {
-                            \$attrs[$index]->verify_type_constraint_error(q{$key}, \$value, \$attrs[$index]->type_constraint)
-                        }
-                    }";
+                if($need_coercion){
+                    $value = "$constraint_var->coerce($value)";
                 }
 
-                $code .= "\$instance->{q{$key}} = \$value;\n";
-
-                if ($attr->is_weak_ref) {
-                    $code .= "Scalar::Util::weaken( \$instance->{q{$key}} ) if ref( \$value );\n";
-                }
+                $code .= "$instance_slot = $value;\n";
             }
         }
         elsif ($attr->is_required) {
             $code .= "Carp::confess('Attribute ($key) is required');";
         }
 
-        $code .= "}\n" if defined $attr->init_arg;
+        $code .= "}\n" if defined $init_arg;
 
         push @res, $code;
     }
 
     if($metaclass->is_anon_class){
-        push @res, q{$instnace->{__METACLASS__} = $metaclass;};
+        push @res, q{$instance->{__METACLASS__} = $metaclass;};
     }
 
     if($has_triggers){
@@ -155,9 +145,10 @@ sub _generate_processattrs {
 }
 
 sub _generate_BUILDARGS {
-    my($self, $metaclass) = @_;
+    my(undef, $metaclass) = @_;
 
-    if ($metaclass->name->can('BUILDARGS') && $metaclass->name->can('BUILDARGS') != \&Mouse::Object::BUILDARGS) {
+    my $class = $metaclass->name;
+    if ( $class->can('BUILDARGS') && $class->can('BUILDARGS') != \&Mouse::Object::BUILDARGS ) {
         return 'my $args = $class->BUILDARGS(@_)';
     }
 
@@ -175,7 +166,7 @@ sub _generate_BUILDARGS {
 }
 
 sub _generate_BUILDALL {
-    my ($class, $metaclass) = @_;
+    my (undef, $metaclass) = @_;
 
     return '' unless $metaclass->name->can('BUILD');
 
@@ -192,3 +183,4 @@ sub _generate_BUILDALL {
 }
 
 1;
+__END__
index fa0d025..28e260f 100644 (file)
@@ -1,36 +1,49 @@
 package Mouse::Meta::Method::Destructor;
-use strict;
-use warnings;
-
-sub generate_destructor_method_inline {
-    my ($class, $meta) = @_;
-
-    my $demolishall = do {
-        if ($meta->name->can('DEMOLISH')) {
-            my @code = ();
-            for my $class ($meta->linearized_isa) {
-                no strict 'refs';
-                if (*{$class . '::DEMOLISH'}{CODE}) {
-                    push @code, "${class}::DEMOLISH(\$self);";
-                }
-            }
-            join "\n", @code;
-        } else {
-            return sub { }; # no demolish =)
+use Mouse::Util; # enables strict and warnings
+
+sub _empty_DESTROY{ }
+
+sub _generate_destructor{
+    my (undef, $metaclass) = @_;
+
+    if(!$metaclass->name->can('DEMOLISH')){
+        return \&_empty_DESTROY;
+    }
+
+    my $demolishall = '';
+    for my $class ($metaclass->linearized_isa) {
+        no strict 'refs';
+        if (*{$class . '::DEMOLISH'}{CODE}) {
+            $demolishall .= "${class}::DEMOLISH(\$self);\n";
         }
-    };
+    }
 
-    my $code = <<"...";
+    my $source = sprintf("#line %d %s\n", __LINE__, __FILE__) . <<"...";
     sub {
         my \$self = shift;
-        $demolishall;
+        local \$?;
+
+        my \$e = do{
+            local \$@;
+            eval{
+                $demolishall;
+            };
+            \$@;
+        };
+        no warnings 'misc';
+        die \$e if \$e; # rethrow
     }
 ...
 
-    local $@;
-    my $res = eval $code;
-    die $@ if $@;
-    return $res;
+    my $code;
+    my $e = do{
+        local $@;
+        $code = eval $source;
+        $@;
+    };
+    die $e if $e;
+    return $code;
 }
 
 1;
+__END__
index 79998a3..5588f29 100755 (executable)
@@ -1,69 +1,67 @@
 package Mouse::Meta::Module;
-use strict;
-use warnings;
+use Mouse::Util qw/:meta get_code_package load_class not_supported/; # enables strict and warnings
 
 use Carp ();
 use Scalar::Util qw/blessed weaken/;
 
-use Mouse::Util qw/:meta get_code_package not_supported load_class/;
+my %METAS;
 
-{
-    my %METACLASS_CACHE;
-
-    # because Mouse doesn't introspect existing classes, we're forced to
-    # only pay attention to other Mouse classes
-    sub _metaclass_cache {
-        my($class, $name) = @_;
-        return $METACLASS_CACHE{$name};
-    }
+sub _metaclass_cache { # DEPRECATED
+    my($class, $name) = @_;
+    return $METAS{$name};
+}
 
-    sub initialize {
-        my($class, $package_name, @args) = @_;
+sub initialize {
+    my($class, $package_name, @args) = @_;
 
-        ($package_name && !ref($package_name))
-            || $class->throw_error("You must pass a package name and it cannot be blessed");
+    ($package_name && !ref($package_name))
+        || $class->throw_error("You must pass a package name and it cannot be blessed");
 
-        return $METACLASS_CACHE{$package_name}
-            ||= $class->_construct_meta(package => $package_name, @args);
-    }
+    return $METAS{$package_name}
+        ||= $class->_construct_meta(package => $package_name, @args);
+}
 
-    sub class_of{
-        my($class_or_instance) = @_;
-        return undef unless defined $class_or_instance;
-        return $METACLASS_CACHE{ blessed($class_or_instance) || $class_or_instance };
-    }
+sub class_of{
+    my($class_or_instance) = @_;
+    return undef unless defined $class_or_instance;
+    return $METAS{ ref($class_or_instance) || $class_or_instance };
+}
 
-    # Means of accessing all the metaclasses that have
-    # been initialized thus far
-    sub get_all_metaclasses         {        %METACLASS_CACHE         }
-    sub get_all_metaclass_instances { values %METACLASS_CACHE         }
-    sub get_all_metaclass_names     { keys   %METACLASS_CACHE         }
-    sub get_metaclass_by_name       { $METACLASS_CACHE{$_[0]}         }
-    sub store_metaclass_by_name     { $METACLASS_CACHE{$_[0]} = $_[1] }
-    sub weaken_metaclass            { weaken($METACLASS_CACHE{$_[0]}) }
-    sub does_metaclass_exist        { defined $METACLASS_CACHE{$_[0]} }
-    sub remove_metaclass_by_name    { delete $METACLASS_CACHE{$_[0]}  }
+# Means of accessing all the metaclasses that have
+# been initialized thus far
+#sub get_all_metaclasses         {        %METAS         }
+sub get_all_metaclass_instances { values %METAS         }
+sub get_all_metaclass_names     { keys   %METAS         }
+sub get_metaclass_by_name       { $METAS{$_[0]}         }
+#sub store_metaclass_by_name     { $METAS{$_[0]} = $_[1] }
+#sub weaken_metaclass            { weaken($METAS{$_[0]}) }
+#sub does_metaclass_exist        { defined $METAS{$_[0]} }
+#sub remove_metaclass_by_name    { delete $METAS{$_[0]}  }
 
-}
 
-sub _new{ Carp::croak("Mouse::Meta::Module is an abstract class") }
 
 sub name { $_[0]->{package} }
 
-sub version   { no strict 'refs'; ${shift->name.'::VERSION'}   }
-sub authority { no strict 'refs'; ${shift->name.'::AUTHORITY'} }
-sub identifier {
-    my $self = shift;
-    return join '-' => (
-        $self->name,
-        ($self->version   || ()),
-        ($self->authority || ()),
-    );
-}
+# The followings are Class::MOP specific methods
+
+#sub version   { no strict 'refs'; ${shift->name.'::VERSION'}   }
+#sub authority { no strict 'refs'; ${shift->name.'::AUTHORITY'} }
+#sub identifier {
+#    my $self = shift;
+#    return join '-' => (
+#       $self->name,
+#        ($self->version   || ()),
+#        ($self->authority || ()),
+#    );
+#}
 
 # add_attribute is an abstract method
 
-sub get_attribute_map {        $_[0]->{attributes}          }
+sub get_attribute_map { # DEPRECATED
+    Carp::cluck('get_attribute_map() has been deprecated');
+    return $_[0]->{attributes};
+}
+
 sub has_attribute     { exists $_[0]->{attributes}->{$_[1]} }
 sub get_attribute     {        $_[0]->{attributes}->{$_[1]} }
 sub get_attribute_list{ keys %{$_[0]->{attributes}}         }
@@ -86,10 +84,10 @@ sub add_method {
     }
 
     if(ref($code) ne 'CODE'){
-        not_supported 'add_method for a method object';
+        $code = \&{$code}; # coerce
     }
 
-    $self->{methods}->{$name}++; # Moose stores meta object here.
+    $self->{methods}->{$name} = $code; # Moose stores meta object here.
 
     my $pkg = $self->name;
     no strict 'refs';
@@ -113,13 +111,32 @@ sub _code_is_mine{
 sub has_method {
     my($self, $method_name) = @_;
 
-    return 1 if $self->{methods}->{$method_name};
+    defined($method_name)
+        or $self->throw_error('You must define a method name');
 
-    my $code = do{ no strict 'refs'; *{$self->{package} . '::' . $method_name}{CODE} };
+    return 1 if $self->{methods}{$method_name};
+
+    my $code = do{
+        no strict 'refs';
+        *{ $self->{package} . '::' . $method_name }{CODE};
+    };
 
     return $code && $self->_code_is_mine($code);
 }
 
+sub get_method_body{
+    my($self, $method_name) = @_;
+
+    defined($method_name)
+        or $self->throw_error('You must define a method name');
+
+    return $self->{methods}{$method_name} ||= do{
+        my $code = do{ no strict 'refs'; *{$self->{package} . '::' . $method_name}{CODE} };
+
+        ($code && $self->_code_is_mine($code)) ? $code : undef;
+    };
+}
+
 sub get_method{
     my($self, $method_name) = @_;
 
@@ -146,43 +163,42 @@ sub get_method_list {
 
 {
     my $ANON_SERIAL = 0;
-    my $ANON_PREFIX = 'Mouse::Meta::Module::__ANON__::';
 
     my %IMMORTALS;
 
     sub create {
-        my ($class, $package_name, %options) = @_;
-
-        $class->throw_error('You must pass a package name') if @_ == 1;
+        my($self, $package_name, %options) = @_;
 
+        my $class = ref($self) || $self;
+        $self->throw_error('You must pass a package name') if @_ < 2;
 
+        my $superclasses;
         if(exists $options{superclasses}){
-            if($class->isa('Mouse::Meta::Class')){
-                (ref $options{superclasses} eq 'ARRAY')
-                    || $class->throw_error("You must pass an ARRAY ref of superclasses");
-            }
-            else{ # role
+            if($self->isa('Mouse::Meta::Role')){
                 delete $options{superclasses};
             }
+            else{
+                $superclasses = delete $options{superclasses};
+                (ref $superclasses eq 'ARRAY')
+                    || $self->throw_error("You must pass an ARRAY ref of superclasses");
+            }
         }
 
-        my $attributes;
-        if(exists $options{attributes}){
-            $attributes = delete $options{attributes};
-           (ref $attributes eq 'ARRAY' || ref $attributes eq 'HASH')
-               || $class->throw_error("You must pass an ARRAY ref of attributes")
-           }
-
-        (ref $options{methods} eq 'HASH')
-            || $class->throw_error("You must pass a HASH ref of methods")
-                if exists $options{methods};
-
-        (ref $options{roles} eq 'ARRAY')
-            || $class->throw_error("You must pass an ARRAY ref of roles")
-                if exists $options{roles};
-
-
-        my @extra_options;
+        my $attributes = delete $options{attributes};
+        if(defined $attributes){
+            (ref $attributes eq 'ARRAY' || ref $attributes eq 'HASH')
+                || $self->throw_error("You must pass an ARRAY ref of attributes");
+        }
+        my $methods = delete $options{methods};
+        if(defined $methods){
+            (ref $methods eq 'HASH')
+                || $self->throw_error("You must pass a HASH ref of methods");
+        }
+        my $roles = delete $options{roles};
+        if(defined $roles){
+            (ref $roles eq 'ARRAY')
+                || $self->throw_error("You must pass an ARRAY ref of roles");
+        }
         my $mortal;
         my $cache_key;
 
@@ -193,14 +209,13 @@ sub get_method_list {
             if(!$mortal){
                     # something like Super::Class|Super::Class::2=Role|Role::1
                     $cache_key = join '=' => (
-                        join('|',      @{$options{superclasses} || []}),
-                        join('|', sort @{$options{roles}        || []}),
+                        join('|',      @{$superclasses || []}),
+                        join('|', sort @{$roles        || []}),
                     );
                     return $IMMORTALS{$cache_key} if exists $IMMORTALS{$cache_key};
             }
-            $package_name = $ANON_PREFIX . ++$ANON_SERIAL;
-
-            push @extra_options, (anon_serial_id => $ANON_SERIAL);
+            $options{anon_serial_id} = ++$ANON_SERIAL;
+            $package_name = $class . '::__ANON__::' . $ANON_SERIAL;
         }
 
         # instantiate a module
@@ -210,26 +225,17 @@ sub get_method_list {
             ${ $package_name . '::AUTHORITY' } = delete $options{authority} if exists $options{authority};
         }
 
-        my %initialize_options = %options;
-        delete @initialize_options{qw(
-            package
-            superclasses
-            attributes
-            methods
-            roles
-        )};
-        my $meta = $class->initialize( $package_name, %initialize_options, @extra_options);
-
-        Mouse::Meta::Module::weaken_metaclass($package_name)
+        my $meta = $self->initialize( $package_name, %options);
+
+        weaken $METAS{$package_name}
             if $mortal;
 
-        # FIXME totally lame
-        $meta->add_method('meta' => sub {
-            $class->initialize(ref($_[0]) || $_[0]);
+        $meta->add_method(meta => sub{
+            $self->initialize(ref($_[0]) || $_[0]);
         });
 
-        $meta->superclasses(@{$options{superclasses}})
-            if exists $options{superclasses};
+        $meta->superclasses(@{$superclasses})
+            if defined $superclasses;
 
         # NOTE:
         # process attributes first, so that they can
@@ -238,26 +244,28 @@ sub get_method_list {
         # I think this should be the order of things.
         if (defined $attributes) {
             if(ref($attributes) eq 'ARRAY'){
+                # array of Mouse::Meta::Attribute
                 foreach my $attr (@{$attributes}) {
-                    $meta->add_attribute($attr->{name} => $attr);
+                    $meta->add_attribute($attr);
                 }
             }
             else{
+                # hash map of name and attribute spec pairs
                 while(my($name, $attr) = each %{$attributes}){
                     $meta->add_attribute($name => $attr);
                 }
             }
         }
-        if (exists $options{methods}) {
-            foreach my $method_name (keys %{$options{methods}}) {
-                $meta->add_method($method_name, $options{methods}->{$method_name});
+        if (defined $methods) {
+            while(my($method_name, $method_body) = each %{$methods}){
+                $meta->add_method($method_name, $method_body);
             }
         }
-        if (exists $options{roles}){
-            Mouse::Util::apply_all_roles($package_name, @{$options{roles}});
+        if (defined $roles){
+            Mouse::Util::apply_all_roles($package_name, @{$roles});
         }
 
-        if(!$mortal && exists $meta->{anon_serial_id}){
+        if($cache_key){
             $IMMORTALS{$cache_key} = $meta;
         }
 
@@ -271,14 +279,19 @@ sub get_method_list {
 
         return if !$serial_id;
 
-        my $stash = $self->namespace;
-
+        # @ISA is a magical variable, so we clear it manually.
         @{$self->{superclasses}} = () if exists $self->{superclasses};
-        %{$stash} = ();
-        Mouse::Meta::Module::remove_metaclass_by_name($self->name);
+
+        # Then, clear the symbol table hash
+        %{$self->namespace} = ();
+
+        my $name = $self->name;
+        delete $METAS{$name};
+
+        $name =~ s/ $serial_id \z//xms;
 
         no strict 'refs';
-        delete ${$ANON_PREFIX}{ $serial_id . '::' };
+        delete ${$name}{ $serial_id . '::' };
 
         return;
     }
index ec08a5b..a3d3544 100644 (file)
@@ -1,8 +1,6 @@
 package Mouse::Meta::Role;
-use strict;
-use warnings;
+use Mouse::Util qw(:meta not_supported english_list); # enables strict and warnings
 
-use Mouse::Util qw(:meta not_supported english_list get_code_info);
 use Mouse::Meta::Module;
 our @ISA = qw(Mouse::Meta::Module);
 
@@ -18,10 +16,12 @@ sub _construct_meta {
     $args{required_methods} ||= [];
     $args{roles}            ||= [];
 
-#    return Mouse::Meta::Class->initialize($class)->new_object(%args)
-#        if $class ne __PACKAGE__;
+    my $self = bless \%args, ref($class) || $class;
+    if($class ne __PACKAGE__){
+        $self->meta->_initialize_object($self, \%args);
+    }
 
-    return bless \%args, ref($class) || $class;
+    return $self;
 }
 
 sub create_anon_role{
@@ -41,7 +41,9 @@ sub get_required_method_list{
 
 sub add_required_methods {
     my($self, @methods) = @_;
-    push @{$self->{required_methods}}, @methods;
+    my %required = map{ $_ => 1 } @{$self->{required_methods}};
+    push @{$self->{required_methods}}, grep{ !$required{$_}++ && !$self->has_method($_) } @methods;
+    return;
 }
 
 sub requires_method {
@@ -54,78 +56,32 @@ sub add_attribute {
     my $name = shift;
 
     $self->{attributes}->{$name} = (@_ == 1) ? $_[0] : { @_ };
+    return;
 }
 
-sub _canonicalize_apply_args{
-    my($self, $applicant, %args) = @_;
-
-    if($applicant->isa('Mouse::Meta::Class')){
-        $args{_to} = 'class';
-    }
-    elsif($applicant->isa('Mouse::Meta::Role')){
-        $args{_to} = 'role';
-    }
-    else{
-        $args{_to} = 'instance';
-
-        not_supported 'Application::ToInstance';
-    }
-
-    if($args{alias} && !exists $args{-alias}){
-        $args{-alias} = $args{alias};
-    }
-    if($args{excludes} && !exists $args{-excludes}){
-        $args{-excludes} = $args{excludes};
-    }
+sub _check_required_methods{
+    my($role, $applicant, $args) = @_;
 
-    if(my $excludes = $args{-excludes}){
-        $args{-excludes} = {}; # replace with a hash ref
-        if(ref $excludes){
-            %{$args{-excludes}} = (map{ $_ => undef } @{$excludes});
-        }
-        else{
-            $args{-excludes}{$excludes} = undef;
-        }
+    if($args->{_to} eq 'role'){
+        $applicant->add_required_methods($role->get_required_method_list);
     }
+    else{ # to class or instance
+        my $applicant_class_name = $applicant->name;
 
-    return \%args;
-}
-
-sub _check_required_methods{
-    my($role, $class, $args, @other_roles) = @_;
-
-    if($args->{_to} eq 'class'){
-        my $class_name = $class->name;
-        my $role_name  = $role->name;
         my @missing;
         foreach my $method_name(@{$role->{required_methods}}){
-            if(!$class_name->can($method_name)){
-                my $has_method      = 0;
-
-                foreach my $another_role_spec(@other_roles){
-                    my $another_role_name = $another_role_spec->[0];
-                    if($role_name ne $another_role_name && $another_role_name->can($method_name)){
-                        $has_method = 1;
-                        last;
-                    }
-                }
-
-                push @missing, $method_name if !$has_method;
-            }
+            next if exists $args->{aliased_methods}{$method_name};
+            next if exists $role->{methods}{$method_name};
+            next if $applicant_class_name->can($method_name);
+
+            push @missing, $method_name;
         }
         if(@missing){
-            $class->throw_error("'$role_name' requires the "
-                . (@missing == 1 ? 'method' : 'methods')
-                . " "
-                . english_list(map{ sprintf q{'%s'}, $_ } @missing)
-                . " to be implemented by '$class_name'");
-        }
-    }
-    elsif($args->{_to} eq 'role'){
-        # apply role($role) to role($class)
-        foreach my $method_name($role->get_required_method_list){
-            next if $class->has_method($method_name); # already has it
-            $class->add_required_methods($method_name);
+            $role->throw_error(sprintf "'%s' requires the method%s %s to be implemented by '%s'",
+                $role->name,
+                (@missing == 1 ? '' : 's'), # method or methods
+                english_list(map{ sprintf q{'%s'}, $_ } @missing),
+                $applicant_class_name);
         }
     }
 
@@ -133,10 +89,7 @@ sub _check_required_methods{
 }
 
 sub _apply_methods{
-    my($role, $class, $args) = @_;
-
-    my $role_name  = $role->name;
-    my $class_name = $class->name;
+    my($role, $applicant, $args) = @_;
 
     my $alias    = $args->{-alias};
     my $excludes = $args->{-excludes};
@@ -144,24 +97,25 @@ sub _apply_methods{
     foreach my $method_name($role->get_method_list){
         next if $method_name eq 'meta';
 
-        my $code = $role_name->can($method_name);
+        my $code = $role->get_method_body($method_name);
 
         if(!exists $excludes->{$method_name}){
-            if(!$class->has_method($method_name)){
-                $class->add_method($method_name => $code);
+            if(!$applicant->has_method($method_name)){
+                # The third argument $role is used in Role::Composite
+                $applicant->add_method($method_name => $code, $role);
             }
         }
 
-        if($alias && $alias->{$method_name}){
+        if(exists $alias->{$method_name}){
             my $dstname = $alias->{$method_name};
 
-            my $dstcode = do{ no strict 'refs'; *{$class_name . '::' . $dstname}{CODE} };
+            my $dstcode = $applicant->get_method_body($dstname);
 
             if(defined($dstcode) && $dstcode != $code){
-                $class->throw_error("Cannot create a method alias if a local method of the same name exists");
+                $role->throw_error("Cannot create a method alias if a local method of the same name exists");
             }
             else{
-                $class->add_method($dstname => $code);
+                $applicant->add_method($dstname => $code, $role);
             }
         }
     }
@@ -170,41 +124,35 @@ sub _apply_methods{
 }
 
 sub _apply_attributes{
-    my($role, $class, $args) = @_;
+    my($role, $applicant, $args) = @_;
 
-    if ($args->{_to} eq 'class') {
-        # apply role to class
-        for my $attr_name ($role->get_attribute_list) {
-            next if $class->has_attribute($attr_name);
-
-            my $spec = $role->get_attribute($attr_name);
-
-            $class->add_attribute($attr_name => %{$spec});
-        }
-    }
-    elsif($args->{_to} eq 'role'){
-        # apply role to role
-        for my $attr_name ($role->get_attribute_list) {
-            next if $class->has_attribute($attr_name);
+    for my $attr_name ($role->get_attribute_list) {
+        next if $applicant->has_attribute($attr_name);
 
-            my $spec = $role->get_attribute($attr_name);
-            $class->add_attribute($attr_name => $spec);
-        }
+        $applicant->add_attribute($attr_name => $role->get_attribute($attr_name));
     }
-
     return;
 }
 
 sub _apply_modifiers{
-    my($role, $class, $args) = @_;
+    my($role, $applicant, $args) = @_;
+
+    if(my $modifiers = $role->{override_method_modifiers}){
+        foreach my $method_name (keys %{$modifiers}){
+            $applicant->add_override_method_modifier($method_name => $modifiers->{$method_name});
+        }
+    }
+
+    for my $modifier_type (qw/before around after/) {
+        my $modifiers = $role->{"${modifier_type}_method_modifiers"}
+            or next;
 
-    for my $modifier_type (qw/override before around after/) {
         my $add_modifier = "add_${modifier_type}_method_modifier";
-        my $modifiers    = $role->{"${modifier_type}_method_modifiers"};
 
-        while(my($method_name, $modifier_codes) = each %{$modifiers}){
-            foreach my $code(ref($modifier_codes) eq 'ARRAY' ? @{$modifier_codes} : $modifier_codes){
-                $class->$add_modifier($method_name => $code);
+        foreach my $method_name (keys %{$modifiers}){
+            foreach my $code(@{ $modifiers->{$method_name} }){
+                next if $applicant->{"_applied_$modifier_type"}{$method_name, $code}++; # skip applied modifiers
+                $applicant->$add_modifier($method_name => $code);
             }
         }
     }
@@ -212,12 +160,12 @@ sub _apply_modifiers{
 }
 
 sub _append_roles{
-    my($role, $class, $args) = @_;
+    my($role, $applicant, $args) = @_;
 
-    my $roles = ($args->{_to} eq 'class') ? $class->roles : $class->get_roles;
+    my $roles = ($args->{_to} eq 'role') ? $applicant->get_roles : $applicant->roles;
 
     foreach my $r($role, @{$role->get_roles}){
-        if(!$class->does_role($r->name)){
+        if(!$applicant->does_role($r->name)){
             push @{$roles}, $r;
         }
     }
@@ -229,132 +177,90 @@ sub apply {
     my $self      = shift;
     my $applicant = shift;
 
-    my $args = $self->_canonicalize_apply_args($applicant, @_);
+    my %args = (@_ == 1) ? %{ $_[0] } : @_;
 
-    $self->_check_required_methods($applicant, $args);
-    $self->_apply_methods($applicant, $args);
-    $self->_apply_attributes($applicant, $args);
-    $self->_apply_modifiers($applicant, $args);
-    $self->_append_roles($applicant, $args);
-    return;
-}
+    my $instance;
 
-sub combine_apply {
-    my(undef, $class, @roles) = @_;
+    if($applicant->isa('Mouse::Meta::Class')){  # Application::ToClass
+        $args{_to} = 'class';
+    }
+    elsif($applicant->isa('Mouse::Meta::Role')){ # Application::ToRole
+        $args{_to} = 'role';
+    }
+    else{                                       # Appplication::ToInstance
+        $args{_to} = 'instance';
+        $instance = $applicant;
 
-    if($class->isa('Mouse::Object')){
-        not_supported 'Application::ToInstance';
+        $applicant = (Mouse::Util::class_of($instance) || 'Mouse::Meta::Class')->create_anon_class(
+            superclasses => [ref $instance],
+            cache        => 1,
+        );
     }
 
-    # check conflicting
-    my %method_provided;
-    my @method_conflicts;
-    my %attr_provided;
-    my %override_provided;
+    if($args{alias} && !exists $args{-alias}){
+        $args{-alias} = $args{alias};
+    }
+    if($args{excludes} && !exists $args{-excludes}){
+        $args{-excludes} = $args{excludes};
+    }
 
-    foreach my $role_spec (@roles) {
-        my $role      = $role_spec->[0]->meta;
-        my $role_name = $role->name;
+    $args{aliased_methods} = {};
+    if(my $alias = $args{-alias}){
+        @{$args{aliased_methods}}{ values %{$alias} } = ();
+    }
 
-        # methods
-        foreach my $method_name($role->get_method_list){
-            next if $class->has_method($method_name); # manually resolved
+    if(my $excludes = $args{-excludes}){
+        $args{-excludes} = {}; # replace with a hash ref
+        if(ref $excludes){
+            %{$args{-excludes}} = (map{ $_ => undef } @{$excludes});
+        }
+        else{
+            $args{-excludes}{$excludes} = undef;
+        }
+    }
 
-            my $code = do{ no strict 'refs'; \&{ $role_name . '::' . $method_name } };
+    $self->_check_required_methods($applicant, \%args);
+    $self->_apply_attributes($applicant, \%args);
+    $self->_apply_methods($applicant, \%args);
+    $self->_apply_modifiers($applicant, \%args);
+    $self->_append_roles($applicant, \%args);
 
-            my $c = $method_provided{$method_name};
 
-            if($c && $c->[0] != $code){
-                push @{$c}, $role;
-                push @method_conflicts, $c;
-            }
-            else{
-                $method_provided{$method_name} = [$code, $method_name, $role];
-            }
-        }
+    if(defined $instance){ # Application::ToInstance
+        # rebless instance
+        bless $instance, $applicant->name;
+        $applicant->_initialize_object($instance, $instance);
+    }
 
-        # attributes
-        foreach my $attr_name($role->get_attribute_list){
-            my $attr = $role->get_attribute($attr_name);
-            my $c    = $attr_provided{$attr_name};
-            if($c && $c != $attr){
-                $class->throw_error("We have encountered an attribute conflict with '$attr_name' "
-                                   . "during composition. This is fatal error and cannot be disambiguated.")
-            }
-            else{
-                $attr_provided{$attr_name} = $attr;
-            }
-        }
+    return;
+}
 
-        # override modifiers
-        foreach my $method_name($role->get_method_modifier_list('override')){
-            my $override = $role->get_override_method_modifier($method_name);
-            my $c        = $override_provided{$method_name};
-            if($c && $c != $override){
-                $class->throw_error( "We have encountered an 'override' method conflict with '$method_name' during "
-                                   . "composition (Two 'override' methods of the same name encountered). "
-                                   . "This is fatal error.")
-            }
-            else{
-                $override_provided{$method_name} = $override;
-            }
-        }
-    }
-    if(@method_conflicts){
-        my $error;
-
-        if(@method_conflicts == 1){
-            my($code, $method_name, @roles) = @{$method_conflicts[0]};
-            $class->throw_error(
-                sprintf q{Due to a method name conflict in roles %s, the method '%s' must be implemented or excluded by '%s'},
-                    english_list(map{ sprintf q{'%s'}, $_->name } @roles), $method_name, $class->name
-            );
-        }
-        else{
-            @method_conflicts = sort { $a->[0] cmp $b->[0] } @method_conflicts; # to avoid hash-ordering bugs
-            my $methods = english_list(map{ sprintf q{'%s'}, $_->[1] } @method_conflicts);
-            my $roles   = english_list(
-                map{ sprintf q{'%s'}, $_->name }
-                map{ my($code, $method_name, @roles) = @{$_}; @roles } @method_conflicts
-            );
-
-            $class->throw_error(
-                sprintf q{Due to method name conflicts in roles %s, the methods %s must be implemented or excluded by '%s'},
-                    $roles, $methods, $class->name
-            );
-        }
-    }
 
-    foreach my $role_spec (@roles) {
-        my($role_name, $args) = @{$role_spec};
+sub combine {
+    my($role_class, @role_specs) = @_;
 
-        my $role = $role_name->meta;
+    require 'Mouse/Meta/Role/Composite.pm'; # we don't want to create its namespace
 
-        $args = $role->_canonicalize_apply_args($class, %{$args});
+    my $composite = Mouse::Meta::Role::Composite->create_anon_role();
 
-        $role->_check_required_methods($class, $args, @roles);
-        $role->_apply_methods($class, $args);
-        $role->_apply_attributes($class, $args);
-        $role->_apply_modifiers($class, $args);
-        $role->_append_roles($class, $args);
+    foreach my $role_spec (@role_specs) {
+        my($role_name, $args) = @{$role_spec};
+        $role_name->meta->apply($composite, %{$args});
     }
-    return;
+    return $composite;
 }
 
 for my $modifier_type (qw/before after around/) {
 
     my $modifier = "${modifier_type}_method_modifiers";
+
     my $add_method_modifier =  sub {
         my ($self, $method_name, $method) = @_;
 
         push @{ $self->{$modifier}->{$method_name} ||= [] }, $method;
         return;
     };
-    my $has_method_modifiers = sub{
-        my($self, $method_name) = @_;
-        my $m = $self->{$modifier}->{$method_name};
-        return $m && @{$m} != 0;
-    };
+
     my $get_method_modifiers = sub {
         my ($self, $method_name) = @_;
         return @{ $self->{$modifier}->{$method_name} ||= [] }
@@ -362,8 +268,9 @@ for my $modifier_type (qw/before after around/) {
 
     no strict 'refs';
     *{ 'add_' . $modifier_type . '_method_modifier'  } = $add_method_modifier;
-    *{ 'has_' . $modifier_type . '_method_modifiers' } = $has_method_modifiers;
     *{ 'get_' . $modifier_type . '_method_modifiers' } = $get_method_modifiers;
+
+    # has_${modifier_type}_method_modifiers is moved into t::lib::Test::Mouse
 }
 
 sub add_override_method_modifier{
@@ -380,23 +287,11 @@ sub add_override_method_modifier{
     $self->{override_method_modifiers}->{$method_name} = $method;
 }
 
-sub has_override_method_modifier {
-    my ($self, $method_name) = @_;
-    return exists $self->{override_method_modifiers}->{$method_name};
-}
-
 sub get_override_method_modifier {
     my ($self, $method_name) = @_;
     return $self->{override_method_modifiers}->{$method_name};
 }
 
-sub get_method_modifier_list {
-    my($self, $modifier_type) = @_;
-
-    return keys %{ $self->{$modifier_type . '_method_modifiers'} };
-}
-
-# This is currently not passing all the Moose tests.
 sub does_role {
     my ($self, $role_name) = @_;
 
@@ -412,9 +307,7 @@ sub does_role {
     return 0;
 }
 
-
 1;
-
 __END__
 
 =head1 NAME
diff --git a/lib/Mouse/Meta/Role/Composite.pm b/lib/Mouse/Meta/Role/Composite.pm
new file mode 100644 (file)
index 0000000..02a0510
--- /dev/null
@@ -0,0 +1,116 @@
+package Mouse::Meta::Role::Composite;
+use Mouse::Util qw(english_list); # enables strict and warnings
+use Mouse::Meta::Role;
+our @ISA = qw(Mouse::Meta::Role);
+
+sub get_method_list{
+    my($self) = @_;
+    return keys %{ $self->{methods} };
+}
+
+sub add_method {
+    my($self, $method_name, $code, $role) = @_;
+
+    if( ($self->{methods}{$method_name} || 0) == $code){
+        # This role already has the same method.
+        return;
+    }
+
+    if($method_name ne 'meta'){
+        my $roles = $self->{composed_roles_by_method}{$method_name} ||= [];
+        push @{$roles}, $role;
+        if(@{$roles} > 1){
+            $self->{conflicting_methods}{$method_name}++;
+        }
+    }
+
+    $self->{methods}{$method_name} = $code;
+    # no need to add a subroutine to the stash
+    return;
+}
+
+sub get_method_body {
+    my($self, $method_name) = @_;
+    return $self->{methods}{$method_name};
+}
+
+sub has_method {
+    # my($self, $method_name) = @_;
+    return 0; # to fool _apply_methods() in combine()
+}
+
+sub has_attribute{
+    # my($self, $method_name) = @_;
+    return 0; # to fool _appply_attributes() in combine()
+}
+
+sub has_override_method_modifier{
+    # my($self, $method_name) = @_;
+    return 0; # to fool _apply_modifiers() in combine()
+}
+
+sub add_attribute{
+    my($self, $attr_name, $spec) = @_;
+
+    my $existing = $self->{attributes}{$attr_name};
+    if($existing && $existing != $spec){
+        $self->throw_error("We have encountered an attribute conflict with '$attr_name' "
+                         . "during composition. This is fatal error and cannot be disambiguated.");
+    }
+    $self->SUPER::add_attribute($attr_name, $spec);
+    return;
+}
+
+sub add_override_method_modifier{
+    my($self, $method_name, $code) = @_;
+
+    my $existing = $self->{override_method_modifiers}{$method_name};
+    if($existing && $existing != $code){
+        $self->throw_error( "We have encountered an 'override' method conflict with '$method_name' during "
+                          . "composition (Two 'override' methods of the same name encountered). "
+                          . "This is fatal error.")
+    }
+    $self->SUPER::add_override_method_modifier($method_name, $code);
+    return;
+}
+
+# components of apply()
+
+sub _apply_methods{
+    my($self, $applicant, $args) = @_;
+
+    if(exists $self->{conflicting_methods}){
+        my $applicant_class_name = $applicant->name;
+
+        my @conflicting = sort grep{ !$applicant_class_name->can($_) } keys %{ $self->{conflicting_methods} };
+
+        if(@conflicting == 1){
+            my $method_name = $conflicting[0];
+            my @roles       = sort @{ $self->{composed_roles_by_method}{$method_name} };
+            $self->throw_error(
+               sprintf q{Due to a method name conflict in roles %s, the method '%s' must be implemented or excluded by '%s'},
+                   english_list(map{ sprintf q{'%s'}, $_->name } @roles), $method_name, $applicant->name
+            );
+        }
+        elsif(@conflicting > 1){
+            my $methods = english_list(map{ sprintf q{'%s'}, $_ } @conflicting);
+
+            my %seen;
+            my $roles   = english_list(
+                sort map{ my $name = $_->name; $seen{$name}++ ? () : sprintf q{'%s'}, $name }
+                map{ @{$_} } @{ $self->{composed_roles_by_method} }{@conflicting}
+            );
+
+            $self->throw_error(
+               sprintf q{Due to method name conflicts in roles %s, the methods %s must be implemented or excluded by '%s'},
+                   $roles, $methods, $applicant->name
+            );
+        }
+    }
+
+    $self->SUPER::_apply_methods($applicant, $args);
+    return;
+}
+1;
+__END__
+
index 79b55d4..caa1a3b 100755 (executable)
@@ -1,12 +1,10 @@
 package Mouse::Meta::Role::Method;
-use strict;
-use warnings;
+use Mouse::Util; # enables strict and warnings
 
 use Mouse::Meta::Method;
 our @ISA = qw(Mouse::Meta::Method);
 
 1;
-
 __END__
 
 =head1 NAME
index 13b4495..190d853 100644 (file)
 package Mouse::Meta::TypeConstraint;
-use strict;
-use warnings;
+use Mouse::Util qw(:meta); # enables strict and warnings
 
-use overload '""'     => sub { shift->{name} },   # stringify to tc name
-             fallback => 1;
+use overload
+    '""'     => sub { shift->{name} },   # stringify to tc name
+    fallback => 1;
 
-use Carp ();
+use Carp qw(confess);
+use Scalar::Util qw(blessed reftype);
 
-use Mouse::Util qw(:meta);
+my $null_check = sub { 1 };
 
 sub new {
-    my $class = shift;
-    my %args = @_;
-    my $name = $args{name} || '__ANON__';
+    my($class, %args) = @_;
 
-    my $check = $args{_compiled_type_constraint} or Carp::croak("missing _compiled_type_constraint");
-    if (ref $check eq 'Mouse::Meta::TypeConstraint') {
-        $check = $check->{_compiled_type_constraint};
+    $args{name} = '__ANON__' if !defined $args{name};
+
+    my $check = delete $args{optimized};
+
+    if($args{_compiled_type_constraint}){
+        Carp::cluck("'_compiled_type_constraint' has been deprecated, use 'optimized' instead")
+            if _MOUSE_VERBOSE;
+
+        $check = $args{_compiled_type_constraint};
     }
 
-    bless +{
-        name                      => $name,
-        _compiled_type_constraint => $check,
-        message                   => $args{message}
-    }, $class;
-}
+    if($check){
+        $args{hand_optimized_type_constraint} = $check;
+        $args{compiled_type_constraint}       = $check;
+    }
 
-sub name { shift->{name} }
+    $check = $args{constraint};
 
-sub check {
+    if(blessed($check)){
+        Carp::cluck("Constraint for $args{name} must be a CODE reference");
+        $check = $check->{compiled_type_constraint};
+    }
+
+    if(defined($check) && ref($check) ne 'CODE'){
+        confess("Constraint for $args{name} is not a CODE reference");
+    }
+
+    $args{package_defined_in} ||= caller;
+
+    my $self = bless \%args, $class;
+    $self->compile_type_constraint() if !$self->{hand_optimized_type_constraint};
+
+    if($self->{type_constraints}){ # Union
+        my @coercions;
+        foreach my $type(@{$self->{type_constraints}}){
+            if($type->has_coercion){
+                push @coercions, $type;
+            }
+        }
+        if(@coercions){
+            $self->{_compiled_type_coercion} = sub {
+                my($thing) = @_;
+                foreach my $type(@coercions){
+                    my $value = $type->coerce($thing);
+                    return $value if $self->check($value);
+                }
+                return $thing;
+            };
+        }
+    }
+
+    return $self;
+}
+
+sub create_child_type{
     my $self = shift;
-    $self->{_compiled_type_constraint}->(@_);
+    # XXX: FIXME
+    return ref($self)->new(
+        # a child inherits its parent's attributes
+        %{$self},
+
+        # but does not inherit 'compiled_type_constraint' and 'hand_optimized_type_constraint'
+        compiled_type_constraint       => undef,
+        hand_optimized_type_constraint => undef,
+
+        # and is given child-specific args, of course.
+        @_,
+
+        # and its parent
+        parent => $self,
+   );
 }
 
-sub validate {
-    my ($self, $value) = @_;
-    if ($self->{_compiled_type_constraint}->($value)) {
-        return undef;
+sub name    { $_[0]->{name}    }
+sub parent  { $_[0]->{parent}  }
+sub message { $_[0]->{message} }
+
+sub _compiled_type_constraint{ $_[0]->{compiled_type_constraint} }
+
+sub has_coercion{ exists $_[0]->{_compiled_type_coercion} }
+
+sub compile_type_constraint{
+    my($self) = @_;
+
+    # add parents first
+    my @checks;
+    for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
+         if($parent->{hand_optimized_type_constraint}){
+            push @checks, $parent->{hand_optimized_type_constraint};
+            last; # a hand optimized constraint must include all the parents
+        }
+        elsif($parent->{constraint}){
+            push @checks, $parent->{constraint};
+        }
     }
-    else {
-        $self->get_message($value);
+
+    # then add child
+    if($self->{constraint}){
+        push @checks, $self->{constraint};
+    }
+
+    if($self->{type_constraints}){ # Union
+        my @types = map{ $_->_compiled_type_constraint } @{ $self->{type_constraints} };
+        push @checks, sub{
+            foreach my $c(@types){
+                return 1 if $c->($_[0]);
+            }
+            return 0;
+        };
+    }
+
+    if(@checks == 0){
+        $self->{compiled_type_constraint} = $null_check;
+    }
+    elsif(@checks == 1){
+        my $c = $checks[0];
+        $self->{compiled_type_constraint} = sub{
+            my(@args) = @_;
+            local $_ = $args[0];
+            return $c->(@args);
+        };
     }
+    else{
+        $self->{compiled_type_constraint} =  sub{
+            my(@args) = @_;
+            local $_ = $args[0];
+            foreach my $c(@checks){
+                return undef if !$c->(@args);
+            }
+            return 1;
+        };
+    }
+    return;
 }
 
-sub assert_valid {
-    my ($self, $value) = @_;
+sub _add_type_coercions{
+    my $self = shift;
+
+    my $coercions = ($self->{_coercion_map} ||= []);
+    my %has       = map{ $_->[0] => undef } @{$coercions};
+
+    for(my $i = 0; $i < @_; $i++){
+        my $from   = $_[  $i];
+        my $action = $_[++$i];
 
-    my $error = $self->validate($value);
-    return 1 if ! defined $error;
+        if(exists $has{$from}){
+            confess("A coercion action already exists for '$from'");
+        }
 
-    Carp::confess($error);
+        my $type = Mouse::Util::TypeConstraints::find_or_parse_type_constraint($from)
+            or confess("Could not find the type constraint ($from) to coerce from");
+
+        push @{$coercions}, [ $type => $action ];
+    }
+
+    # compile
+    if(exists $self->{type_constraints}){ # union type
+        confess("Cannot add additional type coercions to Union types");
+    }
+    else{
+        $self->{_compiled_type_coercion} = sub {
+           my($thing) = @_;\r
+           foreach my $pair (@{$coercions}) {\r
+                #my ($constraint, $converter) = @$pair;\r
+                if ($pair->[0]->check($thing)) {\r
+                  local $_ = $thing;
+                  return $pair->[1]->($thing);
+                }\r
+           }\r
+           return $thing;\r
+        };
+    }
+    return;
 }
 
+sub check {
+    my $self = shift;
+    return $self->_compiled_type_constraint->(@_);
+}
+
+sub coerce {
+    my $self = shift;
+    if(!$self->{_compiled_type_coercion}){
+        confess("Cannot coerce without a type coercion ($self)");
+    }
 
-sub message {
-    return $_[0]->{message};
+    return $_[0] if $self->_compiled_type_constraint->(@_);
+
+    return $self->{_compiled_type_coercion}->(@_);
 }
 
 sub get_message {
@@ -65,20 +212,34 @@ sub get_message {
     }
     else {
         $value = ( defined $value ? overload::StrVal($value) : 'undef' );
-        return
-            "Validation failed for '"
-          . $self->name
-          . "' failed with value $value";
+        return "Validation failed for '$self' failed with value $value";
     }
 }
 
 sub is_a_type_of{
-    my($self, $tc_name) = @_;
+    my($self, $other) = @_;
+
+    # ->is_a_type_of('__ANON__') is always false
+    return 0 if !blessed($other) && $other eq '__ANON__';
+
+    (my $other_name = $other) =~ s/\s+//g;
 
-    return $self->name eq $tc_name
-        || $self->name =~ /\A $tc_name \[/xms; # "ArrayRef" =~ "ArrayRef[Foo]"
+    return 1 if $self->name eq $other_name;
+
+    if(exists $self->{type_constraints}){ # union
+        foreach my $type(@{$self->{type_constraints}}){
+            return 1 if $type->name eq $other_name;
+        }
+    }
+
+    for(my $parent = $self->parent; defined $parent; $parent = $parent->parent){
+        return 1 if $parent->name eq $other_name;
+    }
+
+    return 0;
 }
 
+
 1;
 __END__
 
index ae36dd8..60f6aad 100644 (file)
@@ -1,8 +1,5 @@
 package Mouse::Object;
-use strict;
-use warnings;
-
-use Mouse::Util qw(does dump);
+use Mouse::Util qw(does dump); # enables strict and warnings
 
 sub new {
     my $class = shift;
@@ -33,7 +30,18 @@ sub BUILDARGS {
 sub DESTROY {
     my $self = shift;
 
-    $self->DEMOLISHALL();
+    local $?;
+
+    my $e = do{
+        local $@;
+        eval{
+            $self->DEMOLISHALL();
+        };
+        $@;
+    };
+
+    no warnings 'misc';
+    die $e if $e; # rethrow
 }
 
 sub BUILDALL {
index bc32665..0ae4917 100644 (file)
@@ -1,17 +1,28 @@
 package Mouse::Role;
-use strict;
-use warnings;
+use Mouse::Util qw(not_supported); # enables strict and warnings
 
-use Exporter;
+use Carp qw(confess);
+use Scalar::Util qw(blessed);
 
-use Carp 'confess';
-use Scalar::Util 'blessed';
-
-use Mouse::Util qw(load_class get_code_package not_supported);
 use Mouse ();
+use Mouse::Exporter;
+
+Mouse::Exporter->setup_import_methods(
+    as_is => [qw(
+        extends with
+        has
+        before after around
+        override super
+        augment  inner
+
+        requires excludes
+    ),
+        \&Scalar::Util::blessed,
+        \&Carp::confess,
+    ],
+);
 
-our @ISA = qw(Exporter);
-
+# XXX: for backward compatibility
 our @EXPORT = qw(
     extends with
     has
@@ -24,10 +35,6 @@ our @EXPORT = qw(
     blessed confess
 );
 
-our %is_removable = map{ $_ => undef } @EXPORT;
-delete $is_removable{confess};
-delete $is_removable{blessed};
-
 sub before {
     my $meta = Mouse::Meta::Role->initialize(scalar caller);
 
@@ -57,29 +64,13 @@ sub around {
 
 
 sub super {
-    return unless $Mouse::SUPER_BODY; 
+    return if !defined $Mouse::SUPER_BODY;
     $Mouse::SUPER_BODY->(@Mouse::SUPER_ARGS);
 }
 
 sub override {
-    my $classname = caller;
-    my $meta = Mouse::Meta::Role->initialize($classname);
-
-    my $name = shift;
-    my $code = shift;
-    my $fullname = "${classname}::${name}";
-
-    defined &$fullname
-        && $meta->throw_error("Cannot add an override of method '$fullname' "
-                            . "because there is a local version of '$fullname'");
-
-    $meta->add_override_method_modifier($name => sub {
-        local $Mouse::SUPER_PACKAGE = shift;
-        local $Mouse::SUPER_BODY = shift;
-        local @Mouse::SUPER_ARGS = @_;
-
-        $code->(@_);
-    });
+    # my($name, $code) = @_;
+    Mouse::Meta::Role->initialize(scalar caller)->add_override_method_modifier(@_);
 }
 
 # We keep the same errors messages as Moose::Role emits, here.
@@ -117,45 +108,26 @@ sub excludes {
     not_supported;
 }
 
-sub import {
-    my $class = shift;
+sub init_meta{
+    shift;
+    my %args = @_;
 
-    strict->import;
-    warnings->import;
+    my $class = $args{for_class}
+        or Carp::confess("Cannot call init_meta without specifying a for_class");
 
-    my $caller = caller;
+    my $metaclass  = $args{metaclass}  || 'Mouse::Meta::Role';
 
-    # we should never export to main
-    if ($caller eq 'main') {
-        warn qq{$class does not export its sugar to the 'main' package.\n};
-        return;
-    }
+    my $meta = $metaclass->initialize($class);
 
-    Mouse::Meta::Role->initialize($caller)->add_method(meta => sub {
-        return Mouse::Meta::Role->initialize(ref($_[0]) || $_[0]);
+    $meta->add_method(meta => sub{
+        $metaclass->initialize(ref($_[0]) || $_[0]);
     });
 
-    Mouse::Role->export_to_level(1, @_);
-}
-
-sub unimport {
-    my $caller = caller;
-
-    my $stash = do{
-        no strict 'refs';
-        \%{$caller . '::'}
-    };
+    # make a role type for each Mouse role
+    Mouse::Util::TypeConstraints::role_type($class)
+        unless Mouse::Util::TypeConstraints::find_type_constraint($class);
 
-    for my $keyword (@EXPORT) {
-        my $code;
-        if(exists $is_removable{$keyword}
-            && ($code = $caller->can($keyword))
-            && get_code_package($code) eq __PACKAGE__){
-
-            delete $stash->{$keyword};
-        }
-    }
-    return;
+    return $meta;
 }
 
 1;
index 53137b0..d7a99c2 100644 (file)
@@ -2,7 +2,7 @@ package Mouse::Spec;
 use strict;
 use warnings;
 
-our $VERSION = '0.37';
+our $VERSION = '0.37_06';
 
 our $MouseVersion = $VERSION;
 our $MooseVersion = '0.90';
@@ -12,3 +12,88 @@ sub MooseVersion{ $MooseVersion }
 
 1;
 __END__
+
+=head1 NAME
+
+Mouse::Spec - To what extent Mouse is compatible with Moose
+
+=head1 DESCRIPTION
+
+=head2 Notes about Moose::Cookbook
+
+Many recipes in L<Moose::Cookbook> fit L<Mouse>, including:
+
+=over 4
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe1> - The (always classic) B<Point> example
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe2> - A simple B<BankAccount> example\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe3> - A lazy B<BinaryTree> example
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe4> - Subtypes, and modeling a simple B<Company> class hierarchy
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe5> - More subtypes, coercion in a B<Request> class\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe6> - The augment/inner example\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe7> - Making Moose fast with immutable\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe8> - Builder methods and lazy_build\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe9> - Operator overloading, subtypes, and coercion\r
+
+=item *
+
+L<Moose::Cookbook::Basics::Recipe10> - Using BUILDARGS and BUILD to hook into object construction\r
+
+=item *
+
+L<Moose::Cookbook::Roles::Recipe1> - The Moose::Role example\r
+
+=item *
+
+L<Moose::Cookbook::Roles::Recipe2> - Advanced Role Composition - method exclusion and aliasing
+
+=item *
+
+L<Moose::Cookbook::Roles::Recipe3> - Applying a role to an object instance\r
+
+=item *
+
+L<Moose::Cookbook::Meta::Recipe2> - A meta-attribute, attributes with labels\r
+
+=item *
+
+L<Moose::Cookbook::Meta::Recipe3> - Labels implemented via attribute traits\r
+
+=item *
+
+L<Moose::Cookbook::Extending::Recipe3> - Providing an alternate base object class\r
+
+=back
+
+=head1 SEE ALSO
+
+L<Mouse>
+
+=cut
+
index 835db2a..635fdd7 100644 (file)
@@ -1,50 +1,63 @@
 package Mouse::Util;
-use strict;
-use warnings;
-
-use Exporter;
+use Mouse::Exporter; # enables strict and warnings
 
 use Carp qw(confess);
+use Scalar::Util qw(blessed);
 use B ();
 
 use constant _MOUSE_VERBOSE => !!$ENV{MOUSE_VERBOSE};
 
-our @ISA       = qw(Exporter);
-our @EXPORT_OK = qw(
-    find_meta
-    does_role
-    resolve_metaclass_alias
-    apply_all_roles
-    english_list
+Mouse::Exporter->setup_import_methods(
+    as_is => [qw(
+        find_meta
+        does_role
+        resolve_metaclass_alias
+        apply_all_roles
+        english_list
 
-    load_class
-    is_class_loaded
+        load_class
+        is_class_loaded
 
-    get_linear_isa
-    get_code_info
+        get_linear_isa
+        get_code_info
 
-    get_code_package
+        get_code_package
 
-    not_supported
+        not_supported
 
-    does meta dump
-    _MOUSE_VERBOSE
-);
-our %EXPORT_TAGS = (
-    all  => \@EXPORT_OK,
-    meta => [qw(does meta dump _MOUSE_VERBOSE)],
+        does meta dump
+        _MOUSE_VERBOSE
+    )],
+    groups => {
+        default => [], # export no functions by default
+
+        # The ':meta' group is 'use metaclass' for Mouse
+        meta    => [qw(does meta dump _MOUSE_VERBOSE)],
+    },
+    _export_to_main => 1,
 );
 
+# aliases as public APIs
+# it must be 'require', not 'use', because Mouse::Meta::Module depends on Mouse::Util
+require Mouse::Meta::Module; # for the entities of metaclass cache utilities
+
+BEGIN {
+    *class_of                    = \&Mouse::Meta::Module::class_of;
+    *get_metaclass_by_name       = \&Mouse::Meta::Module::get_metaclass_by_name;
+    *get_all_metaclass_instances = \&Mouse::Meta::Module::get_all_metaclass_instances;
+    *get_all_metaclass_names     = \&Mouse::Meta::Module::get_all_metaclass_names;
+}
+
 # Moose::Util compatible utilities
 
 sub find_meta{
-    return Mouse::Meta::Module::class_of( $_[0] );
+    return class_of( $_[0] );
 }
 
 sub does_role{
     my ($class_or_obj, $role_name) = @_;
 
-    my $meta = Mouse::Meta::Module::class_of($class_or_obj);
+    my $meta = class_of($class_or_obj);
 
     (defined $role_name)
         || ($meta || 'Mouse::Meta::Class')->throw_error("You must supply a role name to does()");
@@ -52,8 +65,6 @@ sub does_role{
     return defined($meta) && $meta->does_role($role_name);
 }
 
-
-
 BEGIN {
     my $impl;
     if ($] >= 5.009_005) {
@@ -151,6 +162,9 @@ BEGIN {
     }
 }
 
+# Utilities from Class::MOP
+
+
 # taken from Class/MOP.pm
 sub is_valid_class_name {
     my $class = shift;
@@ -191,6 +205,7 @@ sub load_first_existing_class {
 }
 
 # taken from Class/MOP.pm
+my %is_class_loaded_cache;
 sub _try_load_one_class {
     my $class = shift;
 
@@ -199,7 +214,7 @@ sub _try_load_one_class {
         confess "Invalid class name ($display)";
     }
 
-    return if is_class_loaded($class);
+    return undef if $is_class_loaded_cache{$class} ||= is_class_loaded($class);
 
     my $file = $class . '.pm';
     $file =~ s{::}{/}g;
@@ -220,14 +235,12 @@ sub load_class {
     return 1;
 }
 
-my %is_class_loaded_cache;
+
 sub is_class_loaded {
     my $class = shift;
 
     return 0 if ref($class) || !defined($class) || !length($class);
 
-    return 1 if $is_class_loaded_cache{$class};
-
     # walk the symbol table tree to avoid autovififying
     # \*{${main::}{"Foo::"}} == \*main::Foo::
 
@@ -239,15 +252,15 @@ sub is_class_loaded {
     }
 
     # check for $VERSION or @ISA
-    return ++$is_class_loaded_cache{$class} if exists $pack->{VERSION}
+    return 1 if exists $pack->{VERSION}
              && defined *{$pack->{VERSION}}{SCALAR} && defined ${ $pack->{VERSION} };
-    return ++$is_class_loaded_cache{$class} if exists $pack->{ISA}
+    return 1 if exists $pack->{ISA}
              && defined *{$pack->{ISA}}{ARRAY} && @{ $pack->{ISA} } != 0;
 
     # check for any method
     foreach my $name( keys %{$pack} ) {
         my $entry = \$pack->{$name};
-        return ++$is_class_loaded_cache{$class} if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
+        return 1 if ref($entry) ne 'GLOB' || defined *{$entry}{CODE};
     }
 
     # fail
@@ -256,7 +269,7 @@ sub is_class_loaded {
 
 
 sub apply_all_roles {
-    my $meta = Mouse::Meta::Class->initialize(shift);
+    my $applicant = blessed($_[0]) ? shift : Mouse::Meta::Class->initialize(shift);
 
     my @roles;
 
@@ -264,22 +277,24 @@ sub apply_all_roles {
     my $max = scalar(@_);
     for (my $i = 0; $i < $max ; $i++) {
         if ($i + 1 < $max && ref($_[$i + 1])) {
-            push @roles, [ $_[$i++] => $_[$i] ];
+            push @roles, [ $_[$i] => $_[++$i] ];
         } else {
-            push @roles, [ $_[$i]   => undef ];
+            push @roles, [ $_[$i] => undef ];
         }
         my $role_name = $roles[-1][0];
         load_class($role_name);
-        ( $role_name->can('meta') && $role_name->meta->isa('Mouse::Meta::Role') )
-            || $meta->throw_error("You can only consume roles, $role_name(".$role_name->meta.") is not a Mouse role");
+
+        my $metarole = get_metaclass_by_name($role_name);
+        ( $metarole && $metarole->isa('Mouse::Meta::Role') )
+            || $applicant->meta->throw_error("You can only consume roles, $role_name(".$role_name->meta.") is not a Mouse role");
     }
 
     if ( scalar @roles == 1 ) {
-        my ( $role, $params ) = @{ $roles[0] };
-        $role->meta->apply( $meta, ( defined $params ? %$params : () ) );
+        my ( $role_name, $params ) = @{ $roles[0] };
+        get_metaclass_by_name($role_name)->apply( $applicant, defined $params ? $params : () );
     }
     else {
-        Mouse::Meta::Role->combine_apply($meta, @roles);
+        Mouse::Meta::Role->combine(@roles)->apply($applicant);
     }
     return;
 }
@@ -309,11 +324,13 @@ sub not_supported{
     Carp::confess("Mouse does not currently support $feature");
 }
 
-sub meta{
-    return Mouse::Meta::Class->initialize($_[0]);
+# general meta() method
+sub meta :method{
+    return Mouse::Meta::Class->initialize(ref($_[0]) || $_[0]);
 }
 
-sub dump { 
+# general dump() method
+sub dump :method {
     my($self, $maxdepth) = @_;
 
     require 'Data/Dumper.pm'; # we don't want to create its namespace
@@ -323,6 +340,7 @@ sub dump {
     return $dd->Dump();
 }
 
+# general does() method
 sub does :method;
 *does = \&does_role; # alias
 
@@ -358,10 +376,18 @@ locally-defined method.
 
 =head3 C<< load_class(ClassName) >>
 
-This will load a given C<ClassName> (or die if it's not loadable).
+This will load a given C<ClassName> (or die if it is not loadable).
 This function can be used in place of tricks like
 C<eval "use $module"> or using C<require>.
 
+=head3 C<< Mouse::Util::class_of(ClassName or Object) >>
+
+=head3 C<< Mouse::Util::get_metaclass_by_name(ClassName) >>
+
+=head3 C<< Mouse::Util::get_all_metaclass_instances() >>
+
+=head3 C<< Mouse::Util::get_all_metaclass_names() >>
+
 =head2 MRO::Compat
 
 =head3 C<get_linear_isa>
@@ -378,7 +404,7 @@ C<eval "use $module"> or using C<require>.
 
 L<Moose::Util>
 
-L<Scalar::Util>
+L<Class::MOP>
 
 L<Sub::Identify>
 
index 54246b8..14ba4bd 100644 (file)
@@ -1,44 +1,38 @@
 package Mouse::Util::TypeConstraints;
-use strict;
-use warnings;
+use Mouse::Util qw(does_role not_supported); # enables strict and warnings
 
-use Exporter;
-
-use Carp ();
+use Carp qw(confess);
 use Scalar::Util qw/blessed looks_like_number openhandle/;
 
-use Mouse::Util qw(does_role not_supported);
-use Mouse::Meta::Module; # class_of
 use Mouse::Meta::TypeConstraint;
+use Mouse::Exporter;
+
+Mouse::Exporter->setup_import_methods(
+    as_is => [qw(
+        as where message optimize_as
+        from via
+        type subtype coerce class_type role_type enum
+        find_type_constraint
+    )],
 
-our @ISA    = qw(Exporter);
-our @EXPORT = qw(
-    as where message from via type subtype coerce class_type role_type enum
-    find_type_constraint
+    _export_to_main => 1,
 );
 
 my %TYPE;
-my %TYPE_SOURCE;
-my %COERCE;
-my %COERCE_KEYS;
 
-sub as ($) {
-    return(as => $_[0]);
-}
-sub where (&) {
-    return(where => $_[0])
-}
-sub message (&) {
-    return(message => $_[0])
-}
+sub as          ($) { (as => $_[0]) }
+sub where       (&) { (where => $_[0]) }
+sub message     (&) { (message => $_[0]) }
+sub optimize_as (&) { (optimize_as => $_[0]) }
 
 sub from    { @_ }
 sub via (&) { $_[0] }
 
 BEGIN {
     my %builtins = (
-        Any        => sub { 1 },
-        Item       => sub { 1 },
+        Any        => undef, # null check
+        Item       => undef, # null check
+        Maybe      => undef, # null check
 
         Bool       => sub { $_[0] ? $_[0] eq '1' : 1 },
         Undef      => sub { !defined($_[0]) },
@@ -70,13 +64,15 @@ BEGIN {
 
     while (my ($name, $code) = each %builtins) {
         $TYPE{$name} = Mouse::Meta::TypeConstraint->new(
-            name                      => $name,
-            _compiled_type_constraint => $code,
+            name      => $name,
+            optimized => $code,
         );
-        $TYPE_SOURCE{$name} = __PACKAGE__;
     }
 
-    sub optimized_constraints { \%TYPE }
+    sub optimized_constraints { # DEPRECATED
+        Carp::cluck('optimized_constraints() has been deprecated');
+        return \%TYPE;
+    }
 
     my @builtins = keys %TYPE;
     sub list_all_builtin_type_constraints { @builtins }
@@ -84,132 +80,79 @@ BEGIN {
     sub list_all_type_constraints         { keys %TYPE }
 }
 
-sub type {
+sub _create_type{
+    my $mode = shift;
+
     my $name;
-    my %conf;
+    my %args;
 
-    if(@_ == 1 && ref $_[0]){ # type { where => ... }
-        %conf = %{$_[0]};
+    if(@_ == 1 && ref $_[0]){   # @_ : { name => $name, where => ... }
+        %args = %{$_[0]};
     }
-    elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
+    elsif(@_ == 2 && ref $_[1]){ # @_ : $name => { where => ... }
         $name = $_[0];
-        %conf = %{$_[1]};
+        %args = %{$_[1]};
     }
-    elsif(@_ % 2){ # odd number of arguments
-        $name = shift;
-        %conf = @_;
+    elsif(@_ % 2){               # @_ : $name => ( where => ... )
+        ($name, %args) = @_;
     }
-    else{
-        %conf = @_;
+    else{                        # @_ : (name => $name, where => ...)
+        %args = @_;
     }
 
-    $name = '__ANON__' if !defined $name;
-
-    my $pkg = caller;
-
-    if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
-        Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
+    if(!defined $name){
+        if(!defined($name = $args{name})){
+            $name = '__ANON__';
+        }
     }
 
-    my $constraint = $conf{where} || do {
-        my $as = delete $conf{as} || 'Any';
-        ($TYPE{$as} ||= _build_type_constraint($as))->{_compiled_type_constraint};
-    };
-
-    my $tc = Mouse::Meta::TypeConstraint->new(
-        name                      => $name,
-        _compiled_type_constraint => sub {
-            local $_ = $_[0];
-            return &{$constraint};
-        },
-    );
+    $args{name} = $name;
+    my $parent;
+    if($mode eq 'subtype'){
+        $parent = delete $args{as};
+        if(!$parent){
+            $parent = delete $args{name};
+            $name   = '__ANON__';
+        }
+    }
 
-    $TYPE_SOURCE{$name} = $pkg;
-    $TYPE{$name}        = $tc;
+    my $package_defined_in = $args{package_defined_in} ||= caller(1);
 
-    return $tc;
-}
+    my $existing = $TYPE{$name};
+    if($existing && $existing->{package_defined_in} ne $package_defined_in){
+        confess("The type constraint '$name' has already been created in "
+              . "$existing->{package_defined_in} and cannot be created again in $package_defined_in");
+    }
 
-sub subtype {
-    my $name;
-    my %conf;
+    $args{constraint} = delete $args{where}        if exists $args{where};
+    $args{optimized}  = delete $args{optimized_as} if exists $args{optimized_as};
 
-    if(@_ == 1 && ref $_[0]){ # type { where => ... }
-        %conf = %{$_[0]};
-    }
-    elsif(@_ == 2 && ref $_[1]){ # type $name => { where => ... }*
-        $name = $_[0];
-        %conf = %{$_[1]};
-    }
-    elsif(@_ % 2){ # odd number of arguments
-        $name = shift;
-        %conf = @_;
+    my $constraint;
+    if($mode eq 'subtype'){
+        $constraint = find_or_create_isa_type_constraint($parent)->create_child_type(%args);
     }
     else{
-        %conf = @_;
-    }
-
-    $name = '__ANON__' if !defined $name;
-
-    my $pkg = caller;
-
-    if ($TYPE{$name} && $TYPE_SOURCE{$name} ne $pkg) {
-        Carp::croak "The type constraint '$name' has already been created in $TYPE_SOURCE{$name} and cannot be created again in $pkg";
+        $constraint = Mouse::Meta::TypeConstraint->new(%args);
     }
 
-    my $constraint    = delete $conf{where};
-    my $as_constraint = find_or_create_isa_type_constraint(delete $conf{as} || 'Any')
-        ->{_compiled_type_constraint};
-
-    my $tc = Mouse::Meta::TypeConstraint->new(
-        name => $name,
-        _compiled_type_constraint => (
-            $constraint ? 
-            sub {
-                local $_ = $_[0];
-                $as_constraint->($_[0]) && $constraint->($_[0])
-            } :
-            sub {
-                local $_ = $_[0];
-                $as_constraint->($_[0]);
-            }
-        ),
-        %conf,
-    );
+    return $TYPE{$name} = $constraint;
+}
 
-    $TYPE_SOURCE{$name} = $pkg;
-    $TYPE{$name}        = $tc;
+sub type {
+    return _create_type('type', @_);
+}
 
-    return $tc;
+sub subtype {
+    return _create_type('subtype', @_);
 }
 
 sub coerce {
-    my $name = shift;
-
-    Carp::croak "Cannot find type '$name', perhaps you forgot to load it."
-        unless $TYPE{$name};
+    my $type_name = shift;
 
-    unless ($COERCE{$name}) {
-        $COERCE{$name}      = {};
-        $COERCE_KEYS{$name} = [];
-    }
-
-    while (my($type, $code) = splice @_, 0, 2) {
-        Carp::croak "A coercion action already exists for '$type'"
-            if $COERCE{$name}->{$type};
-
-        if (! $TYPE{$type}) {
-            # looks parameterized
-            if ($type =~ /^[^\[]+\[.+\]$/) {
-                $TYPE{$type} = _build_type_constraint($type);
-            } else {
-                Carp::croak "Could not find the type constraint ($type) to coerce from"
-            }
-        }
+    my $type = find_type_constraint($type_name)
+        or confess("Cannot find type '$type_name', perhaps you forgot to load it.");
 
-        push @{ $COERCE_KEYS{$name} }, $type;
-        $COERCE{$name}->{$type} = $code;
-    }
+    $type->_add_type_coercions(@_);
     return;
 }
 
@@ -218,194 +161,249 @@ sub class_type {
     if ($conf && $conf->{class}) {
         # No, you're using this wrong
         warn "class_type() should be class_type(ClassName). Perhaps you're looking for subtype $name => as '$conf->{class}'?";
-        subtype $name => (as => $conf->{class});
+        _create_type 'type', $name => (
+            as   => $conf->{class},
+
+            type => 'Class',
+       );
     }
     else {
-        subtype $name => (
-            where => sub { blessed($_) && $_->isa($name) },
+        _create_type 'type', $name => (
+            optimized_as => sub { blessed($_[0]) && $_[0]->isa($name) },
+
+            type => 'Class',
         );
     }
 }
 
 sub role_type {
     my($name, $conf) = @_;
-    my $role = $conf->{role};
-    subtype $name => (
-        where => sub { does_role($_, $role) },
+    my $role = ($conf && $conf->{role}) ? $conf->{role} : $name;
+    _create_type 'type', $name => (
+        optimized_as => sub { blessed($_[0]) && does_role($_[0], $role) },
+
+        type => 'Role',
     );
 }
 
-# this is an original method for Mouse
-sub typecast_constraints {
-    my($class, $pkg, $types, $value) = @_;
+sub typecast_constraints { # DEPRECATED
+    my($class, $pkg, $type, $value) = @_;
     Carp::croak("wrong arguments count") unless @_ == 4;
 
-    local $_;
-    for my $type ( split /\|/, $types ) {
-        next unless $COERCE{$type};
-        for my $coerce_type (@{ $COERCE_KEYS{$type}}) {
-            $_ = $value;
-            next unless $TYPE{$coerce_type}->check($value);
-            $_ = $value;
-            $_ = $COERCE{$type}->{$coerce_type}->($value);
-            return $_ if $types->check($_);
-        }
-    }
-    return $value;
+    Carp::cluck("typecast_constraints() has been deprecated, which was an internal utility anyway");
+
+    return $type->coerce($value);
 }
 
-my $serial_enum = 0;
 sub enum {
+    my($name, %valid);
+
     # enum ['small', 'medium', 'large']
     if (ref($_[0]) eq 'ARRAY') {
-        my @elements = @{ shift @_ };
+        %valid = map{ $_ => undef } @{ $_[0] };
+        $name  = sprintf '(%s)', join '|', sort @{$_[0]};
+    }
+    # enum size => 'small', 'medium', 'large'
+    else{
+        $name  = shift;
+        %valid = map{ $_ => undef } @_;
+    }
+    return _create_type 'type', $name => (
+        optimized_as  => sub{ defined($_[0]) && !ref($_[0]) && exists $valid{$_[0]} },
+
+        type => 'Enum',
+    );
+}
 
-        my $name = 'Mouse::Util::TypeConstaints::Enum::Serial::'
-                 . ++$serial_enum;
-        enum($name, @elements);
-        return $name;
+sub _find_or_create_regular_type{
+    my($spec)  = @_;
+
+    return $TYPE{$spec} if exists $TYPE{$spec};
+
+    my $meta  = Mouse::Util::get_metaclass_by_name($spec);
+
+    if(!$meta){
+        return;
     }
 
-    # enum size => 'small', 'medium', 'large'
-    my $name = shift;
-    my %is_valid = map { $_ => 1 } @_;
+    my $check;
+    my $type;
+    if($meta->isa('Mouse::Meta::Role')){
+        $check = sub{
+            return blessed($_[0]) && $_[0]->does($spec);
+        };
+        $type = 'Role';
+    }
+    else{
+        $check = sub{
+            return blessed($_[0]) && $_[0]->isa($spec);
+        };
+        $type = 'Class';
+    }
 
-    subtype(
-        $name => where => sub { $is_valid{$_} }
+    return $TYPE{$spec} = Mouse::Meta::TypeConstraint->new(
+        name      => $spec,
+        optimized => $check,
+
+        type      => $type,
     );
 }
 
-sub _build_type_constraint {
-    my($spec) = @_;
+$TYPE{ArrayRef}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->_compiled_type_constraint;
 
-    my $code;
-    $spec =~ s/\s+//g;
+    return sub{
+        foreach my $value (@{$_}) {
+            return undef unless $check->($value);
+        }
+        return 1;
+    }
+};
+$TYPE{HashRef}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->_compiled_type_constraint;
+
+    return sub{
+        foreach my $value(values %{$_}){
+            return undef unless $check->($value);
+        }
+        return 1;
+    };
+};
+
+# 'Maybe' type accepts 'Any', so it requires parameters
+$TYPE{Maybe}{constraint_generator} = sub {
+    my($type_parameter) = @_;
+    my $check = $type_parameter->_compiled_type_constraint;
+
+    return sub{
+        return !defined($_) || $check->($_);
+    };
+};
 
-    if ($spec =~ /\A (\w+) \[ (.+) \] \z/xms) {
-        # parameterized
-        my $constraint = $1;
-        my $param      = $2;
-        my $parent;
+sub _find_or_create_parameterized_type{
+    my($base, $param) = @_;
 
-        if ($constraint eq 'Maybe') {
-            $parent = _build_type_constraint('Undef');
+    my $name = sprintf '%s[%s]', $base->name, $param->name;
+
+    $TYPE{$name} ||= do{
+        my $generator = $base->{constraint_generator};
+
+        if(!$generator){
+            confess("The $name constraint cannot be used, because $param doesn't subtype from a parameterizable type");
         }
-        else {
-            $parent = _build_type_constraint($constraint);
+
+        Mouse::Meta::TypeConstraint->new(
+            name               => $name,
+            parent             => $base,
+            constraint         => $generator->($param),
+
+            type               => 'Parameterized',
+        );
+    }
+}
+sub _find_or_create_union_type{
+    my @types = sort{ $a cmp $b } map{ $_->{type_constraints} ? @{$_->{type_constraints}} : $_ } @_;
+
+    my $name = join '|', @types;
+
+    $TYPE{$name} ||= do{
+        return Mouse::Meta::TypeConstraint->new(
+            name              => $name,
+            type_constraints  => \@types,
+
+            type              => 'Union',
+        );
+    };
+}
+
+# The type parser
+sub _parse_type{
+    my($spec, $start) = @_;
+
+    my @list;
+    my $subtype;
+
+    my $len = length $spec;
+    my $i;
+
+    for($i = $start; $i < $len; $i++){
+        my $char = substr($spec, $i, 1);
+
+        if($char eq '['){
+            my $base = _find_or_create_regular_type( substr($spec, $start, $i - $start) )
+                or return;
+
+            ($i, $subtype) = _parse_type($spec, $i+1)
+                or return;
+            $start = $i+1; # reset
+
+            push @list, _find_or_create_parameterized_type($base => $subtype);
         }
-        my $child = _build_type_constraint($param);
-        if ($constraint eq 'ArrayRef') {
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    if (\$parent->check(\$_[0])) {\n" .
-                "        foreach my \$e (\@{\$_[0]}) {\n" .
-                "            return () unless \$child->check(\$e);\n" .
-                "        }\n" .
-                "        return 1;\n" .
-                "    }\n" .
-                "    return ();\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess("Failed to generate inline type constraint: $@");
-        } elsif ($constraint eq 'HashRef') {
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    if (\$parent->check(\$_[0])) {\n" .
-                "        foreach my \$e (values \%{\$_[0]}) {\n" .
-                "            return () unless \$child->check(\$e);\n" .
-                "        }\n" .
-                "        return 1;\n" .
-                "    }\n" .
-                "    return ();\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess($@);
-        } elsif ($constraint eq 'Maybe') {
-            my $code_str =
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    return \$child->check(\$_[0]) || \$parent->check(\$_[0]);\n" .
-                "};\n"
-            ;
-            $code = eval $code_str or Carp::confess($@);
-        } else {
-            Carp::confess("Support for parameterized types other than Maybe, ArrayRef or HashRef is not implemented yet");
+        elsif($char eq ']'){
+            $len = $i+1;
+            last;
         }
-        $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
-    } else {
-        $code = $TYPE{ $spec };
-        if (! $code) {
-            # is $spec a known role?  If so, constrain with 'does' instead of 'isa'
-            require Mouse::Meta::Role;
-            my $check = Mouse::Meta::Role->_metaclass_cache($spec)? 
-                'does' : 'isa';
-            my $code_str = 
-                "#line " . __LINE__ . ' "' . __FILE__ . "\"\n" .
-                "sub {\n" .
-                "    Scalar::Util::blessed(\$_[0]) && \$_[0]->$check('$spec');\n" .
-                "}"
-            ;
-            $code = eval $code_str  or Carp::confess($@);
-            $TYPE{$spec} = Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
+        elsif($char eq '|'){
+            my $type = _find_or_create_regular_type( substr($spec, $start, $i - $start) );
+
+            if(!defined $type){
+                # XXX: Mouse creates a new class type, but Moose does not.
+                $type = class_type( substr($spec, $start, $i - $start) );
+            }
+
+            push @list, $type;
+
+            ($i, $subtype) = _parse_type($spec, $i+1)
+                or return;
+
+            $start = $i+1; # reset
+
+            push @list, $subtype;
         }
     }
-    return Mouse::Meta::TypeConstraint->new( _compiled_type_constraint => $code, name => $spec );
-}
+    if($i - $start){
+        push @list, _find_or_create_regular_type(substr $spec, $start, $i - $start);
+    }
 
-sub find_type_constraint {
-    my($type) = @_;
-    if(blessed($type) && $type->isa('Mouse::Meta::TypeConstraint')){
-        return $type;
+    if(@list == 0){
+       return;
+    }
+    elsif(@list == 1){
+        return ($len, $list[0]);
     }
     else{
-        return $TYPE{$type};
+        return ($len, _find_or_create_union_type(@list));
     }
 }
 
-sub find_or_create_does_type_constraint{
-    not_supported;
-}
 
-sub find_or_create_isa_type_constraint {
-    my $type_constraint = shift;
+sub find_type_constraint {
+    my($spec) = @_;
+    return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
 
-    Carp::confess("Got isa => type_constraints, but Mouse does not yet support parameterized types for containers other than ArrayRef and HashRef and Maybe (rt.cpan.org #39795)")
-        if $type_constraint =~ /\A ( [^\[]+ ) \[\.+\] \z/xms &&
-           $1 ne 'ArrayRef' &&
-           $1 ne 'HashRef'  &&
-           $1 ne 'Maybe'
-    ;
+    $spec =~ s/\s+//g;
+    return $TYPE{$spec};
+}
 
+sub find_or_parse_type_constraint {
+    my($spec) = @_;
+    return $spec if blessed($spec) && $spec->isa('Mouse::Meta::TypeConstraint');
 
-    $type_constraint =~ s/\s+//g;
+    $spec =~ s/\s+//g;
+    return $TYPE{$spec} || do{
+        my($pos, $type) = _parse_type($spec, 0);
+        $type;
+    };
+}
 
-    my $tc =  find_type_constraint($type_constraint);
-    if (!$tc) {
-        my @type_constraints = split /\|/, $type_constraint;
-        if (@type_constraints == 1) {
-            $tc = $TYPE{$type_constraints[0]} ||
-                _build_type_constraint($type_constraints[0]);
-        }
-        else {
-            my @code_list = map {
-                $TYPE{$_} || _build_type_constraint($_)
-            } @type_constraints;
-
-            $tc = Mouse::Meta::TypeConstraint->new(
-                name => $type_constraint,
-
-                _compiled_type_constraint => sub {
-                    foreach my $code (@code_list) {
-                        return 1 if $code->check($_[0]);
-                    }
-                    return 0;
-                },
-            );
-        }
-    }
-    return $tc;
+sub find_or_create_does_type_constraint{
+    return find_or_parse_type_constraint(@_) || role_type(@_);
+}
+
+sub find_or_create_isa_type_constraint {
+    return find_or_parse_type_constraint(@_) || class_type(@_);
 }
 
 1;
@@ -503,7 +501,6 @@ that hierarchy represented visually.
               GlobRef
                 FileHandle
               Object
-                Role
 
 B<NOTE:> Any type followed by a type parameter C<[`a]> can be
 parameterized, this means you can say:
@@ -576,9 +573,13 @@ related C<eq_deeply> function.
 
 =head1 METHODS
 
-=head2 optimized_constraints -> HashRef[CODE]
+=head2 C<< list_all_builtin_type_constraints -> (Names) >>
+
+Returns the names of builtin type constraints.
+
+=head2 C<< list_all_type_constraints -> (Names) >>
 
-Returns the simple type constraints that Mouse understands.
+Returns the names of all the type constraints.
 
 =head1 FUNCTIONS
 
diff --git a/t/000-recipes/001_point.t b/t/000-recipes/001_point.t
deleted file mode 100644 (file)
index 0d1b9a9..0000000
+++ /dev/null
@@ -1,200 +0,0 @@
-#!/usr/bin/perl
-
-use strict;
-use warnings;
-
-use Test::More tests => 59;
-
-use Mouse::Util;
-use Test::Exception;
-
-{
-       package Point;  
-       use Mouse;
-               
-       has 'x' => (isa => 'Int', is => 'ro');
-       has 'y' => (isa => 'Int', is => 'rw');
-       
-       sub clear {
-           my $self = shift;
-           $self->{x} = 0;
-           $self->y(0);    
-       }
-       
-    __PACKAGE__->meta->make_immutable();
-}{     
-       package Point3D;
-       use Mouse;
-       
-       extends 'Point';
-       
-       has 'z' => (isa => 'Int', is => 'bare');
-       
-       after 'clear' => sub {
-           my $self = shift;
-           $self->{z} = 0;
-       };
-       
-    __PACKAGE__->meta->make_immutable();
-}
-
-my $point = Point->new(x => 1, y => 2);        
-isa_ok($point, 'Point');
-isa_ok($point, 'Mouse::Object');
-
-is($point->x, 1, '... got the right value for x');
-is($point->y, 2, '... got the right value for y');
-
-$point->y(10);
-is($point->y, 10, '... got the right (changed) value for y');
-
-dies_ok {
-       $point->y('Foo');
-} '... cannot assign a non-Int to y';
-
-dies_ok {
-    $point->x(1000);
-} '... cannot assign to a read-only method';
-is($point->x, 1, '... got the right (un-changed) value for x');
-
-$point->clear();
-
-is($point->x, 0, '... got the right (cleared) value for x');
-is($point->y, 0, '... got the right (cleared) value for y');
-
-# check the type constraints on the constructor
-
-lives_ok {
-       Point->new(x => 0, y => 0);
-} '... can assign a 0 to x and y';
-
-dies_ok {
-       Point->new(x => 10, y => 'Foo');
-} '... cannot assign a non-Int to y';
-
-dies_ok {
-       Point->new(x => 'Foo', y => 10);
-} '... cannot assign a non-Int to x';
-
-# Point3D
-
-my $point3d = Point3D->new({ x => 10, y => 15, z => 3 });
-isa_ok($point3d, 'Point3D');
-isa_ok($point3d, 'Point');
-isa_ok($point3d, 'Mouse::Object');
-
-is($point3d->x, 10, '... got the right value for x');
-is($point3d->y, 15, '... got the right value for y');
-is($point3d->{'z'}, 3, '... got the right value for z');
-
-dies_ok {
-       $point3d->z;
-} '... there is no method for z';
-
-$point3d->clear();
-
-is($point3d->x, 0, '... got the right (cleared) value for x');
-is($point3d->y, 0, '... got the right (cleared) value for y');
-is($point3d->{'z'}, 0, '... got the right (cleared) value for z');
-
-dies_ok {
-       Point3D->new(x => 10, y => 'Foo', z => 3);
-} '... cannot assign a non-Int to y';
-
-dies_ok {
-       Point3D->new(x => 'Foo', y => 10, z => 3);
-} '... cannot assign a non-Int to x';
-
-dies_ok {
-       Point3D->new(x => 0, y => 10, z => 'Bar');
-} '... cannot assign a non-Int to z';
-
-# test some class introspection
-
-can_ok('Point', 'meta');
-isa_ok(Point->meta, 'Mouse::Meta::Class');
-
-can_ok('Point3D', 'meta');
-isa_ok(Point3D->meta, 'Mouse::Meta::Class');
-
-isnt(Point->meta, Point3D->meta, '... they are different metaclasses as well');
-
-# poke at Point
-
-is_deeply(
-       [ Point->meta->superclasses ],
-       [ 'Mouse::Object' ],
-       '... Point got the automagic base class');
-
-my @Point_methods = qw(meta new x y clear DESTROY);
-my @Point_attrs   = ('x', 'y');
-
-is_deeply(
-    [ sort @Point_methods                 ],
-    [ sort Point->meta->get_method_list() ],
-    '... we match the method list for Point');
-
-SKIP: {
-    skip "Mouse has no method introspection", 1 + @Point_methods;
-        
-    is_deeply(
-        [ sort @Point_attrs                      ],
-        [ sort Point->meta->get_attribute_list() ],
-        '... we match the attribute list for Point');  
-
-    foreach my $method (@Point_methods) {
-        ok(Point->meta->has_method($method), '... Point has the method "' . $method . '"');
-    }
-}
-
-foreach my $attr_name (@Point_attrs ) {
-       ok(Point->meta->has_attribute($attr_name), '... Point has the attribute "' . $attr_name . '"');    
-    my $attr = Point->meta->get_attribute($attr_name);
-       ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
-
-    SKIP: {
-        skip "Mouse type constraints are not objects", 2;
-        isa_ok($attr->type_constraint, 'Mouse::Meta::TypeConstraint'); 
-        is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');        
-    }
-}
-
-# poke at Point3D
-
-is_deeply(
-       [ Point3D->meta->superclasses ],
-       [ 'Point' ],
-       '... Point3D gets the parent given to it');
-
-my @Point3D_methods = qw(new meta clear DESTROY);
-my @Point3D_attrs   = ('z');
-
-SKIP: {
-    skip "Mouse has no method introspection", 2 + @Point3D_methods;
-
-    is_deeply(
-        [ sort @Point3D_methods                 ],
-        [ sort Point3D->meta->get_method_list() ],
-        '... we match the method list for Point3D');
-        
-    is_deeply(
-        [ sort @Point3D_attrs                      ],
-        [ sort Point3D->meta->get_attribute_list() ],
-        '... we match the attribute list for Point3D');        
-
-    foreach my $method (@Point3D_methods) {
-        ok(Point3D->meta->has_method($method), '... Point3D has the method "' . $method . '"');
-    }
-};
-
-foreach my $attr_name (@Point3D_attrs ) {
-       ok(Point3D->meta->has_attribute($attr_name), '... Point3D has the attribute "' . $attr_name . '"');    
-    my $attr = Point3D->meta->get_attribute($attr_name);
-       ok($attr->has_type_constraint, '... Attribute ' . $attr_name . ' has a type constraint');
-    SKIP: {
-        skip "Mouse type constraints are not objects", 2;
-        isa_ok($attr->type_constraint, 'Mouse::Meta::TypeConstraint'); 
-        is($attr->type_constraint->name, 'Int', '... Attribute ' . $attr_name . ' has an Int type constraint');        
-    };
-}
-
diff --git a/t/000_recipes/moose_cookbook_basics_recipe1.t b/t/000_recipes/moose_cookbook_basics_recipe1.t
new file mode 100644 (file)
index 0000000..6288f17
--- /dev/null
@@ -0,0 +1,222 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More 'no_plan';
+use Test::Exception;
+$| = 1;
+
+
+
+# =begin testing SETUP
+{
+
+  package Point;
+  use Mouse;
+
+  has 'x' => (isa => 'Int', is => 'rw', required => 1);
+  has 'y' => (isa => 'Int', is => 'rw', required => 1);
+
+  sub clear {
+      my $self = shift;
+      $self->x(0);
+      $self->y(0);
+  }
+
+  package Point3D;
+  use Mouse;
+
+  extends 'Point';
+
+  has 'z' => (isa => 'Int', is => 'rw', required => 1);
+
+  after 'clear' => sub {
+      my $self = shift;
+      $self->z(0);
+  };
+
+  package main;
+
+  # hash or hashrefs are ok for the constructor
+  my $point1 = Point->new(x => 5, y => 7);
+  my $point2 = Point->new({x => 5, y => 7});
+
+  my $point3d = Point3D->new(x => 5, y => 42, z => -5);
+}
+
+
+
+# =begin testing
+{
+my $point = Point->new( x => 1, y => 2 );
+isa_ok( $point, 'Point' );
+isa_ok( $point, 'Mouse::Object' );
+
+is( $point->x, 1, '... got the right value for x' );
+is( $point->y, 2, '... got the right value for y' );
+
+$point->y(10);
+is( $point->y, 10, '... got the right (changed) value for y' );
+
+dies_ok {
+    $point->y('Foo');
+}
+'... cannot assign a non-Int to y';
+
+dies_ok {
+    Point->new();
+}
+'... must provide required attributes to new';
+
+$point->clear();
+
+is( $point->x, 0, '... got the right (cleared) value for x' );
+is( $point->y, 0, '... got the right (cleared) value for y' );
+
+# check the type constraints on the constructor
+
+lives_ok {
+    Point->new( x => 0, y => 0 );
+}
+'... can assign a 0 to x and y';
+
+dies_ok {
+    Point->new( x => 10, y => 'Foo' );
+}
+'... cannot assign a non-Int to y';
+
+dies_ok {
+    Point->new( x => 'Foo', y => 10 );
+}
+'... cannot assign a non-Int to x';
+
+# Point3D
+
+my $point3d = Point3D->new( { x => 10, y => 15, z => 3 } );
+isa_ok( $point3d, 'Point3D' );
+isa_ok( $point3d, 'Point' );
+isa_ok( $point3d, 'Mouse::Object' );
+
+is( $point3d->x,     10, '... got the right value for x' );
+is( $point3d->y,     15, '... got the right value for y' );
+is( $point3d->{'z'}, 3,  '... got the right value for z' );
+
+$point3d->clear();
+
+is( $point3d->x, 0, '... got the right (cleared) value for x' );
+is( $point3d->y, 0, '... got the right (cleared) value for y' );
+is( $point3d->z, 0, '... got the right (cleared) value for z' );
+
+dies_ok {
+    Point3D->new( x => 10, y => 'Foo', z => 3 );
+}
+'... cannot assign a non-Int to y';
+
+dies_ok {
+    Point3D->new( x => 'Foo', y => 10, z => 3 );
+}
+'... cannot assign a non-Int to x';
+
+dies_ok {
+    Point3D->new( x => 0, y => 10, z => 'Bar' );
+}
+'... cannot assign a non-Int to z';
+
+dies_ok {
+    Point3D->new( x => 10, y => 3 );
+}
+'... z is a required attribute for Point3D';
+
+# test some class introspection
+
+can_ok( 'Point', 'meta' );
+isa_ok( Point->meta, 'Mouse::Meta::Class' );
+
+can_ok( 'Point3D', 'meta' );
+isa_ok( Point3D->meta, 'Mouse::Meta::Class' );
+
+isnt( Point->meta, Point3D->meta,
+    '... they are different metaclasses as well' );
+
+# poke at Point
+
+is_deeply(
+    [ Point->meta->superclasses ],
+    ['Mouse::Object'],
+    '... Point got the automagic base class'
+);
+
+my @Point_methods = qw(meta x y clear);
+my @Point_attrs = ( 'x', 'y' );
+
+is_deeply(
+    [ sort @Point_methods ],
+    [ sort Point->meta->get_method_list() ],
+    '... we match the method list for Point'
+);
+
+is_deeply(
+    [ sort @Point_attrs ],
+    [ sort Point->meta->get_attribute_list() ],
+    '... we match the attribute list for Point'
+);
+
+foreach my $method (@Point_methods) {
+    ok( Point->meta->has_method($method),
+        '... Point has the method "' . $method . '"' );
+}
+
+foreach my $attr_name (@Point_attrs) {
+    ok( Point->meta->has_attribute($attr_name),
+        '... Point has the attribute "' . $attr_name . '"' );
+    my $attr = Point->meta->get_attribute($attr_name);
+    ok( $attr->has_type_constraint,
+        '... Attribute ' . $attr_name . ' has a type constraint' );
+    isa_ok( $attr->type_constraint, 'Mouse::Meta::TypeConstraint' );
+    is( $attr->type_constraint->name, 'Int',
+        '... Attribute ' . $attr_name . ' has an Int type constraint' );
+}
+
+# poke at Point3D
+
+is_deeply(
+    [ Point3D->meta->superclasses ],
+    ['Point'],
+    '... Point3D gets the parent given to it'
+);
+
+my @Point3D_methods = qw( meta z clear );
+my @Point3D_attrs   = ('z');
+
+is_deeply(
+    [ sort @Point3D_methods ],
+    [ sort Point3D->meta->get_method_list() ],
+    '... we match the method list for Point3D'
+);
+
+is_deeply(
+    [ sort @Point3D_attrs ],
+    [ sort Point3D->meta->get_attribute_list() ],
+    '... we match the attribute list for Point3D'
+);
+
+foreach my $method (@Point3D_methods) {
+    ok( Point3D->meta->has_method($method),
+        '... Point3D has the method "' . $method . '"' );
+}
+
+foreach my $attr_name (@Point3D_attrs) {
+    ok( Point3D->meta->has_attribute($attr_name),
+        '... Point3D has the attribute "' . $attr_name . '"' );
+    my $attr = Point3D->meta->get_attribute($attr_name);
+    ok( $attr->has_type_constraint,
+        '... Attribute ' . $attr_name . ' has a type constraint' );
+    isa_ok( $attr->type_constraint, 'Mouse::Meta::TypeConstraint' );
+    is( $attr->type_constraint->name, 'Int',
+        '... Attribute ' . $attr_name . ' has an Int type constraint' );
+}
+}
+
+
+
+
+1;
diff --git a/t/000_recipes/moose_cookbook_basics_recipe6.t b/t/000_recipes/moose_cookbook_basics_recipe6.t
new file mode 100644 (file)
index 0000000..485abcf
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More 'no_plan';
+use Test::Exception;
+$| = 1;
+
+
+
+# =begin testing SETUP
+{
+
+  package Document::Page;
+  use Mouse;
+
+  has 'body' => ( is => 'rw', isa => 'Str', default => sub {''} );
+
+  sub create {
+      my $self = shift;
+      $self->open_page;
+      inner();
+      $self->close_page;
+  }
+
+  sub append_body {
+      my ( $self, $appendage ) = @_;
+      $self->body( $self->body . $appendage );
+  }
+
+  sub open_page  { (shift)->append_body('<page>') }
+  sub close_page { (shift)->append_body('</page>') }
+
+  package Document::PageWithHeadersAndFooters;
+  use Mouse;
+
+  extends 'Document::Page';
+
+  augment 'create' => sub {
+      my $self = shift;
+      $self->create_header;
+      inner();
+      $self->create_footer;
+  };
+
+  sub create_header { (shift)->append_body('<header/>') }
+  sub create_footer { (shift)->append_body('<footer/>') }
+
+  package TPSReport;
+  use Mouse;
+
+  extends 'Document::PageWithHeadersAndFooters';
+
+  augment 'create' => sub {
+      my $self = shift;
+      $self->create_tps_report;
+      inner();
+  };
+
+  sub create_tps_report {
+      (shift)->append_body('<report type="tps"/>');
+  }
+
+  # <page><header/><report type="tps"/><footer/></page>
+  my $report_xml = TPSReport->new->create;
+}
+
+
+
+# =begin testing
+{
+my $tps_report = TPSReport->new;
+isa_ok( $tps_report, 'TPSReport' );
+
+is(
+    $tps_report->create,
+    q{<page><header/><report type="tps"/><footer/></page>},
+    '... got the right TPS report'
+);
+}
+
+
+
+
+1;
diff --git a/t/000_recipes/moose_cookbook_extending_recipe3.t b/t/000_recipes/moose_cookbook_extending_recipe3.t
new file mode 100644 (file)
index 0000000..7470380
--- /dev/null
@@ -0,0 +1,75 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More 'no_plan';
+use Test::Exception;
+$| = 1;
+
+
+
+# =begin testing SETUP
+BEGIN {
+    eval 'use Test::Output;';
+    if ($@) {
+        diag 'Test::Output is required for this test';
+        ok(1);
+        exit 0;
+    }
+}
+
+
+
+# =begin testing SETUP
+{
+
+  package MyApp::Base;
+  use Mouse;
+
+  extends 'Mouse::Object';
+
+  before 'new' => sub { warn "Making a new " . $_[0] };
+
+  no Mouse;
+
+  package MyApp::UseMyBase;
+  use Mouse ();
+  use Mouse::Exporter;
+
+  Mouse::Exporter->setup_import_methods( also => 'Mouse' );
+
+  sub init_meta {
+      shift;
+      return Mouse->init_meta( @_, base_class => 'MyApp::Base' );
+  }
+}
+
+
+
+# =begin testing
+{
+{
+    package Foo;
+
+    MyApp::UseMyBase->import;
+
+    has( 'size' => ( is => 'rw' ) );
+}
+
+ok( Foo->isa('MyApp::Base'), 'Foo isa MyApp::Base' );
+
+ok( Foo->can('size'), 'Foo has a size method' );
+
+my $foo;
+stderr_like(
+    sub { $foo = Foo->new( size => 2 ) },
+    qr/^Making a new Foo/,
+    'got expected warning when calling Foo->new'
+);
+
+is( $foo->size(), 2, '$foo->size is 2' );
+}
+
+
+
+
+1;
@@ -10,8 +10,9 @@ $| = 1;
 # =begin testing SETUP
 {
 
-  package MyApp::Meta::Attribute::Trait::Labeled;
-  use Mouse::Role;
+  package MyApp::Meta::Attribute::Labeled;
+  use Mouse;
+  extends 'Mouse::Meta::Attribute';
 
   has label => (
       is        => 'rw',
@@ -19,17 +20,17 @@ $| = 1;
       predicate => 'has_label',
   );
 
-  package Mouse::Meta::Attribute::Custom::Trait::Labeled;
-  sub register_implementation {'MyApp::Meta::Attribute::Trait::Labeled'}
+  package Mouse::Meta::Attribute::Custom::Labeled;
+  sub register_implementation {'MyApp::Meta::Attribute::Labeled'}
 
   package MyApp::Website;
   use Mouse;
 
   has url => (
-      traits => [qw/Labeled/],
-      is     => 'rw',
-      isa    => 'Str',
-      label  => "The site's URL",
+      metaclass => 'Labeled',
+      is        => 'rw',
+      isa       => 'Str',
+      label     => "The site's URL",
   );
 
   has name => (
@@ -42,11 +43,10 @@ $| = 1;
 
       my $dump = '';
 
-      my %attributes = %{ $self->meta->get_attribute_map };
-      for my $name ( sort keys %attributes ) {
-          my $attribute = $attributes{$name};
+      for my $name ( sort $self->meta->get_attribute_list ) {
+          my $attribute = $self->meta->get_attribute($name);
 
-          if (   $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
+          if (   $attribute->isa('MyApp::Meta::Attribute::Labeled')
               && $attribute->has_label ) {
               $dump .= $attribute->label;
           }
@@ -70,10 +70,9 @@ $| = 1;
 
 # =begin testing
 {
-my $app2
-    = MyApp::Website->new( url => "http://google.com", name => "Google" );
+my $app = MyApp::Website->new( url => "http://google.com", name => "Google" );
 is(
-    $app2->dump, q{name: Google
+    $app->dump, q{name: Google
 The site's URL: http://google.com
 }, '... got the expected dump value'
 );
similarity index 83%
rename from t/000-recipes/moose_cookbook_meta_recipe3.t
rename to t/000_recipes/moose_cookbook_meta_recipe3.t
index fe1ab24..053a4bf 100644 (file)
@@ -42,9 +42,8 @@ $| = 1;
 
       my $dump = '';
 
-      my %attributes = %{ $self->meta->get_attribute_map };
-      for my $name ( sort keys %attributes ) {
-          my $attribute = $attributes{$name};
+      for my $name ( sort $self->meta->get_attribute_list ) {
+          my $attribute = $self->meta->get_attribute($name);
 
           if (   $attribute->does('MyApp::Meta::Attribute::Trait::Labeled')
               && $attribute->has_label ) {
@@ -54,8 +53,8 @@ $| = 1;
               $dump .= $name;
           }
 
-          my $reader = $attribute->get_read_method;
-          $dump .= ": " . $self->$reader . "\n";
+          my $reader = $attribute->get_read_method_ref;
+          $dump .= ": " . $reader->($self) . "\n";
       }
 
       return $dump;
diff --git a/t/000_recipes/moose_cookbook_roles_recipe1.t b/t/000_recipes/moose_cookbook_roles_recipe1.t
new file mode 100644 (file)
index 0000000..16f6775
--- /dev/null
@@ -0,0 +1,203 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More 'no_plan';
+use Test::Exception;
+$| = 1;
+
+
+
+# =begin testing SETUP
+{
+
+  package Eq;
+  use Mouse::Role;
+
+  requires 'equal_to';
+
+  sub not_equal_to {
+      my ( $self, $other ) = @_;
+      not $self->equal_to($other);
+  }
+
+  package Comparable;
+  use Mouse::Role;
+
+  with 'Eq';
+
+  requires 'compare';
+
+  sub equal_to {
+      my ( $self, $other ) = @_;
+      $self->compare($other) == 0;
+  }
+
+  sub greater_than {
+      my ( $self, $other ) = @_;
+      $self->compare($other) == 1;
+  }
+
+  sub less_than {
+      my ( $self, $other ) = @_;
+      $self->compare($other) == -1;
+  }
+
+  sub greater_than_or_equal_to {
+      my ( $self, $other ) = @_;
+      $self->greater_than($other) || $self->equal_to($other);
+  }
+
+  sub less_than_or_equal_to {
+      my ( $self, $other ) = @_;
+      $self->less_than($other) || $self->equal_to($other);
+  }
+
+  package Printable;
+  use Mouse::Role;
+
+  requires 'to_string';
+
+  package US::Currency;
+  use Mouse;
+
+  with 'Comparable', 'Printable';
+
+  has 'amount' => ( is => 'rw', isa => 'Num', default => 0 );
+
+  sub compare {
+      my ( $self, $other ) = @_;
+      $self->amount <=> $other->amount;
+  }
+
+  sub to_string {
+      my $self = shift;
+      sprintf '$%0.2f USD' => $self->amount;
+  }
+}
+
+
+
+# =begin testing
+{
+ok( US::Currency->does('Comparable'), '... US::Currency does Comparable' );
+ok( US::Currency->does('Eq'),         '... US::Currency does Eq' );
+ok( US::Currency->does('Printable'),  '... US::Currency does Printable' );
+
+my $hundred = US::Currency->new( amount => 100.00 );
+isa_ok( $hundred, 'US::Currency' );
+{
+local $TODO = 'UNIVERSAL::DOES is not supported';
+ok( eval{ $hundred->DOES("US::Currency") }, "UNIVERSAL::DOES for class" );
+ok( eval{ $hundred->DOES("Comparable") },   "UNIVERSAL::DOES for role" );
+}
+can_ok( $hundred, 'amount' );
+is( $hundred->amount, 100, '... got the right amount' );
+
+can_ok( $hundred, 'to_string' );
+is( $hundred->to_string, '$100.00 USD',
+    '... got the right stringified value' );
+
+ok( $hundred->does('Comparable'), '... US::Currency does Comparable' );
+ok( $hundred->does('Eq'),         '... US::Currency does Eq' );
+ok( $hundred->does('Printable'),  '... US::Currency does Printable' );
+
+my $fifty = US::Currency->new( amount => 50.00 );
+isa_ok( $fifty, 'US::Currency' );
+
+can_ok( $fifty, 'amount' );
+is( $fifty->amount, 50, '... got the right amount' );
+
+can_ok( $fifty, 'to_string' );
+is( $fifty->to_string, '$50.00 USD', '... got the right stringified value' );
+
+ok( $hundred->greater_than($fifty),             '... 100 gt 50' );
+ok( $hundred->greater_than_or_equal_to($fifty), '... 100 ge 50' );
+ok( !$hundred->less_than($fifty),               '... !100 lt 50' );
+ok( !$hundred->less_than_or_equal_to($fifty),   '... !100 le 50' );
+ok( !$hundred->equal_to($fifty),                '... !100 eq 50' );
+ok( $hundred->not_equal_to($fifty),             '... 100 ne 50' );
+
+ok( !$fifty->greater_than($hundred),             '... !50 gt 100' );
+ok( !$fifty->greater_than_or_equal_to($hundred), '... !50 ge 100' );
+ok( $fifty->less_than($hundred),                 '... 50 lt 100' );
+ok( $fifty->less_than_or_equal_to($hundred),     '... 50 le 100' );
+ok( !$fifty->equal_to($hundred),                 '... !50 eq 100' );
+ok( $fifty->not_equal_to($hundred),              '... 50 ne 100' );
+
+ok( !$fifty->greater_than($fifty),            '... !50 gt 50' );
+ok( $fifty->greater_than_or_equal_to($fifty), '... !50 ge 50' );
+ok( !$fifty->less_than($fifty),               '... 50 lt 50' );
+ok( $fifty->less_than_or_equal_to($fifty),    '... 50 le 50' );
+ok( $fifty->equal_to($fifty),                 '... 50 eq 50' );
+ok( !$fifty->not_equal_to($fifty),            '... !50 ne 50' );
+
+## ... check some meta-stuff
+
+# Eq
+
+my $eq_meta = Eq->meta;
+isa_ok( $eq_meta, 'Mouse::Meta::Role' );
+
+ok( $eq_meta->has_method('not_equal_to'), '... Eq has_method not_equal_to' );
+ok( $eq_meta->requires_method('equal_to'),
+    '... Eq requires_method not_equal_to' );
+
+# Comparable
+
+my $comparable_meta = Comparable->meta;
+isa_ok( $comparable_meta, 'Mouse::Meta::Role' );
+
+ok( $comparable_meta->does_role('Eq'), '... Comparable does Eq' );
+
+foreach my $method_name (
+    qw(
+    equal_to not_equal_to
+    greater_than greater_than_or_equal_to
+    less_than less_than_or_equal_to
+    )
+    ) {
+    ok( $comparable_meta->has_method($method_name),
+        '... Comparable has_method ' . $method_name );
+}
+
+ok( $comparable_meta->requires_method('compare'),
+    '... Comparable requires_method compare' );
+
+# Printable
+
+my $printable_meta = Printable->meta;
+isa_ok( $printable_meta, 'Mouse::Meta::Role' );
+
+ok( $printable_meta->requires_method('to_string'),
+    '... Printable requires_method to_string' );
+
+# US::Currency
+
+my $currency_meta = US::Currency->meta;
+isa_ok( $currency_meta, 'Mouse::Meta::Class' );
+
+ok( $currency_meta->does_role('Comparable'),
+    '... US::Currency does Comparable' );
+ok( $currency_meta->does_role('Eq'), '... US::Currency does Eq' );
+ok( $currency_meta->does_role('Printable'),
+    '... US::Currency does Printable' );
+
+foreach my $method_name (
+    qw(
+    amount
+    equal_to not_equal_to
+    compare
+    greater_than greater_than_or_equal_to
+    less_than less_than_or_equal_to
+    to_string
+    )
+    ) {
+    ok( $currency_meta->has_method($method_name),
+        '... US::Currency has_method ' . $method_name );
+}
+}
+
+
+
+
+1;
diff --git a/t/000_recipes/moose_cookbook_roles_recipe3.t b/t/000_recipes/moose_cookbook_roles_recipe3.t
new file mode 100644 (file)
index 0000000..750a543
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/perl -w
+
+use strict;
+use Test::More 'no_plan';
+use Test::Exception;
+$| = 1;
+
+
+
+# =begin testing SETUP
+{
+    # Not in the recipe, but needed for writing tests.
+    package Employee;
+
+    use Mouse;
+
+    has 'name' => (
+        is       => 'ro',
+        isa      => 'Str',
+        required => 1,
+    );
+
+    has 'work' => (
+        is        => 'rw',
+        isa       => 'Str',
+        predicate => 'has_work',
+    );
+}
+
+
+
+# =begin testing SETUP
+{
+
+  package MyApp::Role::Job::Manager;
+
+  use List::Util qw( first );
+
+  use Mouse::Role;
+
+  has 'employees' => (
+      is  => 'rw',
+      isa => 'ArrayRef[Employee]',
+  );
+
+  sub assign_work {
+      my $self = shift;
+      my $work = shift;
+
+      my $employee = first { !$_->has_work } @{ $self->employees };
+
+      die 'All my employees have work to do!' unless $employee;
+
+      $employee->work($work);
+  }
+
+  package main;
+
+  my $lisa = Employee->new( name => 'Lisa' );
+  MyApp::Role::Job::Manager->meta->apply($lisa);
+
+  my $homer = Employee->new( name => 'Homer' );
+  my $bart  = Employee->new( name => 'Bart' );
+  my $marge = Employee->new( name => 'Marge' );
+
+  $lisa->employees( [ $homer, $bart, $marge ] );
+  $lisa->assign_work('mow the lawn');
+}
+
+
+
+# =begin testing
+{
+{
+    my $lisa = Employee->new( name => 'Lisa' );
+    MyApp::Role::Job::Manager->meta->apply($lisa);
+
+    my $homer = Employee->new( name => 'Homer' );
+    my $bart  = Employee->new( name => 'Bart' );
+    my $marge = Employee->new( name => 'Marge' );
+
+    $lisa->employees( [ $homer, $bart, $marge ] );
+    $lisa->assign_work('mow the lawn');
+
+    ok( $lisa->does('MyApp::Role::Job::Manager'),
+        'lisa now does the manager role' );
+
+    is( $homer->work, 'mow the lawn',
+        'homer was assigned a task by lisa' );
+}
+}
+
+
+
+
+1;
similarity index 100%
rename from t/001-strict.t
rename to t/001_mouse/001-strict.t
similarity index 100%
rename from t/002-warnings.t
rename to t/001_mouse/002-warnings.t
similarity index 100%
rename from t/005-extends.t
rename to t/001_mouse/005-extends.t
similarity index 100%
rename from t/006-unimport.t
rename to t/001_mouse/006-unimport.t
similarity index 86%
rename from t/007-attributes.t
rename to t/001_mouse/007-attributes.t
index 4edd2d0..ae538e6 100644 (file)
@@ -1,9 +1,12 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 15;
+use Test::More tests => 18;
 use Test::Exception;
 
+use lib 't/lib';
+use Test::Mouse;
+
 do {
     package Class;
     use Mouse;
@@ -30,6 +33,10 @@ do {
 ok(!Class->can('x'), "No accessor is injected if 'is' has no value");
 can_ok('Class', 'y', 'z');
 
+has_attribute_ok 'Class', 'x';
+has_attribute_ok 'Class', 'y';
+has_attribute_ok 'Class', 'z';
+
 my $object = Class->new;
 
 ok(!$object->can('x'), "No accessor is injected if 'is' has no value");
diff --git a/t/001_mouse/008-default.t b/t/001_mouse/008-default.t
new file mode 100644 (file)
index 0000000..f77d01b
--- /dev/null
@@ -0,0 +1,53 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 36;
+
+do {
+    package Class;
+    use Mouse;
+
+    has 'x' => (
+        is      => 'rw',
+        default => 10,
+    );
+
+    has 'y' => (
+        is      => 'rw',
+        default => 20,
+    );
+
+    has 'z' => (
+        is => 'rw',
+    );
+};
+
+for(1 .. 2){
+    my $object = Class->new;
+    is($object->x, 10, "attribute has a default of 10");
+    is($object->y, 20, "attribute has a default of 20");
+    is($object->z, undef, "attribute has no default");
+
+    is($object->x(5), 5, "setting a new value");
+    is($object->y(25), 25, "setting a new value");
+    is($object->z(125), 125, "setting a new value");
+
+    is($object->x, 5, "setting a new value does not trigger default");
+    is($object->y, 25, "setting a new value does not trigger default");
+    is($object->z, 125, "setting a new value does not trigger default");
+
+    my $object2 = Class->new(x => 50);
+    is($object2->x, 50, "attribute was initialized to 50");
+    is($object2->y, 20, "attribute has a default of 20");
+    is($object2->z, undef, "attribute has no default");
+
+    is($object2->x(5), 5, "setting a new value");
+    is($object2->y(25), 25, "setting a new value");
+    is($object2->z(125), 125, "setting a new value");
+
+    is($object2->x, 5, "setting a new value does not trigger default");
+    is($object2->y, 25, "setting a new value does not trigger default");
+    is($object2->z, 125, "setting a new value does not trigger default");
+
+    Class->meta->make_immutable;
+}
similarity index 100%
rename from t/010-required.t
rename to t/001_mouse/010-required.t
similarity index 100%
rename from t/011-lazy.t
rename to t/001_mouse/011-lazy.t
similarity index 100%
rename from t/013-clearer.t
rename to t/001_mouse/013-clearer.t
similarity index 100%
rename from t/014-build.t
rename to t/001_mouse/014-build.t
similarity index 100%
rename from t/016-trigger.t
rename to t/001_mouse/016-trigger.t
similarity index 81%
rename from t/018-multiattr-has.t
rename to t/001_mouse/018-multiattr-has.t
index acd258b..8458e89 100644 (file)
@@ -19,7 +19,7 @@ do {
 };
 
 can_ok(Class => qw/a b c/);
-is(keys %{ Class->meta->get_attribute_map }, 3, "three attributes created");
+is_deeply([sort Class->meta->get_attribute_list], [sort qw/a b c/], "three attributes created");
 Class->new(a => 1, b => 2);
 
 is_deeply(\%trigger, { a => 1, b => 1 }, "correct triggers called");
similarity index 100%
rename from t/019-handles.t
rename to t/001_mouse/019-handles.t
similarity index 100%
rename from t/021-weak-ref.t
rename to t/001_mouse/021-weak-ref.t
diff --git a/t/001_mouse/022-init-arg.t b/t/001_mouse/022-init-arg.t
new file mode 100644 (file)
index 0000000..bc0d639
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 20;
+
+do {
+    package Class;
+    use Mouse;
+
+    has name => (
+        is       => 'rw',
+        isa      => 'Str',
+        init_arg => 'key',
+        default  => 'default',
+    );
+
+    has no_init_arg => (
+        is       => 'rw',
+        isa      => 'Str',
+        init_arg => undef,
+        default  => 'default',
+    );
+
+};
+
+for('mutable', 'immutable'){
+    my $object = Class->new;
+    is($object->name, 'default', "accessor uses attribute name ($_)");
+    is($object->{key}, undef, 'nothing in object->{init_arg}!');
+    is($object->{name}, 'default', 'value is in object->{name}');
+
+    my $object2 = Class->new(name => 'name', key => 'key');
+    is($object2->name, 'key', 'attribute value is from name');
+    is($object2->{key}, undef, 'no value for the init_arg');
+    is($object2->{name}, 'key', 'value is in key from name');
+
+    my $attr = $object2->meta->get_attribute('name');
+    ok($attr, 'got the attribute object by name (not init_arg)');
+    is($attr->name, 'name', 'name is name');
+    is($attr->init_arg, 'key', 'init_arg is key');
+
+    my $object3 = Class->new(no_init_arg => 'joe');
+    is($object3->no_init_arg, 'default', 'init_arg => undef ignores attribute name in the constructor');
+
+    Class->meta->make_immutable;
+}
similarity index 100%
rename from t/023-builder.t
rename to t/001_mouse/023-builder.t
similarity index 100%
rename from t/024-isa.t
rename to t/001_mouse/024-isa.t
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from t/025-more-isa.t
rename to t/001_mouse/025-more-isa.t
similarity index 100%
rename from t/029-new.t
rename to t/001_mouse/029-new.t
similarity index 100%
rename from t/030-has-plus.t
rename to t/001_mouse/030-has-plus.t
similarity index 80%
rename from t/031-clone.t
rename to t/001_mouse/031-clone.t
index be9cfa1..cc39e22 100644 (file)
@@ -1,9 +1,10 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 9;
+use Test::More tests => 12;
 use Test::Exception;
 
+my %triggered;
 do {
     package Foo;
     use Mouse;
@@ -27,6 +28,10 @@ do {
     has quux => (
         is => 'rw',
         init_arg => 'quuux',
+        trigger => sub{
+            my($self, $value) = @_;
+            $triggered{$self} = $value;
+        },
     );
 
     sub clone {
@@ -39,11 +44,17 @@ my $foo = Foo->new(bar => [ 1, 2, 3 ], quuux => "indeed");
 
 is($foo->foo, "foo", "attr 1",);
 is($foo->quux, "indeed", "init_arg respected");
+
+is $triggered{$foo}, "indeed";
+
 is_deeply($foo->bar, [ 1 .. 3 ], "attr 2");
 $foo->baz("foo");
 
 my $clone = $foo->clone(foo => "dancing", baz => "bar", quux => "nope", quuux => "yes");
 
+is $triggered{$foo},   "indeed";
+is $triggered{$clone}, "yes", 'clone_object() invokes triggers';
+
 is($clone->foo, "dancing", "overridden attr");
 is_deeply($clone->bar, [ 1 .. 3 ], "clone attr");
 is($clone->baz, "foo", "init_arg=undef means the attr is ignored");
@@ -55,6 +66,6 @@ throws_ok {
 
 throws_ok {
     Foo->meta->clone_object(Foo->meta)
-} qr/You must pass an instance of the metaclass \(Foo\), not \(Mo.se::Meta::Class=HASH\(\w+\)\)/;
+} qr/You must pass an instance of the metaclass \(Foo\), not \(Mouse::Meta::Class=HASH\(\w+\)\)/;
 
 
similarity index 100%
rename from t/033-requires.t
rename to t/001_mouse/033-requires.t
similarity index 100%
rename from t/038-main.t
rename to t/001_mouse/038-main.t
similarity index 68%
rename from t/039-subtype.t
rename to t/001_mouse/039-subtype.t
index 5c4d9e1..755c405 100644 (file)
@@ -1,9 +1,11 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 2;
+use Test::More tests => 7;
 use Test::Exception;
 
+use Mouse::Util::TypeConstraints;
+
 do {
     package My::Class;
     use Mouse;
@@ -24,3 +26,12 @@ ok(My::Class->new(name => 'foo'));
 
 throws_ok { My::Class->new(name => '') } qr/^Attribute \(name\) does not pass the type constraint because: The string is empty!/;
 
+my $st = subtype as 'Str', where{ length };
+
+ok $st->is_a_type_of('Str');
+ok!$st->is_a_type_of('NoemptyStr');
+
+ok $st->check('Foo');
+ok!$st->check(undef);
+ok!$st->check('');
+
similarity index 100%
rename from t/041-enum.t
rename to t/001_mouse/041-enum.t
similarity index 100%
rename from t/042-override.t
rename to t/001_mouse/042-override.t
similarity index 72%
rename from t/043-parameterized-type.t
rename to t/001_mouse/043-parameterized-type.t
index a7eae99..6eaeddd 100644 (file)
@@ -1,7 +1,7 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 16;
+use Test::More tests => 46;
 use Test::Exception;
 
 {
@@ -98,7 +98,7 @@ use Test::Exception;
         package Bar;
         use Mouse;
         use Mouse::Util::TypeConstraints;
-        
+
         subtype 'Bar::List'
             => as 'ArrayRef[HashRef]'
         ;
@@ -127,5 +127,60 @@ use Test::Exception;
     } qr/Attribute \(list\) does not pass the type constraint because: Validation failed for 'Bar::List' failed with value/, "Bad coercion parameter throws an error";
 }
 
+use Mouse::Util::TypeConstraints;
+
+my $t = Mouse::Util::TypeConstraints::find_or_parse_type_constraint('Maybe[Int]');
+ok $t->is_a_type_of($t),            "$t is a type of $t";
+ok $t->is_a_type_of('Maybe'),       "$t is a type of Maybe";
+
+# XXX: how about 'MaybeInt[ Int ]'?
+ok $t->is_a_type_of('Maybe[Int]'),  "$t is a type of Maybe[Int]";
+
+ok!$t->is_a_type_of('Int');
+
+ok $t->check(10);
+ok $t->check(undef);
+ok!$t->check(3.14);
+
+my $u = subtype 'MaybeInt', as 'Maybe[Int]';
+ok $u->is_a_type_of($t),             "$t is a type of $t";
+ok $u->is_a_type_of('Maybe'),        "$t is a type of Maybe";
+
+# XXX: how about 'MaybeInt[ Int ]'?
+ok $u->is_a_type_of('Maybe[Int]'),   "$t is a type of Maybe[Int]";
+
+ok!$u->is_a_type_of('Int');
+
+ok $u->check(10);
+ok $u->check(undef);
+ok!$u->check(3.14);
+
+# XXX: undefined hehaviour
+# ok $t->is_a_type_of($u);
+# ok $u->is_a_type_of($t);
+
+my $w = subtype as 'Maybe[ ArrayRef | HashRef ]';
+
+ok $w->check(undef);
+ok $w->check([]);
+ok $w->check({});
+ok!$w->check(sub{});
+
+ok $w->is_a_type_of('Maybe');
+ok $w->is_a_type_of('Maybe[ArrayRef|HashRef]');
+ok!$w->is_a_type_of('ArrayRef');
+
+my $x = Mouse::Util::TypeConstraints::find_or_parse_type_constraint('ArrayRef[ ArrayRef[ Int | Undef ] ]');
+
+ok $x->is_a_type_of('ArrayRef');
+ok $x->is_a_type_of('ArrayRef[ArrayRef[Int|Undef]]');
+ok!$x->is_a_type_of('ArrayRef[ArrayRef[Str]]');
+
+ok $x->check([]);
+ok $x->check([[]]);
+ok $x->check([[10]]);
+ok $x->check([[10, undef]]);
+ok!$x->check([[10, 3.14]]);
+ok!$x->check({});
 
 
similarity index 81%
rename from t/044-attribute-metaclass.t
rename to t/001_mouse/044-attribute-metaclass.t
index 71fdd11..4c0c38d 100644 (file)
@@ -1,11 +1,11 @@
 #!/usr/bin/env perl
 use strict;
 use warnings;
-use Test::More tests => 5;
+use Test::More tests => 7;
 use lib 't/lib';
 
 do {
-    # copied from  MouseX::AttributeHelpers;
+    # copied from  MooseX::AttributeHelpers;
     package MouseX::AttributeHelpers::Trait::Base;
     use Mouse::Role;
     use Mouse::Util::TypeConstraints;
@@ -56,8 +56,8 @@ do {
 
     # extend the parents stuff to make sure
     # certain bits are now required ...
-    #has '+default'         => (required => 1);
-    #has '+type_constraint' => (required => 1);
+    #has 'default'         => (required => 1);
+    has 'type_constraint' => (is => 'rw', required => 1);
 
     ## Methods called prior to instantiation
 
@@ -131,8 +131,8 @@ do {
         # grab the reader and writer methods
         # as well, this will be useful for
         # our method provider constructors
-        my $attr_reader = $attr->get_read_method;
-        my $attr_writer = $attr->get_write_method;
+        my $attr_reader = $attr->get_read_method_ref;
+        my $attr_writer = $attr->get_write_method_ref;
 
 
         # before we install them, lets
@@ -203,43 +203,48 @@ do {
 
     sub helper_type { 'Num' }
 
-    has 'method_constructors' => (
-        is      => 'ro',
-        isa     => 'HashRef',
-        lazy    => 1,
-        default => sub {
-            return +{
-                set => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[1]) };
-                },
-                add => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[0]->$reader() + $_[1]) };
-                },
-                sub => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[0]->$reader() - $_[1]) };
+    has 'method_constructors' => (\r
+        is      => 'ro',\r
+        isa     => 'HashRef',\r
+        lazy    => 1,\r
+        default => sub {\r
+            return +{\r
+                set => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $_[1] ) };\r
                 },
-                mul => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[0]->$reader() * $_[1]) };
+                get => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $reader->( $_[0] ) };\r
                 },
-                div => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[0]->$reader() / $_[1]) };
-                },
-                mod => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer($_[0]->$reader() % $_[1]) };
-                },
-                abs => sub {
-                    my ($attr, $reader, $writer) = @_;
-                    return sub { $_[0]->$writer(abs($_[0]->$reader()) ) };
-                },
-            }
-        }
-    );
+                add => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $reader->( $_[0] ) + $_[1] ) };\r
+                },\r
+                sub => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $reader->( $_[0] ) - $_[1] ) };\r
+                },\r
+                mul => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $reader->( $_[0] ) * $_[1] ) };\r
+                },\r
+                div => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $reader->( $_[0] ) / $_[1] ) };\r
+                },\r
+                mod => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], $reader->( $_[0] ) % $_[1] ) };\r
+                },\r
+                abs => sub {\r
+                    my ( $attr, $reader, $writer ) = @_;\r
+                    return sub { $writer->( $_[0], abs( $reader->( $_[0] ) ) ) };\r
+                },\r
+            };\r
+        }\r
+    );\r
+\r
 
     package MouseX::AttributeHelpers::Number;
     use Mouse;
@@ -273,11 +278,14 @@ do {
     use Mouse;
 
     has 'ii' => (
-        is  => 'rw',
         isa => 'Num',
+        predicate => 'has_ii',
+
         provides => {
             sub => 'ii_minus',
             abs => 'ii_abs',
+            get => 'get_ii',
+            set => 'set_ii',
        },
 
        traits => [qw(MyNumber)],
@@ -293,6 +301,12 @@ can_ok 'MyClassWithTraits', qw(ii_minus ii_abs);
 
 $k = MyClassWithTraits->new(ii => 10);
 $k->ii_minus(100);
-is $k->ii,    -90;
-is $k->ii_abs, 90;
+is $k->get_ii, -90;
+$k->ii_abs;
+is $k->get_ii,  90;
+
+$k->set_ii(10);
+is $k->get_ii, 10;
+$k->ii_abs;
+is $k->get_ii, 10;
 
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from t/049-coercion-application-order.t
rename to t/001_mouse/049-coercion-application-order.t
old mode 100755 (executable)
new mode 100644 (file)
similarity index 100%
rename from t/051_throw_error.t
rename to t/001_mouse/051_throw_error.t
diff --git a/t/001_mouse/052-undefined-type-in-union.t b/t/001_mouse/052-undefined-type-in-union.t
new file mode 100644 (file)
index 0000000..b2c1235
--- /dev/null
@@ -0,0 +1,34 @@
+#!perl
+
+use strict;
+use warnings;
+use Test::More tests => 6;
+
+use Mouse::Util::TypeConstraints;
+
+{
+    package Foo;
+    use Mouse;
+
+    has my_class => (
+        is  => 'rw',
+        isa => 'My::New::Class | Str',
+    );
+}
+my $t = Foo->meta->get_attribute('my_class')->type_constraint;
+
+eval q{
+    package My::New::Class;
+    use Mouse;
+    package My::New::DerivedClass;
+    use Mouse;
+    extends 'My::New::Class';
+};
+
+isa_ok $t, 'Mouse::Meta::TypeConstraint';
+ok $t->check(My::New::Class->new);
+ok $t->check(My::New::DerivedClass->new);
+ok $t->check('Foo');
+ok!$t->check(undef);
+ok!$t->check(bless {}, 'Foo');
+
diff --git a/t/001_mouse/053-extends-meta.t b/t/001_mouse/053-extends-meta.t
new file mode 100644 (file)
index 0000000..538aba7
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 14;
+use Test::Exception;
+{
+    package My::Meta::Class;
+    use Mouse;
+    extends 'Mouse::Meta::Class';
+
+    has my_class_attr => (
+        is      => 'rw',
+        default => 42,
+    );
+    package My::Meta::Role;
+    use Mouse;
+    extends 'Mouse::Meta::Role';
+
+    has my_role_attr => (
+        is      => 'rw',
+        default => 43,
+    );
+    package My::Meta::Attribute;
+    use Mouse;
+    extends 'Mouse::Meta::Attribute';
+
+    has my_attr_attr => (
+        is      => 'rw',
+        default => 44,
+    );
+}
+
+my $meta = My::Meta::Class->initialize('Foo');
+isa_ok $meta, 'My::Meta::Class';
+isa_ok $meta->meta, 'Mouse::Meta::Class';
+can_ok $meta, qw(name my_class_attr);
+is $meta->name, 'Foo';
+lives_and{
+    is $meta->my_class_attr, 42;
+};
+
+$meta = My::Meta::Role->initialize('Bar');
+isa_ok $meta, 'My::Meta::Role';
+isa_ok $meta->meta, 'Mouse::Meta::Class';
+can_ok $meta, qw(name my_role_attr);
+is $meta->name, 'Bar';
+lives_and{
+    is $meta->my_role_attr, 43;
+};
+
+$meta = My::Meta::Attribute->new('baz');
+isa_ok $meta, 'My::Meta::Attribute';
+can_ok $meta, qw(name my_attr_attr);
+is $meta->name, 'baz';
+lives_and{
+    is $meta->my_attr_attr, 44;
+};
+
diff --git a/t/001_mouse/054-anon-leak.t b/t/001_mouse/054-anon-leak.t
new file mode 100644 (file)
index 0000000..68bd1c6
--- /dev/null
@@ -0,0 +1,51 @@
+#!perl\r
+# This is based on Class-MOP/t/312_anon_class_leak.t\r
+use strict;\r
+use warnings;\r
+use Test::More;\r
+\r
+BEGIN {\r
+    eval "use Test::LeakTrace 0.10;";\r
+    plan skip_all => "Test::LeakTrace 0.10 is required for this test" if $@;\r
+}\r
+\r
+plan tests => 6;\r
+\r
+use Mouse ();\r
+{\r
+    package MyRole;\r
+    use Mouse::Role;\r
+\r
+    sub my_role_method{ }\r
+}\r
+\r
+# 5.10.0 has a bug on weaken($hash_ref) which leaks an AV.\r
+my $expected = ( $] == 5.010_000 ? 1 : 0 );\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Class->create_anon_class();\r
+} '<=', $expected, 'create_anon_class()';\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Class->create_anon_class(superclasses => ['Mouse::Meta::Class']);\r
+} '<=', $expected, 'create_anon_class() with superclasses';\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Class->create_anon_class(attributes => [\r
+        Mouse::Meta::Attribute->new('foo', is => 'bare'),\r
+    ]);\r
+} '<=', $expected, 'create_anon_class() with attributes';\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Class->create_anon_class(roles => [qw(MyRole)]);\r
+} '<=', $expected, 'create_anon_class() with roles';\r
+\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Role->create_anon_role();\r
+} '<=', $expected, 'create_anon_role()';\r
+\r
+leaks_cmp_ok {\r
+    Mouse::Meta::Role->create_anon_role(roles => [qw(MyRole)]);\r
+} '<=', $expected, 'create_anon_role() with roles';\r
+\r
diff --git a/t/001_mouse/055-exporter.t b/t/001_mouse/055-exporter.t
new file mode 100644 (file)
index 0000000..7f945c6
--- /dev/null
@@ -0,0 +1,81 @@
+#!perl\r
+use strict;\r
+use warnings;\r
+use Test::More tests => 14;\r
+\r
+use Mouse ();\r
+\r
+BEGIN{\r
+    package MyMouse;\r
+    use Mouse;\r
+    Mouse::Exporter->setup_import_methods(\r
+        as_is => [qw(foo)],\r
+        also  => [qw(Mouse)],\r
+    );\r
+\r
+    sub foo{ 100 }\r
+\r
+    $INC{'MyMouse.pm'}++;\r
+\r
+    package MyMouseEx;\r
+    use Mouse;\r
+    Mouse::Exporter->setup_import_methods(\r
+        as_is => [\&bar],\r
+        also  => [qw(MyMouse)],\r
+\r
+#        groups => {\r
+#            foobar_only => [qw(foo bar)],\r
+#        },\r
+    );\r
+\r
+    sub bar{ 200 }\r
+\r
+    $INC{'MyMouseEx.pm'}++;\r
+}\r
+\r
+can_ok 'MyMouse',   qw(import unimport);\r
+can_ok 'MyMouseEx', qw(import unimport);\r
+\r
+{\r
+    package MyApp;\r
+    use Test::More;\r
+    use MyMouse;\r
+\r
+    can_ok __PACKAGE__, 'meta';\r
+    ok defined(&foo), 'foo is imported';\r
+    ok defined(&has), 'has is also imported';\r
+\r
+    no MyMouse;\r
+\r
+    ok !defined(&foo), 'foo is unimported';\r
+    ok !defined(&has), 'has is also unimported';\r
+}\r
+{\r
+    package MyAppEx;\r
+    use Test::More;\r
+    use MyMouseEx;\r
+\r
+    can_ok __PACKAGE__, 'meta';\r
+    ok defined(&foo), 'foo is imported';\r
+    ok defined(&bar), 'foo is also imported';\r
+    ok defined(&has), 'has is also imported';\r
+\r
+    no MyMouseEx;\r
+\r
+    ok !defined(&foo), 'foo is unimported';\r
+    ok !defined(&bar), 'foo is also unimported';\r
+    ok !defined(&has), 'has is also unimported';\r
+}\r
+\r
+# exporting groups are not implemented in Moose::Exporter\r
+#{\r
+#    package MyAppExTags;\r
+#    use Test::More;\r
+#    use MyMouseEx qw(:foobar_only);\r
+#\r
+#    can_ok __PACKAGE__, 'meta';\r
+#    ok defined(&foo);\r
+#    ok defined(&bar);\r
+#    ok!defined(&has), "export tags";\r
+#}\r
+\r
diff --git a/t/001_mouse/056-role-combine.t b/t/001_mouse/056-role-combine.t
new file mode 100644 (file)
index 0000000..6ebe4e3
--- /dev/null
@@ -0,0 +1,35 @@
+#!perl\r
+use strict;\r
+use warnings;\r
+use Test::More tests => 2;\r
+use Test::Exception;\r
+{\r
+    package RoleA;\r
+    use Mouse::Role;\r
+\r
+    sub foo { }\r
+    sub bar { }\r
+}\r
+{\r
+    package RoleB;\r
+    use Mouse::Role;\r
+\r
+    sub foo { }\r
+    sub bar { }\r
+}\r
+{\r
+    package Class;\r
+    use Mouse;\r
+    use Test::More;\r
+    use Test::Exception;\r
+\r
+    throws_ok {\r
+        with qw(RoleA RoleB);\r
+    } qr/Due to method name conflicts in roles 'RoleA' and 'RoleB', the methods 'bar' and 'foo' must be/;\r
+\r
+    lives_ok {\r
+        with RoleA => { -excludes => ['foo'] },\r
+             RoleB => { -excludes => ['bar'] },\r
+        ;\r
+    };\r
+}\r
similarity index 86%
rename from t/100-meta-class.t
rename to t/001_mouse/100-meta-class.t
index c76b2da..15da58c 100644 (file)
@@ -101,13 +101,12 @@ can_ok($child_meta, 'find_method_by_name');
 is $child_meta->find_method_by_name('child_method')->fully_qualified_name, 'Child::child_method';
 is $child_meta->find_method_by_name('pawn')->fully_qualified_name,         'Class::pawn';
 
-{
-    local $TODO = 'should be Class::MY_CONST';
-    is( join(' ', sort map{ $_->fully_qualified_name } grep{ $_->package_name ne 'Mouse::Object' } $child_meta->get_all_methods),
-        join(' ', sort qw(
-            Child::bishop Child::child_method Child::meta
 
-            Class::MY_CONST Class::has_pawn Class::pawn Class::stub Class::stub_with_attr
-        ))
-    );
-}
+is( join(' ', sort map{ $_->fully_qualified_name } grep{ $_->package_name ne 'Mouse::Object' } $child_meta->get_all_methods),
+    join(' ', sort qw(
+        Child::bishop Child::child_method Child::meta
+
+        Class::MY_CONST Class::has_pawn Class::pawn Class::stub Class::stub_with_attr
+    ))
+);
+
diff --git a/t/008-default.t b/t/008-default.t
deleted file mode 100644 (file)
index 6fc4c20..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env perl
-use strict;
-use warnings;
-use Test::More tests => 18;
-
-do {
-    package Class;
-    use Mouse;
-
-    has 'x' => (
-        is      => 'rw',
-        default => 10,
-    );
-
-    has 'y' => (
-        is      => 'rw',
-        default => 20,
-    );
-
-    has 'z' => (
-        is => 'rw',
-    );
-};
-
-my $object = Class->new;
-is($object->x, 10, "attribute has a default of 10");
-is($object->y, 20, "attribute has a default of 20");
-is($object->z, undef, "attribute has no default");
-
-is($object->x(5), 5, "setting a new value");
-is($object->y(25), 25, "setting a new value");
-is($object->z(125), 125, "setting a new value");
-
-is($object->x, 5, "setting a new value does not trigger default");
-is($object->y, 25, "setting a new value does not trigger default");
-is($object->z, 125, "setting a new value does not trigger default");
-
-my $object2 = Class->new(x => 50);
-is($object2->x, 50, "attribute was initialized to 50");
-is($object2->y, 20, "attribute has a default of 20");
-is($object2->z, undef, "attribute has no default");
-
-is($object2->x(5), 5, "setting a new value");
-is($object2->y(25), 25, "setting a new value");
-is($object2->z(125), 125, "setting a new value");
-
-is($object2->x, 5, "setting a new value does not trigger default");
-is($object2->y, 25, "setting a new value does not trigger default");
-is($object2->z, 125, "setting a new value does not trigger default");
-
diff --git a/t/010_basics/001_basic_class_setup.t b/t/010_basics/001_basic_class_setup.t
new file mode 100755 (executable)
index 0000000..348d41a
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 29;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+}
+
+can_ok('Foo', 'meta');
+isa_ok(Foo->meta, 'Mouse::Meta::Class');
+
+ok(Foo->meta->has_method('meta'), '... we got the &meta method');
+ok(Foo->isa('Mouse::Object'), '... Foo is automagically a Mouse::Object');
+
+dies_ok {
+   Foo->meta->has_method()
+} '... has_method requires an arg';
+
+can_ok('Foo', 'does');
+
+foreach my $function (qw(
+                         extends
+                         has
+                         before after around
+                         blessed confess
+                         type subtype as where
+                         coerce from via
+                         find_type_constraint
+                         )) {
+    ok(!Foo->meta->has_method($function), '... the meta does not treat "' . $function . '" as a method');
+}
+
+foreach my $import (qw(
+    blessed
+    try
+    catch
+    in_global_destruction
+)) {
+    ok(!Mouse::Object->can($import), "no namespace pollution in Mouse::Object ($import)" );
+
+    local $TODO = $import eq 'blessed' ? "no automatic namespace cleaning yet" : undef;
+    ok(!Foo->can($import), "no namespace pollution in Mouse::Object ($import)" );
+}
diff --git a/t/010_basics/002_require_superclasses.t b/t/010_basics/002_require_superclasses.t
new file mode 100755 (executable)
index 0000000..da4776a
--- /dev/null
@@ -0,0 +1,47 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib 't/lib', 'lib';
+
+use Test::More tests => 4;
+use Test::Exception;
+
+
+
+{
+
+    package Bar;
+    use Mouse;
+
+    ::lives_ok { extends 'Foo' } 'loaded Foo superclass correctly';
+}
+
+{
+
+    package Baz;
+    use Mouse;
+
+    ::lives_ok { extends 'Bar' } 'loaded (inline) Bar superclass correctly';
+}
+
+{
+
+    package Foo::Bar;
+    use Mouse;
+
+    ::lives_ok { extends 'Foo', 'Bar' }
+    'loaded Foo and (inline) Bar superclass correctly';
+}
+
+{
+
+    package Bling;
+    use Mouse;
+
+    ::throws_ok { extends 'No::Class' }
+    qr{Can't locate No/Class\.pm in \@INC},
+    'correct error when superclass could not be found';
+}
+
diff --git a/t/010_basics/003_super_and_override.t b/t/010_basics/003_super_and_override.t
new file mode 100755 (executable)
index 0000000..600d5db
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 16;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    sub foo { 'Foo::foo' }
+    sub bar { 'Foo::bar' }
+    sub baz { 'Foo::baz' }
+
+    package Bar;
+    use Mouse;
+
+    extends 'Foo';
+
+    override bar => sub { 'Bar::bar -> ' . super() };
+
+    package Baz;
+    use Mouse;
+
+    extends 'Bar';
+
+    override bar => sub { 'Baz::bar -> ' . super() };
+    override baz => sub { 'Baz::baz -> ' . super() };
+
+    no Mouse; # ensure super() still works after unimport
+}
+
+my $baz = Baz->new();
+isa_ok($baz, 'Baz');
+isa_ok($baz, 'Bar');
+isa_ok($baz, 'Foo');
+
+is($baz->foo(), 'Foo::foo', '... got the right value from &foo');
+is($baz->bar(), 'Baz::bar -> Bar::bar -> Foo::bar', '... got the right value from &bar');
+is($baz->baz(), 'Baz::baz -> Foo::baz', '... got the right value from &baz');
+
+my $bar = Bar->new();
+isa_ok($bar, 'Bar');
+isa_ok($bar, 'Foo');
+
+is($bar->foo(), 'Foo::foo', '... got the right value from &foo');
+is($bar->bar(), 'Bar::bar -> Foo::bar', '... got the right value from &bar');
+is($bar->baz(), 'Foo::baz', '... got the right value from &baz');
+
+my $foo = Foo->new();
+isa_ok($foo, 'Foo');
+
+is($foo->foo(), 'Foo::foo', '... got the right value from &foo');
+is($foo->bar(), 'Foo::bar', '... got the right value from &bar');
+is($foo->baz(), 'Foo::baz', '... got the right value from &baz');
+
+# some error cases
+
+{
+    package Bling;
+    use Mouse;
+
+    sub bling { 'Bling::bling' }
+
+    package Bling::Bling;
+    use Mouse;
+
+    extends 'Bling';
+
+    sub bling { 'Bling::bling' }
+
+    ::dies_ok {
+        override 'bling' => sub {};
+    } '... cannot override a method which has a local equivalent';
+
+}
+
diff --git a/t/010_basics/004_inner_and_augment.t b/t/010_basics/004_inner_and_augment.t
new file mode 100755 (executable)
index 0000000..14c4de1
--- /dev/null
@@ -0,0 +1,86 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 16;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    sub foo { 'Foo::foo(' . (inner() || '') . ')' }
+    sub bar { 'Foo::bar(' . (inner() || '') . ')' }
+    sub baz { 'Foo::baz(' . (inner() || '') . ')' }
+
+    package Bar;
+    use Mouse;
+
+    extends 'Foo';
+
+    augment foo => sub { 'Bar::foo(' . (inner() || '') . ')' };
+    augment bar => sub { 'Bar::bar' };
+
+    no Mouse; # ensure inner() still works after unimport
+
+    package Baz;
+    use Mouse;
+
+    extends 'Bar';
+
+    augment foo => sub { 'Baz::foo' };
+    augment baz => sub { 'Baz::baz' };
+
+    # this will actually never run,
+    # because Bar::bar does not call inner()
+    augment bar => sub { 'Baz::bar' };
+}
+
+my $baz = Baz->new();
+isa_ok($baz, 'Baz');
+isa_ok($baz, 'Bar');
+isa_ok($baz, 'Foo');
+
+is($baz->foo(), 'Foo::foo(Bar::foo(Baz::foo))', '... got the right value from &foo');
+is($baz->bar(), 'Foo::bar(Bar::bar)', '... got the right value from &bar');
+is($baz->baz(), 'Foo::baz(Baz::baz)', '... got the right value from &baz');
+
+my $bar = Bar->new();
+isa_ok($bar, 'Bar');
+isa_ok($bar, 'Foo');
+
+is($bar->foo(), 'Foo::foo(Bar::foo())', '... got the right value from &foo');
+is($bar->bar(), 'Foo::bar(Bar::bar)', '... got the right value from &bar');
+is($bar->baz(), 'Foo::baz()', '... got the right value from &baz');
+
+my $foo = Foo->new();
+isa_ok($foo, 'Foo');
+
+is($foo->foo(), 'Foo::foo()', '... got the right value from &foo');
+is($foo->bar(), 'Foo::bar()', '... got the right value from &bar');
+is($foo->baz(), 'Foo::baz()', '... got the right value from &baz');
+
+# some error cases
+
+{
+    package Bling;
+    use Mouse;
+
+    sub bling { 'Bling::bling' }
+
+    package Bling::Bling;
+    use Mouse;
+
+    extends 'Bling';
+
+    sub bling { 'Bling::bling' }
+
+    ::dies_ok {
+        augment 'bling' => sub {};
+    } '... cannot augment a method which has a local equivalent';
+
+}
+
diff --git a/t/010_basics/005_override_augment_inner_super.t b/t/010_basics/005_override_augment_inner_super.t
new file mode 100755 (executable)
index 0000000..c7ae92a
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 5;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    sub foo { 'Foo::foo(' . (inner() || '') . ')' };
+    sub bar { 'Foo::bar(' . (inner() || '') . ')' }
+
+    package Bar;
+    use Mouse;
+
+    extends 'Foo';
+
+    augment  'foo' => sub { 'Bar::foo' };
+    override 'bar' => sub { 'Bar::bar -> ' . super() };
+
+    package Baz;
+    use Mouse;
+
+    extends 'Bar';
+
+    override 'foo' => sub { 'Baz::foo -> ' . super() };
+    augment  'bar' => sub { 'Baz::bar' };
+}
+
+my $baz = Baz->new();
+isa_ok($baz, 'Baz');
+isa_ok($baz, 'Bar');
+isa_ok($baz, 'Foo');
+
+=pod
+
+Let em clarify what is happening here. Baz::foo is calling
+super(), which calls Bar::foo, which is an augmented sub
+that calls Foo::foo, then calls inner() which actually
+then calls Bar::foo. Confusing I know,.. but this is
+*exactly* what is it supposed to do :)
+
+=cut
+
+is($baz->foo,
+  'Baz::foo -> Foo::foo(Bar::foo)',
+  '... got the right value from mixed augment/override foo');
+
+=pod
+
+Allow me to clarify this one now ...
+
+Since Baz::bar is an augment routine, it needs to find the
+correct inner() to be called by. In this case it is Foo::bar.
+However, Bar::bar is in-between us, so it should actually be
+called first. Bar::bar is an overriden sub, and calls super()
+which in turn then calls our Foo::bar, which calls inner(),
+which calls Baz::bar.
+
+Confusing I know, but it is correct :)
+
+=cut
+
+{
+    local $TODO = 'mixed augment/override is not supported';
+    is($baz->bar,
+        'Bar::bar -> Foo::bar(Baz::bar)',
+        '... got the right value from mixed augment/override bar');
+}
diff --git a/t/010_basics/006_override_and_foreign_classes.t b/t/010_basics/006_override_and_foreign_classes.t
new file mode 100755 (executable)
index 0000000..043d733
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+
+
+
+=pod
+
+This just tests the interaction of override/super
+with non-Mouse superclasses. It really should not
+cause issues, the only thing it does is to create
+a metaclass for Foo so that it can find the right
+super method.
+
+This may end up being a sensitive issue for some
+non-Mouse classes, but in 99% of the cases it
+should be just fine.
+
+=cut
+
+{
+    package Foo;
+    use strict;
+    use warnings;
+
+    sub new { bless {} => shift() }
+
+    sub foo { 'Foo::foo' }
+    sub bar { 'Foo::bar' }
+    sub baz { 'Foo::baz' }
+
+    package Bar;
+    use Mouse;
+
+    extends 'Foo';
+
+    override bar => sub { 'Bar::bar -> ' . super() };
+
+    package Baz;
+    use Mouse;
+
+    extends 'Bar';
+
+    override bar => sub { 'Baz::bar -> ' . super() };
+    override baz => sub { 'Baz::baz -> ' . super() };
+}
+
+my $baz = Baz->new();
+isa_ok($baz, 'Baz');
+isa_ok($baz, 'Bar');
+isa_ok($baz, 'Foo');
+
+is($baz->foo(), 'Foo::foo', '... got the right value from &foo');
+is($baz->bar(), 'Baz::bar -> Bar::bar -> Foo::bar', '... got the right value from &bar');
+is($baz->baz(), 'Baz::baz -> Foo::baz', '... got the right value from &baz');
+
+my $bar = Bar->new();
+isa_ok($bar, 'Bar');
+isa_ok($bar, 'Foo');
+
+is($bar->foo(), 'Foo::foo', '... got the right value from &foo');
+is($bar->bar(), 'Bar::bar -> Foo::bar', '... got the right value from &bar');
+is($bar->baz(), 'Foo::baz', '... got the right value from &baz');
+
+my $foo = Foo->new();
+isa_ok($foo, 'Foo');
+
+is($foo->foo(), 'Foo::foo', '... got the right value from &foo');
+is($foo->bar(), 'Foo::bar', '... got the right value from &bar');
+is($foo->baz(), 'Foo::baz', '... got the right value from &baz');
\ No newline at end of file
diff --git a/t/010_basics/007_always_strict_warnings.t b/t/010_basics/007_always_strict_warnings.t
new file mode 100755 (executable)
index 0000000..0c65f9e
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/perl
+
+use Test::More tests => 15;
+
+# for classes ...
+{
+    package Foo;
+    use Mouse;
+
+    eval '$foo = 5;';
+    ::ok($@, '... got an error because strict is on');
+    ::like($@, qr/Global symbol \"\$foo\" requires explicit package name at/, '... got the right error');
+
+    {
+        my $warn;
+        local $SIG{__WARN__} = sub { $warn = $_[0] };
+
+        ::ok(!$warn, '... no warning yet');
+
+        eval 'my $bar = 1 + "hello"';
+
+        ::ok($warn, '... got a warning');
+        ::like($warn, qr/Argument \"hello\" isn\'t numeric in addition \(\+\)/, '.. and it is the right warning');
+    }
+}
+
+# and for roles ...
+{
+    package Bar;
+    use Mouse::Role;
+
+    eval '$foo = 5;';
+    ::ok($@, '... got an error because strict is on');
+    ::like($@, qr/Global symbol \"\$foo\" requires explicit package name at/, '... got the right error');
+
+    {
+        my $warn;
+        local $SIG{__WARN__} = sub { $warn = $_[0] };
+
+        ::ok(!$warn, '... no warning yet');
+
+        eval 'my $bar = 1 + "hello"';
+
+        ::ok($warn, '... got a warning');
+        ::like($warn, qr/Argument \"hello\" isn\'t numeric in addition \(\+\)/, '.. and it is the right warning');
+    }
+}
+
+# and for exporters
+{
+    package Bar;
+    use Mouse::Exporter;
+
+    eval '$foo = 5;';
+    ::ok($@, '... got an error because strict is on');
+    ::like($@, qr/Global symbol \"\$foo\" requires explicit package name at/, '... got the right error');
+
+    {
+        my $warn;
+        local $SIG{__WARN__} = sub { $warn = $_[0] };
+
+        ::ok(!$warn, '... no warning yet');
+
+        eval 'my $bar = 1 + "hello"';
+
+        ::ok($warn, '... got a warning');
+        ::like($warn, qr/Argument \"hello\" isn\'t numeric in addition \(\+\)/, '.. and it is the right warning');
+    }
+}
diff --git a/t/010_basics/008_wrapped_method_cxt_propagation.t b/t/010_basics/008_wrapped_method_cxt_propagation.t
new file mode 100755 (executable)
index 0000000..664b187
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+
+
+
+{
+    package TouchyBase;
+    use Mouse;
+
+    has x => ( is => 'rw', default => 0 );
+
+    sub inc { $_[0]->x( 1 + $_[0]->x ) }
+
+    sub scalar_or_array {
+        wantarray ? (qw/a b c/) : "x";
+    }
+
+    sub void {
+        die "this must be void context" if defined wantarray;
+    }
+
+    package AfterSub;
+    use Mouse;
+
+    extends "TouchyBase";
+
+    after qw/scalar_or_array void/ => sub {
+        my $self = shift;
+        $self->inc;
+    }
+}
+
+my $base = TouchyBase->new;
+my $after = AfterSub->new;
+
+foreach my $obj ( $base, $after ) {
+    my $class = ref $obj;
+    my @array = $obj->scalar_or_array;
+    my $scalar = $obj->scalar_or_array;
+
+    is_deeply(\@array, [qw/a b c/], "array context ($class)");
+    is($scalar, "x", "scalar context ($class)");
+
+    {
+        local $@;
+        eval { $obj->void };
+        ok( !$@, "void context ($class)" );
+    }
+
+    if ( $obj->isa("AfterSub") ) {
+        is( $obj->x, 3, "methods were wrapped" );
+    }
+}
+
diff --git a/t/010_basics/009_import_unimport.t b/t/010_basics/009_import_unimport.t
new file mode 100755 (executable)
index 0000000..0ad5c3e
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 41;
+
+
+my @moose_exports = qw(
+    extends with
+    has
+    before after around
+    override
+    augment
+    super inner
+);
+
+{
+    package Foo;
+
+    eval 'use Mouse';
+    die $@ if $@;
+}
+
+can_ok('Foo', $_) for @moose_exports;
+
+{
+    package Foo;
+
+    eval 'no Mouse';
+    die $@ if $@;
+}
+
+ok(!Foo->can($_), '... Foo can no longer do ' . $_) for @moose_exports;
+
+# and check the type constraints as well
+
+my @moose_type_constraint_exports = qw(
+    type subtype as where message
+    coerce from via
+    enum
+    find_type_constraint
+);
+
+{
+    package Bar;
+
+    eval 'use Mouse::Util::TypeConstraints';
+    die $@ if $@;
+}
+
+can_ok('Bar', $_) for @moose_type_constraint_exports;
+
+{
+    package Bar;
+
+    eval 'no Mouse::Util::TypeConstraints';
+    die $@ if $@;
+}
+
+
+ok(!Bar->can($_), '... Bar can no longer do ' . $_) for @moose_type_constraint_exports;
+
+{
+    package Baz;
+
+    use Scalar::Util qw( blessed );
+    use Mouse;
+
+    no Mouse;
+}
+
+can_ok( 'Baz', 'blessed' );
diff --git a/t/010_basics/011_moose_respects_type_constraints.t b/t/010_basics/011_moose_respects_type_constraints.t
new file mode 100755 (executable)
index 0000000..f5193f0
--- /dev/null
@@ -0,0 +1,62 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+use Test::Exception;
+
+use Mouse::Util::TypeConstraints;
+
+=pod
+
+This tests demonstrates that Mouse will not override
+a preexisting type constraint of the same name when
+making constraints for a Mouse-class.
+
+It also tests that an attribute which uses a 'Foo' for
+it's isa option will get the subtype Foo, and not a
+type representing the Foo moose class.
+
+=cut
+
+BEGIN {
+    # create this subtype first (in BEGIN)
+    subtype Foo
+        => as 'Value'
+        => where { $_ eq 'Foo' };
+}
+
+{ # now seee if Mouse will override it
+    package Foo;
+    use Mouse;
+}
+
+my $foo_constraint = find_type_constraint('Foo');
+isa_ok($foo_constraint, 'Mouse::Meta::TypeConstraint');
+
+is($foo_constraint->parent->name, 'Value', '... got the Value subtype for Foo');
+
+ok($foo_constraint->check('Foo'), '... my constraint passed correctly');
+ok(!$foo_constraint->check('Bar'), '... my constraint failed correctly');
+
+{
+    package Bar;
+    use Mouse;
+
+    has 'foo' => (is => 'rw', isa => 'Foo');
+}
+
+my $bar = Bar->new;
+isa_ok($bar, 'Bar');
+
+lives_ok {
+    $bar->foo('Foo');
+} '... checked the type constraint correctly';
+
+dies_ok {
+    $bar->foo(Foo->new);
+} '... checked the type constraint correctly';
+
+
+
diff --git a/t/010_basics/013_create.t b/t/010_basics/013_create.t
new file mode 100755 (executable)
index 0000000..1d1d28f
--- /dev/null
@@ -0,0 +1,60 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 7;
+use Test::Exception;
+
+{
+    package Class;
+    use Mouse;
+
+    package Foo;
+    use Mouse::Role;
+    sub foo_role_applied { 1 }
+
+    package Conflicts::With::Foo;
+    use Mouse::Role;
+    sub foo_role_applied { 0 }
+
+    package Not::A::Role;
+    sub lol_wut { 42 }
+}
+
+my $new_class;
+
+lives_ok {
+    $new_class = Mouse::Meta::Class->create(
+        'Class::WithFoo',
+        superclasses => ['Class'],
+        roles        => ['Foo'],
+    );
+} 'creating lives';
+ok $new_class;
+
+my $with_foo = Class::WithFoo->new;
+
+ok $with_foo->foo_role_applied;
+isa_ok $with_foo, 'Class', '$with_foo';
+
+throws_ok {
+    Mouse::Meta::Class->create(
+        'Made::Of::Fail',
+        superclasses => ['Class'],
+        roles => 'Foo', # "oops"
+    );
+} qr/You must pass an ARRAY ref of roles/;
+
+ok !Mouse::Util::is_class_loaded('Made::Of::Fail'), "did not create Made::Of::Fail";
+
+dies_ok {
+    Mouse::Meta::Class->create(
+        'Continuing::To::Fail',
+        superclasses => ['Class'],
+        roles        => ['Foo', 'Conflicts::With::Foo'],
+    );
+} 'conflicting roles == death';
+
+# XXX: Continuing::To::Fail gets created anyway
+
diff --git a/t/010_basics/014_create_anon.t b/t/010_basics/014_create_anon.t
new file mode 100755 (executable)
index 0000000..6e681d0
--- /dev/null
@@ -0,0 +1,72 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 8;
+
+use Mouse::Meta::Class;
+
+{
+    package Class;
+    use Mouse;
+
+    package Foo;
+    use Mouse::Role;
+    sub foo_role_applied { 1 }
+
+    package Bar;
+    use Mouse::Role;
+    sub bar_role_applied { 1 }
+}
+
+# try without caching first
+
+{
+    my $class_and_foo_1 = Mouse::Meta::Class->create_anon_class(
+        superclasses => ['Class'],
+        roles        => ['Foo'],
+    );
+
+    my $class_and_foo_2 = Mouse::Meta::Class->create_anon_class(
+        superclasses => ['Class'],
+        roles        => ['Foo'],
+    );
+
+    isnt $class_and_foo_1->name, $class_and_foo_2->name,
+      'creating the same class twice without caching results in 2 classes';
+
+    map { ok $_->name->foo_role_applied } ($class_and_foo_1, $class_and_foo_2);
+}
+
+# now try with caching
+
+{
+    my $class_and_foo_1 = Mouse::Meta::Class->create_anon_class(
+        superclasses => ['Class'],
+        roles        => ['Foo'],
+        cache        => 1,
+    );
+
+    my $class_and_foo_2 = Mouse::Meta::Class->create_anon_class(
+        superclasses => ['Class'],
+        roles        => ['Foo'],
+        cache        => 1,
+    );
+
+    is $class_and_foo_1->name, $class_and_foo_2->name,
+      'with cache, the same class is the same class';
+
+    map { ok $_->name->foo_role_applied } ($class_and_foo_1, $class_and_foo_2);
+
+    my $class_and_bar = Mouse::Meta::Class->create_anon_class(
+        superclasses => ['Class'],
+        roles        => ['Bar'],
+        cache        => 1,
+    );
+
+    isnt $class_and_foo_1->name, $class_and_bar,
+      'class_and_foo and class_and_bar are different';
+
+    ok $class_and_bar->name->bar_role_applied;
+}
diff --git a/t/010_basics/015_buildargs.t b/t/010_basics/015_buildargs.t
new file mode 100755 (executable)
index 0000000..4b9b1f3
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 14;
+
+{
+    package Foo;
+    use Mouse;
+
+    has bar => ( is => "rw" );
+    has baz => ( is => "rw" );
+
+    sub BUILDARGS {
+        my ( $self, @args ) = @_;
+        unshift @args, "bar" if @args % 2 == 1;
+        return {@args};
+    }
+
+    package Bar;
+    use Mouse;
+
+    extends qw(Foo);
+}
+
+foreach my $class qw(Foo Bar) {
+    is( $class->new->bar, undef, "no args" );
+    is( $class->new( bar => 42 )->bar, 42, "normal args" );
+    is( $class->new( 37 )->bar, 37, "single arg" );
+    {
+        my $o = $class->new(bar => 42, baz => 47);
+        is($o->bar, 42, '... got the right bar');
+        is($o->baz, 47, '... got the right bar');
+    }
+    {
+        my $o = $class->new(42, baz => 47);
+        is($o->bar, 42, '... got the right bar');
+        is($o->baz, 47, '... got the right bar');
+    }
+}
+
+
diff --git a/t/010_basics/016_load_into_main.t b/t/010_basics/016_load_into_main.t
new file mode 100755 (executable)
index 0000000..58737b7
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+BEGIN {
+    eval "use Test::Output;";
+    plan skip_all => "Test::Output is required for this test" if $@;
+    plan tests => 2;
+}
+
+stderr_like( sub { package main; eval 'use Mouse' },
+             qr/\QMouse does not export its sugar to the 'main' package/,
+             'Mouse warns when loaded from the main package' );
+
+stderr_like( sub { package main; eval 'use Mouse::Role' },
+             qr/\QMouse::Role does not export its sugar to the 'main' package/,
+             'Mouse::Role warns when loaded from the main package' );
diff --git a/t/010_basics/017_error_handling.t b/t/010_basics/017_error_handling.t
new file mode 100755 (executable)
index 0000000..fee2964
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+use Test::Exception;
+
+# This tests the error handling in Mouse::Object only
+
+{
+    package Foo;
+    use Mouse;
+}
+
+throws_ok { Foo->new('bad') } qr/^\QSingle parameters to new() must be a HASH ref/,
+          'A single non-hashref arg to a constructor throws an error';
+throws_ok { Foo->new(undef) } qr/^\QSingle parameters to new() must be a HASH ref/,
+          'A single non-hashref arg to a constructor throws an error';
+
+throws_ok { Foo->does() } qr/^\QYou must supply a role name to does()/,
+          'Cannot call does() without a role name';
diff --git a/t/010_basics/019-destruction.t b/t/010_basics/019-destruction.t
new file mode 100755 (executable)
index 0000000..72cd82a
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+our @demolished;
+package Foo;
+use Mouse;
+
+sub DEMOLISH {
+    my $self = shift;
+    push @::demolished, __PACKAGE__;
+}
+
+package Foo::Sub;
+use Mouse;
+extends 'Foo';
+
+sub DEMOLISH {
+    my $self = shift;
+    push @::demolished, __PACKAGE__;
+}
+
+package Foo::Sub::Sub;
+use Mouse;
+extends 'Foo::Sub';
+
+sub DEMOLISH {
+    my $self = shift;
+    push @::demolished, __PACKAGE__;
+}
+
+package main;
+{
+    my $foo = Foo->new;
+}
+is_deeply(\@demolished, ['Foo'], "Foo demolished properly");
+@demolished = ();
+{
+    my $foo_sub = Foo::Sub->new;
+}
+is_deeply(\@demolished, ['Foo::Sub', 'Foo'], "Foo::Sub demolished properly");
+@demolished = ();
+{
+    my $foo_sub_sub = Foo::Sub::Sub->new;
+}
+is_deeply(\@demolished, ['Foo::Sub::Sub', 'Foo::Sub', 'Foo'],
+          "Foo::Sub::Sub demolished properly");
+@demolished = ();
diff --git a/t/010_basics/failing/010_method_modifier_with_regexp.t b/t/010_basics/failing/010_method_modifier_with_regexp.t
new file mode 100755 (executable)
index 0000000..786b8c3
--- /dev/null
@@ -0,0 +1,84 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+use Test::Exception;
+
+{
+
+    package Dog;
+    use Mouse;
+
+    sub bark_once {
+        my $self = shift;
+        return 'bark';
+    }
+
+    sub bark_twice {
+        return 'barkbark';
+    }
+
+    around qr/bark.*/ => sub {
+        'Dog::around(' . $_[0]->() . ')';
+    };
+
+}
+
+my $dog = Dog->new;
+is( $dog->bark_once,  'Dog::around(bark)', 'around modifier is called' );
+is( $dog->bark_twice, 'Dog::around(barkbark)', 'around modifier is called' );
+
+{
+
+    package Cat;
+    use Mouse;
+    our $BEFORE_BARK_COUNTER = 0;
+    our $AFTER_BARK_COUNTER  = 0;
+
+    sub bark_once {
+        my $self = shift;
+        return 'bark';
+    }
+
+    sub bark_twice {
+        return 'barkbark';
+    }
+
+    before qr/bark.*/ => sub {
+        $BEFORE_BARK_COUNTER++;
+    };
+
+    after qr/bark.*/ => sub {
+        $AFTER_BARK_COUNTER++;
+    };
+
+}
+
+my $cat = Cat->new;
+$cat->bark_once;
+is( $Cat::BEFORE_BARK_COUNTER, 1, 'before modifier is called once' );
+is( $Cat::AFTER_BARK_COUNTER,  1, 'after modifier is called once' );
+$cat->bark_twice;
+is( $Cat::BEFORE_BARK_COUNTER, 2, 'before modifier is called twice' );
+is( $Cat::AFTER_BARK_COUNTER,  2, 'after modifier is called twice' );
+
+{
+    package Dog::Role;
+    use Mouse::Role;
+
+    ::dies_ok {
+        before qr/bark.*/ => sub {};
+    } '... this is not currently supported';
+
+    ::dies_ok {
+        around qr/bark.*/ => sub {};
+    } '... this is not currently supported';
+
+    ::dies_ok {
+        after  qr/bark.*/ => sub {};
+    } '... this is not currently supported';
+
+}
+
diff --git a/t/010_basics/failing/012_rebless.t b/t/010_basics/failing/012_rebless.t
new file mode 100755 (executable)
index 0000000..e8c6722
--- /dev/null
@@ -0,0 +1,85 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+use Test::Exception;
+use Scalar::Util 'blessed';
+
+use Mouse::Util::TypeConstraints;
+
+subtype 'Positive'
+     => as 'Num'
+     => where { $_ > 0 };
+
+{
+    package Parent;
+    use Mouse;
+
+    has name => (
+        is       => 'rw',
+        isa      => 'Str',
+    );
+
+    has lazy_classname => (
+        is      => 'ro',
+        lazy    => 1,
+        default => sub { "Parent" },
+    );
+
+    has type_constrained => (
+        is      => 'rw',
+        isa     => 'Num',
+        default => 5.5,
+    );
+
+    package Child;
+    use Mouse;
+    extends 'Parent';
+
+    has '+name' => (
+        default => 'Junior',
+    );
+
+    has '+lazy_classname' => (
+        default => sub { "Child" },
+    );
+
+    has '+type_constrained' => (
+        isa     => 'Int',
+        default => 100,
+    );
+}
+
+my $foo = Parent->new;
+my $bar = Parent->new;
+
+is(blessed($foo), 'Parent', 'Parent->new gives a Parent object');
+is($foo->name, undef, 'No name yet');
+is($foo->lazy_classname, 'Parent', "lazy attribute initialized");
+lives_ok { $foo->type_constrained(10.5) } "Num type constraint for now..";
+
+# try to rebless, except it will fail due to Child's stricter type constraint
+throws_ok { Child->meta->rebless_instance($foo) }
+qr/^Attribute \(type_constrained\) does not pass the type constraint because\: Validation failed for 'Int' failed with value 10\.5/,
+'... this failed cause of type check';
+throws_ok { Child->meta->rebless_instance($bar) }
+qr/^Attribute \(type_constrained\) does not pass the type constraint because\: Validation failed for 'Int' failed with value 5\.5/,
+'... this failed cause of type check';;
+
+$foo->type_constrained(10);
+$bar->type_constrained(5);
+
+Child->meta->rebless_instance($foo);
+Child->meta->rebless_instance($bar);
+
+is(blessed($foo), 'Child', 'successfully reblessed into Child');
+is($foo->name, 'Junior', "Child->name's default came through");
+
+is($foo->lazy_classname, 'Parent', "lazy attribute was already initialized");
+is($bar->lazy_classname, 'Child', "lazy attribute just now initialized");
+
+throws_ok { $foo->type_constrained(10.5) }
+qr/^Attribute \(type_constrained\) does not pass the type constraint because\: Validation failed for 'Int' failed with value 10\.5/,
+'... this failed cause of type check';
diff --git a/t/010_basics/failing/018_methods.t b/t/010_basics/failing/018_methods.t
new file mode 100755 (executable)
index 0000000..bb683bc
--- /dev/null
@@ -0,0 +1,44 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 6;
+
+
+my $test1 = Mouse::Meta::Class->create_anon_class;
+$test1->add_method( 'foo1', sub { } );
+
+my $t1    = $test1->new_object;
+my $t1_am = $t1->meta->get_method('foo1')->associated_metaclass;
+
+ok( $t1_am, 'associated_metaclass is defined' );
+
+isa_ok(
+    $t1_am, 'Mouse::Meta::Class',
+    'associated_metaclass is correct class'
+);
+
+like( $t1_am->name(), qr/::__ANON__::/,
+    'associated_metaclass->name looks like an anonymous class' );
+
+{
+    package Test2;
+
+    use Mouse;
+
+    sub foo2 { }
+}
+
+my $t2    = Test2->new;
+my $t2_am = $t2->meta->get_method('foo2')->associated_metaclass;
+
+ok( $t2_am, 'associated_metaclass is defined' );
+
+isa_ok(
+    $t2_am, 'Mouse::Meta::Class',
+    'associated_metaclass is correct class'
+);
+
+is( $t2_am->name(), 'Test2',
+    'associated_metaclass->name is Test2' );
diff --git a/t/010_basics/failing/020-global-destruction-helper.pl b/t/010_basics/failing/020-global-destruction-helper.pl
new file mode 100755 (executable)
index 0000000..a0defbe
--- /dev/null
@@ -0,0 +1,34 @@
+#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+\r
+{\r
+    package Foo;\r
+    use Mouse;\r
+\r
+    sub DEMOLISH {\r
+        my $self = shift;\r
+        my ($igd) = @_;\r
+\r
+        print $igd;\r
+    }\r
+}\r
+\r
+{\r
+    package Bar;\r
+    use Mouse;\r
+\r
+    sub DEMOLISH {\r
+        my $self = shift;\r
+        my ($igd) = @_;\r
+\r
+        print $igd;\r
+    }\r
+\r
+    __PACKAGE__->meta->make_immutable;\r
+}\r
+\r
+our $foo = Foo->new;\r
+our $bar = Bar->new;\r
diff --git a/t/010_basics/failing/020-global-destruction.t b/t/010_basics/failing/020-global-destruction.t
new file mode 100755 (executable)
index 0000000..484a722
--- /dev/null
@@ -0,0 +1,50 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+
+{
+    package Foo;
+    use Mouse;
+
+    sub DEMOLISH {
+        my $self = shift;
+        my ($igd) = @_;
+        ::ok(
+            !$igd,
+            'in_global_destruction state is passed to DEMOLISH properly (false)'
+        );
+    }
+}
+
+{
+    my $foo = Foo->new;
+}
+
+{
+    package Bar;
+    use Mouse;
+
+    sub DEMOLISH {
+        my $self = shift;
+        my ($igd) = @_;
+        ::ok(
+            !$igd,
+            'in_global_destruction state is passed to DEMOLISH properly (false)'
+        );
+    }
+
+    __PACKAGE__->meta->make_immutable;
+}
+
+{
+    my $bar = Bar->new;
+}
+
+ok(
+    $_,
+    'in_global_destruction state is passed to DEMOLISH properly (true)'
+) for split //, `$^X t/010_basics/020-global-destruction-helper.pl`;
+
diff --git a/t/010_basics/failing/021-instance-new.t b/t/010_basics/failing/021-instance-new.t
new file mode 100755 (executable)
index 0000000..1c7d84d
--- /dev/null
@@ -0,0 +1,25 @@
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Exception;
+BEGIN {
+    eval "use Test::Output;";
+    plan skip_all => "Test::Output is required for this test" if $@;
+    plan tests => 2;
+}
+
+{
+    package Foo;
+    use Mouse;
+}
+
+{
+    my $foo = Foo->new();
+    stderr_like { $foo->new() }
+    qr/\QCalling new() on an instance is deprecated/,
+        '$object->new() is deprecated';
+
+    Foo->meta->make_immutable, redo
+        if Foo->meta->is_mutable;
+}
diff --git a/t/020_attributes/002_attribute_writer_generation.t b/t/020_attributes/002_attribute_writer_generation.t
new file mode 100644 (file)
index 0000000..0c49739
--- /dev/null
@@ -0,0 +1,121 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 29;
+use Test::Exception;
+
+use Scalar::Util 'isweak';
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    eval {
+        has 'foo' => (
+            reader => 'get_foo',
+            writer => 'set_foo',
+        );
+    };
+    ::ok(!$@, '... created the writer method okay');
+
+    eval {
+        has 'foo_required' => (
+            reader   => 'get_foo_required',
+            writer   => 'set_foo_required',
+            required => 1,
+        );
+    };
+    ::ok(!$@, '... created the required writer method okay');
+
+    eval {
+        has 'foo_int' => (
+            reader => 'get_foo_int',
+            writer => 'set_foo_int',
+            isa    => 'Int',
+        );
+    };
+    ::ok(!$@, '... created the writer method with type constraint okay');
+
+    eval {
+        has 'foo_weak' => (
+            reader   => 'get_foo_weak',
+            writer   => 'set_foo_weak',
+            weak_ref => 1
+        );
+    };
+    ::ok(!$@, '... created the writer method with weak_ref okay');
+}
+
+{
+    my $foo = Foo->new(foo_required => 'required');
+    isa_ok($foo, 'Foo');
+
+    # regular writer
+
+    can_ok($foo, 'set_foo');
+    is($foo->get_foo(), undef, '... got an unset value');
+    lives_ok {
+        $foo->set_foo(100);
+    } '... set_foo wrote successfully';
+    is($foo->get_foo(), 100, '... got the correct set value');
+
+    ok(!isweak($foo->{foo}), '... it is not a weak reference');
+
+    # required writer
+
+    dies_ok {
+        Foo->new;
+    } '... cannot create without the required attribute';
+
+    can_ok($foo, 'set_foo_required');
+    is($foo->get_foo_required(), 'required', '... got an unset value');
+    lives_ok {
+        $foo->set_foo_required(100);
+    } '... set_foo_required wrote successfully';
+    is($foo->get_foo_required(), 100, '... got the correct set value');
+
+    dies_ok {
+        $foo->set_foo_required();
+    } '... set_foo_required died successfully with no value';
+
+    lives_ok {
+        $foo->set_foo_required(undef);
+    } '... set_foo_required did accept undef';
+
+    ok(!isweak($foo->{foo_required}), '... it is not a weak reference');
+
+    # with type constraint
+
+    can_ok($foo, 'set_foo_int');
+    is($foo->get_foo_int(), undef, '... got an unset value');
+    lives_ok {
+        $foo->set_foo_int(100);
+    } '... set_foo_int wrote successfully';
+    is($foo->get_foo_int(), 100, '... got the correct set value');
+
+    dies_ok {
+        $foo->set_foo_int("Foo");
+    } '... set_foo_int died successfully';
+
+    ok(!isweak($foo->{foo_int}), '... it is not a weak reference');
+
+    # with weak_ref
+
+    my $test = [];
+
+    can_ok($foo, 'set_foo_weak');
+    is($foo->get_foo_weak(), undef, '... got an unset value');
+    lives_ok {
+        $foo->set_foo_weak($test);
+    } '... set_foo_weak wrote successfully';
+    is($foo->get_foo_weak(), $test, '... got the correct set value');
+
+    ok(isweak($foo->{foo_weak}), '... it is a weak reference');
+}
+
+
+
diff --git a/t/020_attributes/003_attribute_accessor_generation.t b/t/020_attributes/003_attribute_accessor_generation.t
new file mode 100644 (file)
index 0000000..4b8620b
--- /dev/null
@@ -0,0 +1,208 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 57;
+use Test::Exception;
+
+use Scalar::Util 'isweak';
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    eval {
+        has 'foo' => (
+            accessor => 'foo',
+        );
+    };
+    ::ok(!$@, '... created the accessor method okay');
+
+    eval {
+        has 'lazy_foo' => (
+            accessor => 'lazy_foo',
+            lazy     => 1,
+            default  => sub { 10 }
+        );
+    };
+    ::ok(!$@, '... created the lazy accessor method okay');
+
+
+    eval {
+        has 'foo_required' => (
+            accessor => 'foo_required',
+            required => 1,
+        );
+    };
+    ::ok(!$@, '... created the required accessor method okay');
+
+    eval {
+        has 'foo_int' => (
+            accessor => 'foo_int',
+            isa      => 'Int',
+        );
+    };
+    ::ok(!$@, '... created the accessor method with type constraint okay');
+
+    eval {
+        has 'foo_weak' => (
+            accessor => 'foo_weak',
+            weak_ref => 1
+        );
+    };
+    ::ok(!$@, '... created the accessor method with weak_ref okay');
+
+    eval {
+        has 'foo_deref' => (
+            accessor => 'foo_deref',
+            isa => 'ArrayRef',
+            auto_deref => 1,
+        );
+    };
+    ::ok(!$@, '... created the accessor method with auto_deref okay');
+
+    eval {
+        has 'foo_deref_ro' => (
+            reader => 'foo_deref_ro',
+            isa => 'ArrayRef',
+            auto_deref => 1,
+        );
+    };
+    ::ok(!$@, '... created the reader method with auto_deref okay');
+
+    eval {
+        has 'foo_deref_hash' => (
+            accessor => 'foo_deref_hash',
+            isa => 'HashRef',
+            auto_deref => 1,
+        );
+    };
+    ::ok(!$@, '... created the reader method with auto_deref okay');
+}
+
+{
+    my $foo = Foo->new(foo_required => 'required');
+    isa_ok($foo, 'Foo');
+
+    # regular accessor
+
+    can_ok($foo, 'foo');
+    is($foo->foo(), undef, '... got an unset value');
+    lives_ok {
+        $foo->foo(100);
+    } '... foo wrote successfully';
+    is($foo->foo(), 100, '... got the correct set value');
+
+    ok(!isweak($foo->{foo}), '... it is not a weak reference');
+
+    # required writer
+
+    dies_ok {
+        Foo->new;
+    } '... cannot create without the required attribute';
+
+    can_ok($foo, 'foo_required');
+    is($foo->foo_required(), 'required', '... got an unset value');
+    lives_ok {
+        $foo->foo_required(100);
+    } '... foo_required wrote successfully';
+    is($foo->foo_required(), 100, '... got the correct set value');
+
+    lives_ok {
+        $foo->foo_required(undef);
+    } '... foo_required did not die with undef';
+
+    is($foo->foo_required, undef, "value is undef");
+
+    ok(!isweak($foo->{foo_required}), '... it is not a weak reference');
+
+    # lazy
+
+    ok(!exists($foo->{lazy_foo}), '... no value in lazy_foo slot');
+
+    can_ok($foo, 'lazy_foo');
+    is($foo->lazy_foo(), 10, '... got an deferred value');
+
+    # with type constraint
+
+    can_ok($foo, 'foo_int');
+    is($foo->foo_int(), undef, '... got an unset value');
+    lives_ok {
+        $foo->foo_int(100);
+    } '... foo_int wrote successfully';
+    is($foo->foo_int(), 100, '... got the correct set value');
+
+    dies_ok {
+        $foo->foo_int("Foo");
+    } '... foo_int died successfully';
+
+    ok(!isweak($foo->{foo_int}), '... it is not a weak reference');
+
+    # with weak_ref
+
+    my $test = [];
+
+    can_ok($foo, 'foo_weak');
+    is($foo->foo_weak(), undef, '... got an unset value');
+    lives_ok {
+        $foo->foo_weak($test);
+    } '... foo_weak wrote successfully';
+    is($foo->foo_weak(), $test, '... got the correct set value');
+
+    ok(isweak($foo->{foo_weak}), '... it is a weak reference');
+
+    can_ok( $foo, 'foo_deref');
+    is_deeply( [$foo->foo_deref()], [], '... default default value');
+    my @list;
+    lives_ok {
+        @list = $foo->foo_deref();
+    } "... doesn't deref undef value";
+    is_deeply( \@list, [], "returns empty list in list context");
+
+    lives_ok {
+        $foo->foo_deref( [ qw/foo bar gorch/ ] );
+    } '... foo_deref wrote successfully';
+
+    is( Scalar::Util::reftype( scalar $foo->foo_deref() ), "ARRAY", "returns an array reference in scalar context" );
+    is_deeply( scalar($foo->foo_deref()), [ qw/foo bar gorch/ ], "correct array" );
+
+    is( scalar( () = $foo->foo_deref() ), 3, "returns list in list context" );
+    is_deeply( [ $foo->foo_deref() ], [ qw/foo bar gorch/ ], "correct list" );
+
+
+    can_ok( $foo, 'foo_deref' );
+    is_deeply( [$foo->foo_deref_ro()], [], "... default default value" );
+
+    dies_ok {
+        $foo->foo_deref_ro( [] );
+    } "... read only";
+
+    $foo->{foo_deref_ro} = [qw/la la la/];
+
+    is_deeply( scalar($foo->foo_deref_ro()), [qw/la la la/], "scalar context ro" );
+    is_deeply( [ $foo->foo_deref_ro() ], [qw/la la la/], "list context ro" );
+
+    can_ok( $foo, 'foo_deref_hash' );
+    is_deeply( { $foo->foo_deref_hash() }, {}, "... default default value" );
+
+    my %hash;
+    lives_ok {
+        %hash = $foo->foo_deref_hash();
+    } "... doesn't deref undef value";
+    is_deeply( \%hash, {}, "returns empty list in list context");
+
+    lives_ok {
+        $foo->foo_deref_hash( { foo => 1, bar => 2 } );
+    } '... foo_deref_hash wrote successfully';
+
+    is_deeply( scalar($foo->foo_deref_hash), { foo => 1, bar => 2 }, "scalar context" );
+
+    %hash = $foo->foo_deref_hash;
+    is_deeply( \%hash, { foo => 1, bar => 2 }, "list context");
+}
+
+
+
diff --git a/t/020_attributes/005_attribute_does.t b/t/020_attributes/005_attribute_does.t
new file mode 100644 (file)
index 0000000..a895bdb
--- /dev/null
@@ -0,0 +1,110 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 9;
+use Test::Exception;
+
+
+
+{
+    package Foo::Role;
+    use Mouse::Role;
+    use Mouse::Util::TypeConstraints;
+
+    # if does() exists on its own, then
+    # we create a type constraint for
+    # it, just as we do for isa()
+    has 'bar' => (is => 'rw', does => 'Bar::Role');
+    has 'baz' => (
+        is   => 'rw',
+        does => 'Bar::Role'
+    );
+
+    package Bar::Role;
+    use Mouse::Role;
+
+    # if isa and does appear together, then see if Class->does(Role)
+    # if it does work... then the does() check is actually not needed
+    # since the isa() check will imply the does() check
+    has 'foo' => (is => 'rw', isa => 'Foo::Class', does => 'Foo::Role');
+
+    package Foo::Class;
+    use Mouse;
+
+    with 'Foo::Role';
+
+    package Bar::Class;
+    use Mouse;
+
+    with 'Bar::Role';
+
+}
+
+my $foo = Foo::Class->new;
+isa_ok($foo, 'Foo::Class');
+
+my $bar = Bar::Class->new;
+isa_ok($bar, 'Bar::Class');
+
+lives_ok {
+    $foo->bar($bar);
+} '... bar passed the type constraint okay';
+
+dies_ok {
+    $foo->bar($foo);
+} '... foo did not pass the type constraint okay';
+
+lives_ok {
+    $foo->baz($bar);
+} '... baz passed the type constraint okay';
+
+dies_ok {
+    $foo->baz($foo);
+} '... foo did not pass the type constraint okay';
+
+lives_ok {
+    $bar->foo($foo);
+} '... foo passed the type constraint okay';
+
+
+
+# some error conditions
+
+{
+    package Baz::Class;
+    use Test::More;
+    use Mouse;
+
+    local $TODO = 'setting both isa and does';
+
+    # if isa and does appear together, then see if Class->does(Role)
+    # if it does not,.. we have a conflict... so we die loudly
+    ::dies_ok {
+        has 'foo' => (isa => 'Foo::Class', does => 'Bar::Class');
+    } '... cannot have a does() which is not done by the isa()';
+}
+
+{
+    package Bling;
+    use strict;
+    use warnings;
+
+    sub bling { 'Bling::bling' }
+
+    package Bling::Bling;
+    use Test::More;
+    use Mouse;
+
+    local $TODO = 'setting both isa and does';
+
+    # if isa and does appear together, then see if Class->does(Role)
+    # if it does not,.. we have a conflict... so we die loudly
+    ::dies_ok {
+        has 'foo' => (isa => 'Bling', does => 'Bar::Class');
+    } '... cannot have a isa() which is cannot does()';
+}
+
+
+
diff --git a/t/020_attributes/006_attribute_required.t b/t/020_attributes/006_attribute_required.t
new file mode 100644 (file)
index 0000000..ba61a74
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (is => 'ro', required => 1);
+    has 'baz' => (is => 'rw',  default => 100, required => 1);
+    has 'boo' => (is => 'rw', lazy => 1, default => 50, required => 1);
+}
+
+{
+    my $foo = Foo->new(bar => 10, baz => 20, boo => 100);
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 10, '... got the right bar');
+    is($foo->baz, 20, '... got the right baz');
+    is($foo->boo, 100, '... got the right boo');
+}
+
+{
+    my $foo = Foo->new(bar => 10, boo => 5);
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 10, '... got the right bar');
+    is($foo->baz, 100, '... got the right baz');
+    is($foo->boo, 5, '... got the right boo');
+}
+
+{
+    my $foo = Foo->new(bar => 10);
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, 10, '... got the right bar');
+    is($foo->baz, 100, '... got the right baz');
+    is($foo->boo, 50, '... got the right boo');
+}
+
+#Yeah.. this doesn't work like this anymore, see below. (groditi)
+#throws_ok {
+#    Foo->new(bar => 10, baz => undef);
+#} qr/^Attribute \(baz\) is required and cannot be undef/, '... must supply all the required attribute';
+
+#throws_ok {
+#    Foo->new(bar => 10, boo => undef);
+#} qr/^Attribute \(boo\) is required and cannot be undef/, '... must supply all the required attribute';
+
+lives_ok {
+    Foo->new(bar => 10, baz => undef);
+} '... undef is a valid attribute value';
+
+lives_ok {
+    Foo->new(bar => 10, boo => undef);
+}  '... undef is a valid attribute value';
+
+
+throws_ok {
+    Foo->new;
+} qr/^Attribute \(bar\) is required/, '... must supply all the required attribute';
+
diff --git a/t/020_attributes/007_attribute_custom_metaclass.t b/t/020_attributes/007_attribute_custom_metaclass.t
new file mode 100644 (file)
index 0000000..9e702d7
--- /dev/null
@@ -0,0 +1,96 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 16;
+use Test::Exception;
+
+
+
+{
+    package Foo::Meta::Attribute;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+
+    around 'new' => sub {
+        my $next = shift;
+        my $self = shift;
+        my $name = shift;
+        $next->($self, $name, (is => 'rw', isa => 'Foo'), @_);
+    };
+
+    package Foo;
+    use Mouse;
+
+    has 'foo' => (metaclass => 'Foo::Meta::Attribute');
+}
+{
+    my $foo = Foo->new;
+    isa_ok($foo, 'Foo');
+
+    my $foo_attr = Foo->meta->get_attribute('foo');
+    isa_ok($foo_attr, 'Foo::Meta::Attribute');
+    isa_ok($foo_attr, 'Mouse::Meta::Attribute');
+
+    is($foo_attr->name, 'foo', '... got the right name for our meta-attribute');
+    ok($foo_attr->has_accessor, '... our meta-attrubute created the accessor for us');
+
+    ok($foo_attr->has_type_constraint, '... our meta-attrubute created the type_constraint for us');
+
+    my $foo_attr_type_constraint = $foo_attr->type_constraint;
+    isa_ok($foo_attr_type_constraint, 'Mouse::Meta::TypeConstraint');
+
+    is($foo_attr_type_constraint->name, 'Foo', '... got the right type constraint name');
+
+    local $TODO = '$type_constraint->parent is not reliable';
+    is($foo_attr_type_constraint->parent, 'Object', '... got the right type constraint parent name');
+}
+{
+    package Bar::Meta::Attribute;
+    use Mouse;
+
+    #extends 'Class::MOP::Attribute';
+    extends 'Foo::Meta::Attribute';
+
+    package Bar;
+    use Mouse;
+
+    ::lives_ok {
+        has 'bar' => (metaclass => 'Bar::Meta::Attribute');
+    } '... the attribute metaclass need not be a Mouse::Meta::Attribute as long as it behaves';
+}
+
+{
+    package Mouse::Meta::Attribute::Custom::Foo;
+    sub register_implementation { 'Foo::Meta::Attribute' }
+
+    package Mouse::Meta::Attribute::Custom::Bar;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+
+    package Another::Foo;
+    use Mouse;
+
+    ::lives_ok {
+        has 'foo' => (metaclass => 'Foo');
+    } '... the attribute metaclass alias worked correctly';
+
+    ::lives_ok {
+        has 'bar' => (metaclass => 'Bar', is => 'bare');
+    } '... the attribute metaclass alias worked correctly';
+}
+
+{
+    my $foo_attr = Another::Foo->meta->get_attribute('foo');
+    isa_ok($foo_attr, 'Foo::Meta::Attribute');
+    isa_ok($foo_attr, 'Mouse::Meta::Attribute');
+
+    my $bar_attr = Another::Foo->meta->get_attribute('bar');
+    isa_ok($bar_attr, 'Mouse::Meta::Attribute::Custom::Bar');
+    isa_ok($bar_attr, 'Mouse::Meta::Attribute');
+}
+
+
diff --git a/t/020_attributes/008_attribute_type_unions.t b/t/020_attributes/008_attribute_type_unions.t
new file mode 100644 (file)
index 0000000..b1227a5
--- /dev/null
@@ -0,0 +1,99 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 18;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (is => 'rw', isa => 'ArrayRef | HashRef');
+}
+
+my $foo = Foo->new;
+isa_ok($foo, 'Foo');
+
+lives_ok {
+    $foo->bar([])
+} '... set bar successfully with an ARRAY ref';
+
+lives_ok {
+    $foo->bar({})
+} '... set bar successfully with a HASH ref';
+
+dies_ok {
+    $foo->bar(100)
+} '... couldnt set bar successfully with a number';
+
+dies_ok {
+    $foo->bar(sub {})
+} '... couldnt set bar successfully with a CODE ref';
+
+# check the constructor
+
+lives_ok {
+    Foo->new(bar => [])
+} '... created new Foo with bar successfully set with an ARRAY ref';
+
+lives_ok {
+    Foo->new(bar => {})
+} '... created new Foo with bar successfully set with a HASH ref';
+
+dies_ok {
+    Foo->new(bar => 50)
+} '... didnt create a new Foo with bar as a number';
+
+dies_ok {
+    Foo->new(bar => sub {})
+} '... didnt create a new Foo with bar as a CODE ref';
+
+{
+    package Bar;
+    use Mouse;
+
+    has 'baz' => (is => 'rw', isa => 'Str | CodeRef');
+}
+
+my $bar = Bar->new;
+isa_ok($bar, 'Bar');
+
+lives_ok {
+    $bar->baz('a string')
+} '... set baz successfully with a string';
+
+lives_ok {
+    $bar->baz(sub { 'a sub' })
+} '... set baz successfully with a CODE ref';
+
+dies_ok {
+    $bar->baz(\(my $var1))
+} '... couldnt set baz successfully with a SCALAR ref';
+
+dies_ok {
+    $bar->baz({})
+} '... couldnt set bar successfully with a HASH ref';
+
+# check the constructor
+
+lives_ok {
+    Bar->new(baz => 'a string')
+} '... created new Bar with baz successfully set with a string';
+
+lives_ok {
+    Bar->new(baz => sub { 'a sub' })
+} '... created new Bar with baz successfully set with a CODE ref';
+
+dies_ok {
+    Bar->new(baz => \(my $var2))
+} '... didnt create a new Bar with baz as a number';
+
+dies_ok {
+    Bar->new(baz => {})
+} '... didnt create a new Bar with baz as a HASH ref';
+
+
diff --git a/t/020_attributes/011_more_attr_delegation.t b/t/020_attributes/011_more_attr_delegation.t
new file mode 100644 (file)
index 0000000..15f8e6a
--- /dev/null
@@ -0,0 +1,224 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 39;
+use Test::Exception;
+
+=pod
+
+This tests the more complex
+delegation cases and that they
+do not fail at compile time.
+
+=cut
+
+{
+
+    package ChildASuper;
+    use Mouse;
+
+    sub child_a_super_method { "as" }
+
+    package ChildA;
+    use Mouse;
+
+    extends "ChildASuper";
+
+    sub child_a_method_1 { "a1" }
+    sub child_a_method_2 { Scalar::Util::blessed($_[0]) . " a2" }
+
+    package ChildASub;
+    use Mouse;
+
+    extends "ChildA";
+
+    sub child_a_method_3 { "a3" }
+
+    package ChildB;
+    use Mouse;
+
+    sub child_b_method_1 { "b1" }
+    sub child_b_method_2 { "b2" }
+    sub child_b_method_3 { "b3" }
+
+    package ChildC;
+    use Mouse;
+
+    sub child_c_method_1 { "c1" }
+    sub child_c_method_2 { "c2" }
+    sub child_c_method_3_la { "c3" }
+    sub child_c_method_4_la { "c4" }
+
+    package ChildD;
+    use Mouse;
+
+    sub child_d_method_1 { "d1" }
+    sub child_d_method_2 { "d2" }
+
+    package ChildE;
+    # no Mouse
+
+    sub new { bless {}, shift }
+    sub child_e_method_1 { "e1" }
+    sub child_e_method_2 { "e2" }
+
+    package ChildF;
+    # no Mouse
+
+    sub new { bless {}, shift }
+    sub child_f_method_1 { "f1" }
+    sub child_f_method_2 { "f2" }
+
+    package ChildG;
+    use Mouse;
+
+    sub child_g_method_1 { "g1" }
+
+    package Parent;
+    use Mouse;
+
+    ::dies_ok {
+        has child_a => (
+            is      => "ro",
+            default => sub { ChildA->new },
+            handles => qr/.*/,
+        );
+    } "all_methods requires explicit isa";
+
+    ::lives_ok {
+        has child_a => (
+            isa     => "ChildA",
+            is      => "ro",
+            default => sub { ChildA->new },
+            handles => qr/.*/,
+        );
+    } "allow all_methods with explicit isa";
+
+    ::lives_ok {
+        has child_b => (
+            is      => 'ro',
+            default => sub { ChildB->new },
+            handles => [qw/child_b_method_1/],
+        );
+    } "don't need to declare isa if method list is predefined";
+
+    ::lives_ok {
+        has child_c => (
+            isa     => "ChildC",
+            is      => "ro",
+            default => sub { ChildC->new },
+            handles => qr/_la$/,
+        );
+    } "can declare regex collector";
+
+    ::dies_ok {
+        has child_d => (
+            is      => "ro",
+            default => sub { ChildD->new },
+            handles => sub {
+                my ( $class, $delegate_class ) = @_;
+            }
+        );
+    } "can't create attr with generative handles parameter and no isa";
+
+    our $TODO;
+{
+    local $TODO = 'handles => CODE is not supported';
+    ::lives_ok {
+        has child_d => (
+            isa     => "ChildD",
+            is      => "ro",
+            default => sub { ChildD->new },
+            handles => sub {
+                my ( $class, $delegate_class ) = @_;
+                return;
+            }
+        );
+    } "can't create attr with generative handles parameter and no isa";
+}
+
+    ::lives_ok {
+        has child_e => (
+            isa     => "ChildE",
+            is      => "ro",
+            default => sub { ChildE->new },
+            handles => ["child_e_method_2"],
+        );
+    } "can delegate to non moose class using explicit method list";
+
+{
+    local $TODO = 'handles => CODE is not supported';
+    my $delegate_class;
+    ::lives_ok {
+        has child_f => (
+            isa     => "ChildF",
+            is      => "ro",
+            default => sub { ChildF->new },
+            handles => sub {
+                $delegate_class = $_[1]->name;
+                return;
+            },
+        );
+    } "subrefs on non moose class give no meta";
+
+    ::is( $delegate_class, "ChildF", "plain classes are handed down to subs" );
+}
+
+    ::lives_ok {
+        has child_g => (
+            isa     => "ChildG",
+            default => sub { ChildG->new },
+            handles => ["child_g_method_1"],
+        );
+    } "can delegate to object even without explicit reader";
+
+    sub parent_method { "p" }
+}
+
+# sanity
+
+isa_ok( my $p = Parent->new, "Parent" );
+isa_ok( $p->child_a, "ChildA" );
+isa_ok( $p->child_b, "ChildB" );
+isa_ok( $p->child_c, "ChildC" );
+isa_ok( $p->child_d, "ChildD" );
+isa_ok( $p->child_e, "ChildE" );
+isa_ok( $p->child_f, "ChildF" );
+
+ok(!$p->can('child_g'), '... no child_g accessor defined');
+
+
+is( $p->parent_method, "p", "parent method" );
+is( $p->child_a->child_a_super_method, "as", "child supermethod" );
+is( $p->child_a->child_a_method_1, "a1", "child method" );
+
+can_ok( $p, "child_a_super_method" );
+can_ok( $p, "child_a_method_1" );
+can_ok( $p, "child_a_method_2" );
+ok( !$p->can( "child_a_method_3" ), "but not subclass of delegate class" );
+
+is( $p->child_a_method_1, $p->child_a->child_a_method_1, "delegate behaves the same" );
+is( $p->child_a_method_2, "ChildA a2", "delegates are their own invocants" );
+
+
+can_ok( $p, "child_b_method_1" );
+ok( !$p->can("child_b_method_2"), "but not ChildB's unspecified siblings" );
+
+
+ok( !$p->can($_), "none of ChildD's methods ($_)" )
+    for grep { /^child/ } map { $_->name } ChildD->meta->get_all_methods();
+
+can_ok( $p, "child_c_method_3_la" );
+can_ok( $p, "child_c_method_4_la" );
+
+is( $p->child_c_method_3_la, "c3", "ChildC method delegated OK" );
+
+can_ok( $p, "child_e_method_2" );
+ok( !$p->can("child_e_method_1"), "but not child_e_method_1");
+
+is( $p->child_e_method_2, "e2", "delegate to non moose class (child_e_method_2)" );
+
+can_ok( $p, "child_g_method_1" );
+is( $p->child_g_method_1, "g1", "delegate to moose class without reader (child_g_method_1)" );
diff --git a/t/020_attributes/012_misc_attribute_tests.t b/t/020_attributes/012_misc_attribute_tests.t
new file mode 100644 (file)
index 0000000..ac46d5a
--- /dev/null
@@ -0,0 +1,279 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 43;
+use Test::Exception;
+
+use lib 't/lib';
+use Test::Mouse;
+
+{
+    {
+        package Test::Attribute::Inline::Documentation;
+        use Mouse;
+
+        has 'foo' => (
+            documentation => q{
+                The 'foo' attribute is my favorite
+                attribute in the whole wide world.
+            },
+            is => 'bare',
+        );
+    }
+
+    my $foo_attr = Test::Attribute::Inline::Documentation->meta->get_attribute('foo');
+
+    ok($foo_attr->has_documentation, '... the foo has docs');
+    is($foo_attr->documentation,
+            q{
+                The 'foo' attribute is my favorite
+                attribute in the whole wide world.
+            },
+    '... got the foo docs');
+}
+
+{
+    {
+        package Test::For::Lazy::TypeConstraint;
+        use Mouse;
+        use Mouse::Util::TypeConstraints;
+
+        has 'bad_lazy_attr' => (
+            is => 'rw',
+            isa => 'ArrayRef',
+            lazy => 1,
+            default => sub { "test" },
+        );
+
+        has 'good_lazy_attr' => (
+            is => 'rw',
+            isa => 'ArrayRef',
+            lazy => 1,
+            default => sub { [] },
+        );
+
+    }
+
+    my $test = Test::For::Lazy::TypeConstraint->new;
+    isa_ok($test, 'Test::For::Lazy::TypeConstraint');
+
+    dies_ok {
+        $test->bad_lazy_attr;
+    } '... this does not work';
+
+    lives_ok {
+        $test->good_lazy_attr;
+    } '... this does work';
+}
+
+{
+    {
+        package Test::Arrayref::Attributes;
+        use Mouse;
+
+        has [qw(foo bar baz)] => (
+            is => 'rw',
+        );
+
+    }
+
+    my $test = Test::Arrayref::Attributes->new;
+    isa_ok($test, 'Test::Arrayref::Attributes');
+    can_ok($test, qw(foo bar baz));
+
+}
+
+{
+    {
+        package Test::Arrayref::RoleAttributes::Role;
+        use Mouse::Role;
+
+        has [qw(foo bar baz)] => (
+            is => 'rw',
+        );
+
+    }
+    {
+        package Test::Arrayref::RoleAttributes;
+        use Mouse;
+        with 'Test::Arrayref::RoleAttributes::Role';
+    }
+
+    my $test = Test::Arrayref::RoleAttributes->new;
+    isa_ok($test, 'Test::Arrayref::RoleAttributes');
+    can_ok($test, qw(foo bar baz));
+
+}
+
+{
+    {
+        package Test::UndefDefault::Attributes;
+        use Mouse;
+
+        has 'foo' => (
+            is      => 'ro',
+            isa     => 'Str',
+            default => sub { return }
+        );
+
+    }
+
+    dies_ok {
+        Test::UndefDefault::Attributes->new;
+    } '... default must return a value which passes the type constraint';
+
+}
+
+{
+    {
+        package OverloadedStr;
+        use Mouse;
+        use overload '""' => sub { 'this is *not* a string' };
+
+        has 'a_str' => ( isa => 'Str' , is => 'rw' );
+    }
+
+    my $moose_obj = OverloadedStr->new;
+
+    is($moose_obj->a_str( 'foobar' ), 'foobar', 'setter took string');
+    ok($moose_obj, 'this is a *not* a string');
+
+    throws_ok {
+        $moose_obj->a_str( $moose_obj )
+    } qr/Attribute \(a_str\) does not pass the type constraint because\: Validation failed for 'Str' failed with value OverloadedStr=HASH\(0x.+?\)/,
+    '... dies without overloading the string';
+
+}
+
+{
+    {
+        package OverloadBreaker;
+        use Mouse;
+
+        has 'a_num' => ( isa => 'Int' , is => 'rw', default => 7.5 );
+    }
+
+    throws_ok {
+        OverloadBreaker->new;
+    } qr/Attribute \(a_num\) does not pass the type constraint because\: Validation failed for 'Int' failed with value 7\.5/,
+    '... this doesnt trip overload to break anymore ';
+
+    lives_ok {
+        OverloadBreaker->new(a_num => 5);
+    } '... this works fine though';
+
+}
+
+{
+    {
+      package Test::Builder::Attribute;
+        use Mouse;
+
+        has 'foo'  => ( required => 1, builder => 'build_foo', is => 'ro');
+        sub build_foo { return "works" };
+    }
+
+    my $meta = Test::Builder::Attribute->meta;
+    my $foo_attr  = $meta->get_attribute("foo");
+
+    ok($foo_attr->is_required, "foo is required");
+    ok($foo_attr->has_builder, "foo has builder");
+    is($foo_attr->builder, "build_foo",  ".. and it's named build_foo");
+
+    my $instance = Test::Builder::Attribute->new;
+    is($instance->foo, 'works', "foo builder works");
+}
+
+{
+    {
+        package Test::Builder::Attribute::Broken;
+        use Mouse;
+
+        has 'foo'  => ( required => 1, builder => 'build_foo', is => 'ro');
+    }
+
+    dies_ok {
+        Test::Builder::Attribute::Broken->new;
+    } '... no builder, wtf';
+}
+
+
+{
+    {
+      package Test::LazyBuild::Attribute;
+        use Mouse;
+
+        has 'foo'  => ( lazy_build => 1, is => 'ro');
+        has '_foo' => ( lazy_build => 1, is => 'ro');
+        has 'fool' => ( lazy_build => 1, is => 'ro');
+        sub _build_foo { return "works" };
+        sub _build__foo { return "works too" };
+    }
+
+    my $meta = Test::LazyBuild::Attribute->meta;
+    my $foo_attr  = $meta->get_attribute("foo");
+    my $_foo_attr = $meta->get_attribute("_foo");
+
+    ok($foo_attr->is_lazy, "foo is lazy");
+    ok($foo_attr->is_lazy_build, "foo is lazy_build");
+
+    ok($foo_attr->has_clearer, "foo has clearer");
+    is($foo_attr->clearer, "clear_foo",  ".. and it's named clear_foo");
+
+    ok($foo_attr->has_builder, "foo has builder");
+    is($foo_attr->builder, "_build_foo",  ".. and it's named build_foo");
+
+    ok($foo_attr->has_predicate, "foo has predicate");
+    is($foo_attr->predicate, "has_foo",  ".. and it's named has_foo");
+
+    ok($_foo_attr->is_lazy, "_foo is lazy");
+    ok(!$_foo_attr->is_required, "lazy_build attributes are no longer automatically required");
+    ok($_foo_attr->is_lazy_build, "_foo is lazy_build");
+
+    ok($_foo_attr->has_clearer, "_foo has clearer");
+    is($_foo_attr->clearer, "_clear_foo",  ".. and it's named _clear_foo");
+
+    ok($_foo_attr->has_builder, "_foo has builder");
+    is($_foo_attr->builder, "_build__foo",  ".. and it's named _build_foo");
+
+    ok($_foo_attr->has_predicate, "_foo has predicate");
+    is($_foo_attr->predicate, "_has_foo",  ".. and it's named _has_foo");
+
+    my $instance = Test::LazyBuild::Attribute->new;
+    ok(!$instance->has_foo, "noo foo value yet");
+    ok(!$instance->_has_foo, "noo _foo value yet");
+    is($instance->foo, 'works', "foo builder works");
+    is($instance->_foo, 'works too', "foo builder works too");
+    dies_ok { $instance->fool }
+#    throws_ok { $instance->fool }
+#        qr/Test::LazyBuild::Attribute does not support builder method \'_build_fool\' for attribute \'fool\'/,
+            "Correct error when a builder method is not present";
+
+}
+
+{
+    package OutOfClassTest;
+
+    use Mouse;
+}
+
+# Mouse::Exporter does not support 'with_meta'
+#lives_ok { OutOfClassTest::has('foo', is => 'bare'); } 'create attr via direct sub call';
+#lives_ok { OutOfClassTest->can('has')->('bar', is => 'bare'); } 'create attr via can';
+
+#ok(OutOfClassTest->meta->get_attribute('foo'), 'attr created from sub call');
+#ok(OutOfClassTest->meta->get_attribute('bar'), 'attr created from can');
+
+
+{
+    {
+        package Foo;
+        use Mouse;
+
+        ::throws_ok { has 'foo' => ( 'ro', isa => 'Str' ) }
+            qr/^Usage/, 'has throws error with odd number of attribute options';
+    }
+
+}
diff --git a/t/020_attributes/013_attr_dereference_test.t b/t/020_attributes/013_attr_dereference_test.t
new file mode 100644 (file)
index 0000000..7389df8
--- /dev/null
@@ -0,0 +1,81 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+use Test::Exception;
+
+
+
+{
+    package Customer;
+    use Mouse;
+
+    package Firm;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    ::lives_ok {
+        has 'customers' => (
+            is         => 'ro',
+            isa        => subtype('ArrayRef' => where {
+                            (blessed($_) && $_->isa('Customer') || return) for @$_; 1 }),
+            auto_deref => 1,
+        );
+    } '... successfully created attr';
+}
+
+{
+    my $customer = Customer->new;
+    isa_ok($customer, 'Customer');
+
+    my $firm = Firm->new(customers => [ $customer ]);
+    isa_ok($firm, 'Firm');
+
+    can_ok($firm, 'customers');
+
+    is_deeply(
+        [ $firm->customers ],
+        [ $customer ],
+        '... got the right dereferenced value'
+    );
+}
+
+{
+    my $firm = Firm->new();
+    isa_ok($firm, 'Firm');
+
+    can_ok($firm, 'customers');
+
+    is_deeply(
+        [ $firm->customers ],
+        [],
+        '... got the right dereferenced value'
+    );
+}
+
+{
+    package AutoDeref;
+    use Mouse;
+
+    has 'bar' => (
+        is         => 'rw',
+        isa        => 'ArrayRef[Int]',
+        auto_deref => 1,
+    );
+}
+
+{
+    my $autoderef = AutoDeref->new;
+
+    dies_ok {
+        $autoderef->bar(1, 2, 3);
+    } '... its auto-de-ref-ing, not auto-en-ref-ing';
+
+    lives_ok  {
+        $autoderef->bar([ 1, 2, 3 ])
+    } '... set the results of bar correctly';
+
+    is_deeply [ $autoderef->bar ], [ 1, 2, 3 ], '... auto-dereffed correctly';
+}
diff --git a/t/020_attributes/014_misc_attribute_coerce_lazy.t b/t/020_attributes/014_misc_attribute_coerce_lazy.t
new file mode 100644 (file)
index 0000000..ccd8883
--- /dev/null
@@ -0,0 +1,51 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+use Test::Exception;
+
+
+
+{
+    package HTTPHeader;
+    use Mouse;
+
+    has 'array' => (is => 'ro');
+    has 'hash'  => (is => 'ro');
+}
+
+{
+    package Request;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    subtype Header =>
+        => as Object
+        => where { $_->isa('HTTPHeader') };
+
+    coerce Header
+        => from ArrayRef
+            => via { HTTPHeader->new(array => $_[0]) }
+        => from HashRef
+            => via { HTTPHeader->new(hash => $_[0]) };
+
+    has 'headers'  => (
+           is      => 'rw',
+           isa     => 'Header',
+           coerce  => 1,
+           lazy    => 1,
+           default => sub { [ 'content-type', 'text/html' ] }
+    );
+}
+
+my $r = Request->new;
+isa_ok($r, 'Request');
+
+lives_ok {
+    $r->headers;
+} '... this coerces and passes the type constraint even with lazy';
+
+
+
index aaa6ece..eea984e 100644 (file)
@@ -1,16 +1,14 @@
 #!/usr/bin/perl
-use lib 't/lib';
 
 use strict;
 use warnings;
 
-use Test::More tests => 12;
+use lib 't/lib';
 
+use Test::More tests => 12;
 use Test::Exception;
 use Test::Mouse;
 
-
-
 {
     package My::Attribute::Trait;
     use Mouse::Role;
@@ -21,11 +19,9 @@ use Test::Mouse;
 
     after 'install_accessors' => sub {
         my $self = shift;
-        my $reader = $self->get_read_method;
-
         $self->associated_class->add_method(
             $self->alias_to,
-            sub { shift->$reader(@_) },
+            $self->get_read_method_ref
         );
     };
 }
@@ -58,7 +54,6 @@ can_ok($c, 'baz');
 is($c->baz, 100, '... got the right value for baz');
 
 my $bar_attr = $c->meta->get_attribute('bar');
-
 does_ok($bar_attr, 'My::Attribute::Trait');
 ok($bar_attr->has_applied_traits, '... got the applied traits');
 is_deeply($bar_attr->applied_traits, [qw/My::Attribute::Trait/], '... got the applied traits');
diff --git a/t/020_attributes/016_attribute_traits_registered.t b/t/020_attributes/016_attribute_traits_registered.t
new file mode 100755 (executable)
index 0000000..bb216f9
--- /dev/null
@@ -0,0 +1,125 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More tests => 23;
+use Test::Exception;
+use Test::Mouse;
+
+
+
+{
+    package My::Attribute::Trait;
+    use Mouse::Role;
+
+    has 'alias_to' => (is => 'ro', isa => 'Str');
+
+    has foo => ( is => "ro", default => "blah" );
+
+    after 'install_accessors' => sub {
+        my $self = shift;
+        $self->associated_class->add_method(
+            $self->alias_to,
+            $self->get_read_method_ref
+        );
+    };
+
+    package Mouse::Meta::Attribute::Custom::Trait::Aliased;
+    sub register_implementation { 'My::Attribute::Trait' }
+}
+
+{
+    package My::Other::Attribute::Trait;
+    use Mouse::Role;
+
+    my $method = sub {
+        42;
+    };
+
+    has the_other_attr => ( isa => "Str", is => "rw", default => "oink" );
+
+    after 'install_accessors' => sub {
+        my $self = shift;
+        $self->associated_class->add_method(
+            'additional_method',
+            $method
+        );
+    };
+
+    package Mouse::Meta::Attribute::Custom::Trait::Other;
+    sub register_implementation { 'My::Other::Attribute::Trait' }
+}
+
+{
+    package My::Class;
+    use Mouse;
+
+    has 'bar' => (
+        traits   => [qw/Aliased/],
+        is       => 'ro',
+        isa      => 'Int',
+        alias_to => 'baz',
+    );
+}
+
+{
+    package My::Derived::Class;
+    use Mouse;
+
+    extends 'My::Class';
+
+    has '+bar' => (
+        traits   => [qw/Other/],
+    );
+}
+
+my $c = My::Class->new(bar => 100);
+isa_ok($c, 'My::Class');
+
+is($c->bar, 100, '... got the right value for bar');
+
+can_ok($c, 'baz') and
+is($c->baz, 100, '... got the right value for baz');
+
+my $bar_attr = $c->meta->get_attribute('bar');
+does_ok($bar_attr, 'My::Attribute::Trait');
+is($bar_attr->foo, "blah", "attr initialized");
+
+ok(!$bar_attr->meta->does_role('Aliased'), "does_role ignores aliases for sanity");
+{
+local $TODO = 'aliased name is not supported';
+ok($bar_attr->does('Aliased'), "attr->does uses aliases");
+}
+ok(!$bar_attr->meta->does_role('Fictional'), "does_role returns false for nonexistent roles");
+ok(!$bar_attr->does('Fictional'), "attr->does returns false for nonexistent roles");
+
+my $quux = My::Derived::Class->new(bar => 1000);
+
+is($quux->bar, 1000, '... got the right value for bar');
+
+can_ok($quux, 'baz');
+is($quux->baz, 1000, '... got the right value for baz');
+
+my $derived_bar_attr = $quux->meta->get_attribute("bar");
+does_ok($derived_bar_attr, 'My::Attribute::Trait' );
+
+is( $derived_bar_attr->foo, "blah", "attr initialized" );
+
+does_ok($derived_bar_attr, 'My::Other::Attribute::Trait' );
+
+is($derived_bar_attr->the_other_attr, "oink", "attr initialized" );
+
+ok(!$derived_bar_attr->meta->does_role('Aliased'), "does_role ignores aliases for sanity");
+{
+local $TODO = 'aliased name is not supported';
+ok($derived_bar_attr->does('Aliased'), "attr->does uses aliases");
+}
+ok(!$derived_bar_attr->meta->does_role('Fictional'), "does_role returns false for nonexistent roles");
+ok(!$derived_bar_attr->does('Fictional'), "attr->does returns false for nonexistent roles");
+
+can_ok($quux, 'additional_method');
+is(eval { $quux->additional_method }, 42, '... got the right value for additional_method');
+
diff --git a/t/020_attributes/017_attribute_traits_n_meta.t b/t/020_attributes/017_attribute_traits_n_meta.t
new file mode 100755 (executable)
index 0000000..4d96e6c
--- /dev/null
@@ -0,0 +1,70 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib 't/lib';
+
+use Test::More tests => 7;
+use Test::Exception;
+use Test::Mouse;
+
+
+
+{
+    package My::Meta::Attribute::DefaultReadOnly;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+
+    around 'new' => sub {
+        my $next = shift;
+        my ($self, $name, %options) = @_;
+        $options{is} = 'ro'
+            unless exists $options{is};
+        $next->($self, $name, %options);
+    };
+}
+
+{
+    package My::Attribute::Trait;
+    use Mouse::Role;
+
+    has 'alias_to' => (is => 'ro', isa => 'Str');
+
+    after 'install_accessors' => sub {
+        my $self = shift;
+        $self->associated_class->add_method(
+            $self->alias_to,
+            $self->get_read_method_ref
+        );
+    };
+}
+
+{
+    package My::Class;
+    use Mouse;
+
+    has 'bar' => (
+        metaclass => 'My::Meta::Attribute::DefaultReadOnly',
+        traits    => [qw/My::Attribute::Trait/],
+        isa       => 'Int',
+        alias_to  => 'baz',
+    );
+}
+
+my $c = My::Class->new(bar => 100);
+isa_ok($c, 'My::Class');
+
+is($c->bar, 100, '... got the right value for bar');
+
+can_ok($c, 'baz');
+is($c->baz, 100, '... got the right value for baz');
+
+isa_ok($c->meta->get_attribute('bar'), 'My::Meta::Attribute::DefaultReadOnly');
+does_ok($c->meta->get_attribute('bar'), 'My::Attribute::Trait');
+is($c->meta->get_attribute('bar')->_is_metadata, 'ro', '... got the right metaclass customization');
+
+
+
+
diff --git a/t/020_attributes/018_no_init_arg.t b/t/020_attributes/018_no_init_arg.t
new file mode 100644 (file)
index 0000000..40b53cc
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    eval {
+        has 'foo' => (
+            is => "rw",
+            init_arg => undef,
+        );
+    };
+    ::ok(!$@, '... created the attr okay');
+}
+
+{
+    my $foo = Foo->new( foo => "bar" );
+    isa_ok($foo, 'Foo');
+
+    is( $foo->foo, undef, "field is not set via init arg" );
+
+    $foo->foo("blah");
+
+    is( $foo->foo, "blah", "field is set via setter" );
+}
diff --git a/t/020_attributes/020_trigger_and_coerce.t b/t/020_attributes/020_trigger_and_coerce.t
new file mode 100644 (file)
index 0000000..38d3e91
--- /dev/null
@@ -0,0 +1,56 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 11;
+use Test::Exception;
+
+
+
+{
+
+    package Fake::DateTime;
+    use Mouse;
+
+    has 'string_repr' => ( is => 'ro' );
+
+    package Mortgage;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    coerce 'Fake::DateTime' => from 'Str' =>
+        via { Fake::DateTime->new( string_repr => $_ ) };
+
+    has 'closing_date' => (
+        is      => 'rw',
+        isa     => 'Fake::DateTime',
+        coerce  => 1,
+        trigger => sub {
+            my ( $self, $val ) = @_;
+            ::pass('... trigger is being called');
+            ::isa_ok( $self->closing_date, 'Fake::DateTime' );
+            ::isa_ok( $val,                'Fake::DateTime' );
+        }
+    );
+}
+
+{
+    my $mtg = Mortgage->new( closing_date => 'yesterday' );
+    isa_ok( $mtg, 'Mortgage' );
+
+    # check that coercion worked
+    isa_ok( $mtg->closing_date, 'Fake::DateTime' );
+}
+
+Mortgage->meta->make_immutable;
+ok( Mortgage->meta->is_immutable, '... Mortgage is now immutable' );
+
+{
+    my $mtg = Mortgage->new( closing_date => 'yesterday' );
+    isa_ok( $mtg, 'Mortgage' );
+
+    # check that coercion worked
+    isa_ok( $mtg->closing_date, 'Fake::DateTime' );
+}
+
diff --git a/t/020_attributes/024_attribute_traits_parameterized.t b/t/020_attributes/024_attribute_traits_parameterized.t
new file mode 100644 (file)
index 0000000..57a3d05
--- /dev/null
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More tests => 5;
+
+{
+    package My::Attribute::Trait;
+    use Mouse::Role;
+
+    sub reversed_name {
+        my $self = shift;
+        scalar reverse $self->name;
+    }
+}
+
+{
+    package My::Class;
+    use Mouse;
+
+    has foo => (
+        traits => [
+            'My::Attribute::Trait' => {
+                -alias => {
+                    reversed_name => 'eman',
+                },
+            },
+        ],
+        is => 'bare',
+    );
+}
+
+{
+    package My::Other::Class;
+    use Mouse;
+
+    has foo => (
+        traits => [
+            'My::Attribute::Trait' => {
+                -alias => {
+                    reversed_name => 'reversed',
+                },
+                -excludes => 'reversed_name',
+            },
+        ],
+        is => 'bare',
+    );
+}
+
+my $attr = My::Class->meta->get_attribute('foo');
+is($attr->eman, 'oof', 'the aliased method is in the attribute');
+ok(!$attr->can('reversed'), "the method was not installed under the other class' alias");
+
+my $other_attr = My::Other::Class->meta->get_attribute('foo');
+is($other_attr->reversed, 'oof', 'the aliased method is in the attribute');
+ok(!$other_attr->can('enam'), "the method was not installed under the other class' alias");
+ok(!$other_attr->can('reversed_name'), "the method was not installed under the original name when that was excluded");
+
diff --git a/t/020_attributes/025_chained_coercion.t b/t/020_attributes/025_chained_coercion.t
new file mode 100644 (file)
index 0000000..894d6ea
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 4;
+use Test::Exception;
+
+{
+    package Baz;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    coerce 'Baz' => from 'HashRef' => via { Baz->new($_) };
+
+    has 'hello' => (
+        is      => 'ro',
+        isa     => 'Str',
+    );
+
+    package Bar;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    coerce 'Bar' => from 'HashRef' => via { Bar->new($_) };
+
+    has 'baz' => (
+        is      => 'ro',
+        isa     => 'Baz',
+        coerce  => 1
+    );
+
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (
+        is      => 'ro',
+        isa     => 'Bar',
+        coerce  => 1,
+    );
+}
+
+my $foo = Foo->new(bar => { baz => { hello => 'World' } });
+isa_ok($foo, 'Foo');
+isa_ok($foo->bar, 'Bar');
+isa_ok($foo->bar->baz, 'Baz');
+is($foo->bar->baz->hello, 'World', '... this all worked fine');
+
+
diff --git a/t/020_attributes/026_attribute_without_any_methods.t b/t/020_attributes/026_attribute_without_any_methods.t
new file mode 100644 (file)
index 0000000..0acf3c0
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+BEGIN{ $ENV{MOUSE_VERBOSE} = 1 }
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+use Mouse ();
+use Mouse::Meta::Class;
+
+my $meta = Mouse::Meta::Class->create('Banana');
+
+my $warn;
+$SIG{__WARN__} = sub { $warn = "@_" };
+
+$meta->add_attribute('foo');
+like $warn, qr/Attribute \(foo\) of class Banana has no associated methods/,
+  'correct error message';
+
+$warn = '';
+$meta->add_attribute('bar', is => 'bare');
+is $warn, '', 'add attribute with no methods and is => "bare"';
diff --git a/t/020_attributes/029_accessor_context.t b/t/020_attributes/029_accessor_context.t
new file mode 100644 (file)
index 0000000..b959f31
--- /dev/null
@@ -0,0 +1,68 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 14;
+use Test::Exception;
+
+lives_ok {
+    package My::Class;
+    use Mouse;
+
+    has s_rw => (
+        is => 'rw',
+    );
+
+    has s_ro => (
+        is => 'ro',
+    );
+
+    has a_rw => (
+        is  => 'rw',
+        isa => 'ArrayRef',
+
+        auto_deref => 1,
+    );
+
+    has a_ro => (
+        is  => 'ro',
+        isa => 'ArrayRef',
+
+        auto_deref => 1,
+    );
+
+    has h_rw => (
+        is  => 'rw',
+        isa => 'HashRef',
+
+        auto_deref => 1,
+    );
+
+    has h_ro => (
+        is  => 'ro',
+        isa => 'HashRef',
+
+        auto_deref => 1,
+    );
+} 'class definition';
+
+lives_ok {
+    my $o = My::Class->new();
+
+    is_deeply [scalar $o->s_rw], [undef], 'uninitialized scalar attribute/rw in scalar context';
+    is_deeply [$o->s_rw],        [undef], 'uninitialized scalar attribute/rw in list context';
+    is_deeply [scalar $o->s_ro], [undef], 'uninitialized scalar attribute/ro in scalar context';
+    is_deeply [$o->s_ro],        [undef], 'uninitialized scalar attribute/ro in list context';
+
+
+    is_deeply [scalar $o->a_rw], [undef], 'uninitialized ArrayRef attribute/rw in scalar context';
+    is_deeply [$o->a_rw],        [],      'uninitialized ArrayRef attribute/rw in list context';
+    is_deeply [scalar $o->a_ro], [undef], 'uninitialized ArrayRef attribute/ro in scalar context';
+    is_deeply [$o->a_ro],        [],      'uninitialized ArrayRef attribute/ro in list context';
+
+    is_deeply [scalar $o->h_rw], [undef], 'uninitialized HashRef attribute/rw in scalar context';
+    is_deeply [$o->h_rw],        [],      'uninitialized HashRef attribute/rw in list context';
+    is_deeply [scalar $o->h_ro], [undef], 'uninitialized HashRef attribute/ro in scalar context';
+    is_deeply [$o->h_ro],        [],      'uninitialized HashRef attribute/ro in list context';
+
+} 'testing';
diff --git a/t/020_attributes/030_non_alpha_attr_names.t b/t/020_attributes/030_non_alpha_attr_names.t
new file mode 100644 (file)
index 0000000..81105a8
--- /dev/null
@@ -0,0 +1,41 @@
+use strict;
+use warnings;
+
+use Test::More tests => 12;
+
+{
+    package Foo;
+    use Mouse;
+    has 'type' => (
+        required => 0,
+        reader   => 'get_type',
+        default  => 1,
+    );
+
+    has '@type' => (
+        required => 0,
+        reader   => 'get_at_type',
+        default  => 2,
+    );
+
+    has 'has spaces' => (
+        required => 0,
+        reader   => 'get_hs',
+        default  => 42,
+    );
+
+    no Mouse;
+}
+
+{
+    my $foo = Foo->new;
+
+    ok( Foo->meta->has_attribute($_), "Foo has '$_' attribute" )
+        for 'type', '@type', 'has spaces';
+
+    is( $foo->get_type,    1,  q{'type' attribute default is 1} );
+    is( $foo->get_at_type, 2,  q{'@type' attribute default is 1} );
+    is( $foo->get_hs,      42, q{'has spaces' attribute default is 42} );
+
+    Foo->meta->make_immutable, redo if Foo->meta->is_mutable;
+}
diff --git a/t/020_attributes/failing/001_attribute_reader_generation.t b/t/020_attributes/failing/001_attribute_reader_generation.t
new file mode 100644 (file)
index 0000000..6e2f233
--- /dev/null
@@ -0,0 +1,87 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 20;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    eval {
+        has 'foo' => (
+            reader => 'get_foo'
+        );
+    };
+    ::ok(!$@, '... created the reader method okay');
+
+    eval {
+        has 'lazy_foo' => (
+            reader => 'get_lazy_foo',
+            lazy => 1,
+            default => sub { 10 }
+        );
+    };
+    ::ok(!$@, '... created the lazy reader method okay') or warn $@;
+
+    my $warn;
+
+    eval {
+        local $SIG{__WARN__} = sub { $warn = $_[0] };
+        has 'mtfnpy' => (
+            reder => 'get_mftnpy'
+        );
+    };
+    ::ok($warn, '... got a warning for mispelled attribute argument');
+}
+
+{
+    my $foo = Foo->new;
+    isa_ok($foo, 'Foo');
+
+    can_ok($foo, 'get_foo');
+    is($foo->get_foo(), undef, '... got an undefined value');
+    dies_ok {
+        $foo->get_foo(100);
+    } '... get_foo is a read-only';
+
+    ok(!exists($foo->{lazy_foo}), '... no value in get_lazy_foo slot');
+
+    can_ok($foo, 'get_lazy_foo');
+    is($foo->get_lazy_foo(), 10, '... got an deferred value');
+    dies_ok {
+        $foo->get_lazy_foo(100);
+    } '... get_lazy_foo is a read-only';
+}
+
+{
+    my $foo = Foo->new;
+    isa_ok($foo, 'Foo');
+
+    my $attr = $foo->meta->find_attribute_by_name("lazy_foo");
+
+    isa_ok( $attr, "Mouse::Meta::Attribute" );
+
+    ok( $attr->is_lazy, "it's lazy" );
+
+    is( $attr->get_raw_value($foo), undef, "raw value" );
+
+    is( $attr->get_value($foo), 10, "lazy value" );
+
+    is( $attr->get_raw_value($foo), 10, "raw value" );
+}
+
+{
+    my $foo = Foo->new(foo => 10, lazy_foo => 100);
+    isa_ok($foo, 'Foo');
+
+    is($foo->get_foo(), 10, '... got the correct value');
+    is($foo->get_lazy_foo(), 100, '... got the correct value');
+}
+
+
+
diff --git a/t/020_attributes/failing/004_attribute_triggers.t b/t/020_attributes/failing/004_attribute_triggers.t
new file mode 100644 (file)
index 0000000..d7dd0e6
--- /dev/null
@@ -0,0 +1,222 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Scalar::Util 'isweak';
+
+use Test::More tests => 43;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (is      => 'rw',
+                  isa     => 'Maybe[Bar]',
+                  trigger => sub {
+                      my ($self, $bar) = @_;
+                      $bar->foo($self) if defined $bar;
+                  });
+
+    has 'baz' => (writer => 'set_baz',
+                  reader => 'get_baz',
+                  isa    => 'Baz',
+                  trigger => sub {
+                      my ($self, $baz) = @_;
+                      $baz->foo($self);
+                  });
+
+
+    package Bar;
+    use Mouse;
+
+    has 'foo' => (is => 'rw', isa => 'Foo', weak_ref => 1);
+
+    package Baz;
+    use Mouse;
+
+    has 'foo' => (is => 'rw', isa => 'Foo', weak_ref => 1);
+}
+
+{
+    my $foo = Foo->new;
+    isa_ok($foo, 'Foo');
+
+    my $bar = Bar->new;
+    isa_ok($bar, 'Bar');
+
+    my $baz = Baz->new;
+    isa_ok($baz, 'Baz');
+
+    lives_ok {
+        $foo->bar($bar);
+    } '... did not die setting bar';
+
+    is($foo->bar, $bar, '... set the value foo.bar correctly');
+    is($bar->foo, $foo, '... which in turn set the value bar.foo correctly');
+
+    ok(isweak($bar->{foo}), '... bar.foo is a weak reference');
+
+    lives_ok {
+        $foo->bar(undef);
+    } '... did not die un-setting bar';
+
+    is($foo->bar, undef, '... set the value foo.bar correctly');
+    is($bar->foo, $foo, '... which in turn set the value bar.foo correctly');
+
+    # test the writer
+
+    lives_ok {
+        $foo->set_baz($baz);
+    } '... did not die setting baz';
+
+    is($foo->get_baz, $baz, '... set the value foo.baz correctly');
+    is($baz->foo, $foo, '... which in turn set the value baz.foo correctly');
+
+    ok(isweak($baz->{foo}), '... baz.foo is a weak reference');
+}
+
+{
+    my $bar = Bar->new;
+    isa_ok($bar, 'Bar');
+
+    my $baz = Baz->new;
+    isa_ok($baz, 'Baz');
+
+    my $foo = Foo->new(bar => $bar, baz => $baz);
+    isa_ok($foo, 'Foo');
+
+    is($foo->bar, $bar, '... set the value foo.bar correctly');
+    is($bar->foo, $foo, '... which in turn set the value bar.foo correctly');
+
+    ok(isweak($bar->{foo}), '... bar.foo is a weak reference');
+
+    is($foo->get_baz, $baz, '... set the value foo.baz correctly');
+    is($baz->foo, $foo, '... which in turn set the value baz.foo correctly');
+
+    ok(isweak($baz->{foo}), '... baz.foo is a weak reference');
+}
+
+# some errors
+
+{
+    package Bling;
+    use Mouse;
+
+    ::dies_ok {
+        has('bling' => (is => 'rw', trigger => 'Fail'));
+    } '... a trigger must be a CODE ref';
+
+    ::dies_ok {
+        has('bling' => (is => 'rw', trigger => []));
+    } '... a trigger must be a CODE ref';
+}
+
+# Triggers do not fire on built values
+
+{
+    package Blarg;
+    use Mouse;
+
+    our %trigger_calls;
+    our %trigger_vals;
+    has foo => (is => 'rw', default => sub { 'default foo value' },
+                trigger => sub { my ($self, $val, $attr) = @_;
+                                 $trigger_calls{foo}++;
+                                 $trigger_vals{foo} = $val });
+    has bar => (is => 'rw', lazy_build => 1,
+                trigger => sub { my ($self, $val, $attr) = @_;
+                                 $trigger_calls{bar}++;
+                                 $trigger_vals{bar} = $val });
+    sub _build_bar { return 'default bar value' }
+    has baz => (is => 'rw', builder => '_build_baz',
+                trigger => sub { my ($self, $val, $attr) = @_;
+                                 $trigger_calls{baz}++;
+                                 $trigger_vals{baz} = $val });
+    sub _build_baz { return 'default baz value' }
+}
+
+{
+    my $blarg;
+    lives_ok { $blarg = Blarg->new; } 'Blarg->new() lives';
+    ok($blarg, 'Have a $blarg');
+    foreach my $attr (qw/foo bar baz/) {
+        is($blarg->$attr(), "default $attr value", "$attr has default value");
+    }
+    is_deeply(\%Blarg::trigger_calls, {}, 'No triggers fired');
+    foreach my $attr (qw/foo bar baz/) {
+        $blarg->$attr("Different $attr value");
+    }
+    is_deeply(\%Blarg::trigger_calls, { map { $_ => 1 } qw/foo bar baz/ }, 'All triggers fired once on assign');
+    is_deeply(\%Blarg::trigger_vals, { map { $_ => "Different $_ value" } qw/foo bar baz/ }, 'All triggers given assigned values');
+
+    lives_ok { $blarg => Blarg->new( map { $_ => "Yet another $_ value" } qw/foo bar baz/ ) } '->new() with parameters';
+    is_deeply(\%Blarg::trigger_calls, { map { $_ => 2 } qw/foo bar baz/ }, 'All triggers fired once on construct');
+    is_deeply(\%Blarg::trigger_vals, { map { $_ => "Yet another $_ value" } qw/foo bar baz/ }, 'All triggers given assigned values');
+}
+
+# Triggers do not receive the meta-attribute as an argument, but do
+# receive the old value
+
+{
+    package Foo;
+    use Mouse;
+    our @calls;
+    has foo => (is => 'rw', trigger => sub { push @calls, [@_] });
+}
+
+{
+    my $attr = Foo->meta->get_attribute('foo');
+
+    my $foo = Foo->new;
+    $attr->set_value( $foo, 2 );
+
+    is_deeply(
+        \@Foo::calls,
+        [ [ $foo, 2 ] ],
+        'trigger called correctly on initial set via meta-API',
+    );
+    @Foo::calls = ();
+
+    $attr->set_value( $foo, 3 );
+
+    is_deeply(
+        \@Foo::calls,
+        [ [ $foo, 3, 2 ] ],
+        'trigger called correctly on second set via meta-API',
+    );
+    @Foo::calls = ();
+
+    $attr->set_raw_value( $foo, 4 );
+
+    is_deeply(
+        \@Foo::calls,
+        [ ],
+        'trigger not called using set_raw_value method',
+    );
+    @Foo::calls = ();
+}
+
+{
+    my $foo = Foo->new(foo => 2);
+    is_deeply(
+        \@Foo::calls,
+        [ [ $foo, 2 ] ],
+        'trigger called correctly on construction',
+    );
+    @Foo::calls = ();
+
+    $foo->foo(3);
+    is_deeply(
+        \@Foo::calls,
+        [ [ $foo, 3, 2 ] ],
+        'trigger called correctly on set (with old value)',
+    );
+    @Foo::calls = ();
+    Foo->meta->make_immutable, redo if Foo->meta->is_mutable;
+}
+
+
diff --git a/t/020_attributes/failing/009_attribute_inherited_slot_specs.t b/t/020_attributes/failing/009_attribute_inherited_slot_specs.t
new file mode 100644 (file)
index 0000000..058331a
--- /dev/null
@@ -0,0 +1,270 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 84;
+use Test::Exception;
+
+
+
+{
+    package Thing;
+    use Mouse;
+
+    sub hello   { 'Hello World (from Thing)' }
+    sub goodbye { 'Goodbye World (from Thing)' }
+
+    package Foo;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    subtype 'FooStr'
+        => as 'Str'
+        => where { /Foo/ };
+
+    coerce 'FooStr'
+        => from ArrayRef
+            => via { 'FooArrayRef' };
+
+    has 'bar' => (is => 'ro', isa => 'Str', default => 'Foo::bar');
+    has 'baz' => (is => 'rw', isa => 'Ref');
+    has 'foo' => (is => 'rw', isa => 'FooStr');
+
+    has 'gorch' => (is => 'ro');
+    has 'gloum' => (is => 'ro', default => sub {[]});
+    has 'fleem' => (is => 'ro');
+
+    has 'bling' => (is => 'ro', isa => 'Thing');
+    has 'blang' => (is => 'ro', isa => 'Thing', handles => ['goodbye']);
+
+    has 'bunch_of_stuff' => (is => 'rw', isa => 'ArrayRef');
+
+    has 'one_last_one' => (is => 'rw', isa => 'Ref');
+
+    # this one will work here ....
+    has 'fail' => (isa => 'CodeRef', is => 'bare');
+    has 'other_fail' => (is => 'bare');
+
+    package Bar;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    extends 'Foo';
+
+    ::lives_ok {
+        has '+bar' => (default => 'Bar::bar');
+    } '... we can change the default attribute option';
+
+    ::lives_ok {
+        has '+baz' => (isa => 'ArrayRef');
+    } '... we can add change the isa as long as it is a subtype';
+
+    ::lives_ok {
+        has '+foo' => (coerce => 1);
+    } '... we can change/add coerce as an attribute option';
+
+    ::lives_ok {
+        has '+gorch' => (required => 1);
+    } '... we can change/add required as an attribute option';
+
+    ::lives_ok {
+        has '+gloum' => (lazy => 1);
+    } '... we can change/add lazy as an attribute option';
+
+    ::lives_ok {
+        has '+gloum' => (lazy_build => 1);
+    } '... we can add lazy_build as an attribute option';
+
+    ::lives_ok {
+        has '+bunch_of_stuff' => (isa => 'ArrayRef[Int]');
+    } '... extend an attribute with parameterized type';
+
+    ::lives_ok {
+        has '+one_last_one' => (isa => subtype('Ref', where { blessed $_ eq 'CODE' }));
+    } '... extend an attribute with anon-subtype';
+
+    ::lives_ok {
+        has '+one_last_one' => (isa => 'Value');
+    } '... now can extend an attribute with a non-subtype';
+
+    ::lives_ok {
+        has '+fleem' => (weak_ref => 1);
+    } '... now allowed to add the weak_ref option via inheritance';
+
+    ::lives_ok {
+        has '+bling' => (handles => ['hello']);
+    } '... we can add the handles attribute option';
+
+    # this one will *not* work here ....
+    ::dies_ok {
+        has '+blang' => (handles => ['hello']);
+    } '... we can not alter the handles attribute option';
+    ::lives_ok {
+        has '+fail' => (isa => 'Ref');
+    } '... can now create an attribute with an improper subtype relation';
+    ::dies_ok {
+        has '+other_fail' => (trigger => sub {});
+    } '... cannot create an attribute with an illegal option';
+    ::throws_ok {
+        has '+does_not_exist' => (isa => 'Str');
+    } qr/in Bar/, '... cannot extend a non-existing attribute';
+}
+
+my $foo = Foo->new;
+isa_ok($foo, 'Foo');
+
+is($foo->foo, undef, '... got the right undef default value');
+lives_ok { $foo->foo('FooString') } '... assigned foo correctly';
+is($foo->foo, 'FooString', '... got the right value for foo');
+
+dies_ok { $foo->foo([]) } '... foo is not coercing (as expected)';
+
+is($foo->bar, 'Foo::bar', '... got the right default value');
+dies_ok { $foo->bar(10) } '... Foo::bar is a read/only attr';
+
+is($foo->baz, undef, '... got the right undef default value');
+
+{
+    my $hash_ref = {};
+    lives_ok { $foo->baz($hash_ref) } '... Foo::baz accepts hash refs';
+    is($foo->baz, $hash_ref, '... got the right value assigned to baz');
+
+    my $array_ref = [];
+    lives_ok { $foo->baz($array_ref) } '... Foo::baz accepts an array ref';
+    is($foo->baz, $array_ref, '... got the right value assigned to baz');
+
+    my $scalar_ref = \(my $var);
+    lives_ok { $foo->baz($scalar_ref) } '... Foo::baz accepts scalar ref';
+    is($foo->baz, $scalar_ref, '... got the right value assigned to baz');
+
+    lives_ok { $foo->bunch_of_stuff([qw[one two three]]) } '... Foo::bunch_of_stuff accepts an array of strings';
+
+    lives_ok { $foo->one_last_one(sub { 'Hello World'}) } '... Foo::one_last_one accepts a code ref';
+
+    my $code_ref = sub { 1 };
+    lives_ok { $foo->baz($code_ref) } '... Foo::baz accepts a code ref';
+    is($foo->baz, $code_ref, '... got the right value assigned to baz');
+}
+
+dies_ok {
+    Bar->new;
+} '... cannot create Bar without required gorch param';
+
+my $bar = Bar->new(gorch => 'Bar::gorch');
+isa_ok($bar, 'Bar');
+isa_ok($bar, 'Foo');
+
+is($bar->foo, undef, '... got the right undef default value');
+lives_ok { $bar->foo('FooString') } '... assigned foo correctly';
+is($bar->foo, 'FooString', '... got the right value for foo');
+lives_ok { $bar->foo([]) } '... assigned foo correctly';
+is($bar->foo, 'FooArrayRef', '... got the right value for foo');
+
+is($bar->gorch, 'Bar::gorch', '... got the right default value');
+
+is($bar->bar, 'Bar::bar', '... got the right default value');
+dies_ok { $bar->bar(10) } '... Bar::bar is a read/only attr';
+
+is($bar->baz, undef, '... got the right undef default value');
+
+{
+    my $hash_ref = {};
+    dies_ok { $bar->baz($hash_ref) } '... Bar::baz does not accept hash refs';
+
+    my $array_ref = [];
+    lives_ok { $bar->baz($array_ref) } '... Bar::baz can accept an array ref';
+    is($bar->baz, $array_ref, '... got the right value assigned to baz');
+
+    my $scalar_ref = \(my $var);
+    dies_ok { $bar->baz($scalar_ref) } '... Bar::baz does not accept a scalar ref';
+
+    lives_ok { $bar->bunch_of_stuff([1, 2, 3]) } '... Bar::bunch_of_stuff accepts an array of ints';
+    dies_ok { $bar->bunch_of_stuff([qw[one two three]]) } '... Bar::bunch_of_stuff does not accept an array of strings';
+
+    my $code_ref = sub { 1 };
+    dies_ok { $bar->baz($code_ref) } '... Bar::baz does not accept a code ref';
+}
+
+# check some meta-stuff
+
+ok(Bar->meta->has_attribute('foo'), '... Bar has a foo attr');
+ok(Bar->meta->has_attribute('bar'), '... Bar has a bar attr');
+ok(Bar->meta->has_attribute('baz'), '... Bar has a baz attr');
+ok(Bar->meta->has_attribute('gorch'), '... Bar has a gorch attr');
+ok(Bar->meta->has_attribute('gloum'), '... Bar has a gloum attr');
+ok(Bar->meta->has_attribute('bling'), '... Bar has a bling attr');
+ok(Bar->meta->has_attribute('bunch_of_stuff'), '... Bar does have a bunch_of_stuff attr');
+{
+local $TODO = 'not supported';
+ok(!Bar->meta->has_attribute('blang'), '... Bar does not have a blang attr');
+}
+ok(Bar->meta->has_attribute('fail'), '... Bar has a fail attr');
+{
+local $TODO = 'not supported';
+ok(!Bar->meta->has_attribute('other_fail'), '... Bar does not have an other_fail attr');
+}
+
+isnt(Foo->meta->get_attribute('foo'),
+     Bar->meta->get_attribute('foo'),
+     '... Foo and Bar have different copies of foo');
+isnt(Foo->meta->get_attribute('bar'),
+     Bar->meta->get_attribute('bar'),
+     '... Foo and Bar have different copies of bar');
+isnt(Foo->meta->get_attribute('baz'),
+     Bar->meta->get_attribute('baz'),
+     '... Foo and Bar have different copies of baz');
+isnt(Foo->meta->get_attribute('gorch'),
+     Bar->meta->get_attribute('gorch'),
+     '... Foo and Bar have different copies of gorch');
+isnt(Foo->meta->get_attribute('gloum'),
+     Bar->meta->get_attribute('gloum'),
+     '... Foo and Bar have different copies of gloum');
+isnt(Foo->meta->get_attribute('bling'),
+     Bar->meta->get_attribute('bling'),
+     '... Foo and Bar have different copies of bling');
+isnt(Foo->meta->get_attribute('bunch_of_stuff'),
+     Bar->meta->get_attribute('bunch_of_stuff'),
+     '... Foo and Bar have different copies of bunch_of_stuff');
+
+ok(Bar->meta->get_attribute('bar')->has_type_constraint,
+   '... Bar::bar inherited the type constraint too');
+ok(Bar->meta->get_attribute('baz')->has_type_constraint,
+  '... Bar::baz inherited the type constraint too');
+
+is(Bar->meta->get_attribute('bar')->type_constraint->name,
+   'Str', '... Bar::bar inherited the right type constraint too');
+
+is(Foo->meta->get_attribute('baz')->type_constraint->name,
+  'Ref', '... Foo::baz inherited the right type constraint too');
+is(Bar->meta->get_attribute('baz')->type_constraint->name,
+   'ArrayRef', '... Bar::baz inherited the right type constraint too');
+
+ok(!Foo->meta->get_attribute('gorch')->is_required,
+  '... Foo::gorch is not a required attr');
+ok(Bar->meta->get_attribute('gorch')->is_required,
+   '... Bar::gorch is a required attr');
+
+is(Foo->meta->get_attribute('bunch_of_stuff')->type_constraint->name,
+  'ArrayRef',
+  '... Foo::bunch_of_stuff is an ArrayRef');
+is(Bar->meta->get_attribute('bunch_of_stuff')->type_constraint->name,
+  'ArrayRef[Int]',
+  '... Bar::bunch_of_stuff is an ArrayRef[Int]');
+
+ok(!Foo->meta->get_attribute('gloum')->is_lazy,
+   '... Foo::gloum is not a required attr');
+ok(Bar->meta->get_attribute('gloum')->is_lazy,
+   '... Bar::gloum is a required attr');
+
+ok(!Foo->meta->get_attribute('foo')->should_coerce,
+  '... Foo::foo should not coerce');
+ok(Bar->meta->get_attribute('foo')->should_coerce,
+   '... Bar::foo should coerce');
+
+ok(!Foo->meta->get_attribute('bling')->has_handles,
+   '... Foo::foo should not handles');
+ok(Bar->meta->get_attribute('bling')->has_handles,
+   '... Bar::foo should handles');
+
+
diff --git a/t/020_attributes/failing/010_attribute_delegation.t b/t/020_attributes/failing/010_attribute_delegation.t
new file mode 100644 (file)
index 0000000..9dd746a
--- /dev/null
@@ -0,0 +1,436 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 92;
+use Test::Exception;
+
+
+
+# -------------------------------------------------------------------
+# HASH handles
+# -------------------------------------------------------------------
+# the canonical form of of the 'handles'
+# option is the hash ref mapping a
+# method name to the delegated method name
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (is => 'rw', default => 10);
+
+    sub baz { 42 }
+
+    package Bar;
+    use Mouse;
+
+    has 'foo' => (
+        is      => 'rw',
+        default => sub { Foo->new },
+        handles => {
+            'foo_bar' => 'bar',
+            foo_baz => 'baz',
+            'foo_bar_to_20' => [ bar => 20 ],
+        },
+    );
+}
+
+my $bar = Bar->new;
+isa_ok($bar, 'Bar');
+
+ok($bar->foo, '... we have something in bar->foo');
+isa_ok($bar->foo, 'Foo');
+
+my $meth = Bar->meta->get_method('foo_bar');
+isa_ok($meth, 'Mouse::Meta::Method::Delegation');
+is($meth->associated_attribute->name, 'foo',
+   'associated_attribute->name for this method is foo');
+
+is($bar->foo->bar, 10, '... bar->foo->bar returned the right default');
+
+can_ok($bar, 'foo_bar');
+is($bar->foo_bar, 10, '... bar->foo_bar delegated correctly');
+
+# change the value ...
+
+$bar->foo->bar(30);
+
+# and make sure the delegation picks it up
+
+is($bar->foo->bar, 30, '... bar->foo->bar returned the right (changed) value');
+is($bar->foo_bar, 30, '... bar->foo_bar delegated correctly');
+
+# change the value through the delegation ...
+
+$bar->foo_bar(50);
+
+# and make sure everyone sees it
+
+is($bar->foo->bar, 50, '... bar->foo->bar returned the right (changed) value');
+is($bar->foo_bar, 50, '... bar->foo_bar delegated correctly');
+
+# change the object we are delegating too
+
+my $foo = Foo->new(bar => 25);
+isa_ok($foo, 'Foo');
+
+is($foo->bar, 25, '... got the right foo->bar');
+
+lives_ok {
+    $bar->foo($foo);
+} '... assigned the new Foo to Bar->foo';
+
+is($bar->foo, $foo, '... assigned bar->foo with the new Foo');
+
+is($bar->foo->bar, 25, '... bar->foo->bar returned the right result');
+is($bar->foo_bar, 25, '... and bar->foo_bar delegated correctly again');
+
+# curried handles
+$bar->foo_bar_to_20;
+is($bar->foo_bar, 20, '... correctly curried a single argument');
+
+# -------------------------------------------------------------------
+# ARRAY handles
+# -------------------------------------------------------------------
+# we also support an array based format
+# which assumes that the name is the same
+# on either end
+
+{
+    package Engine;
+    use Mouse;
+
+    sub go   { 'Engine::go'   }
+    sub stop { 'Engine::stop' }
+
+    package Car;
+    use Mouse;
+
+    has 'engine' => (
+        is      => 'rw',
+        default => sub { Engine->new },
+        handles => [ 'go', 'stop' ]
+    );
+}
+
+my $car = Car->new;
+isa_ok($car, 'Car');
+
+isa_ok($car->engine, 'Engine');
+can_ok($car->engine, 'go');
+can_ok($car->engine, 'stop');
+
+is($car->engine->go, 'Engine::go', '... got the right value from ->engine->go');
+is($car->engine->stop, 'Engine::stop', '... got the right value from ->engine->stop');
+
+can_ok($car, 'go');
+can_ok($car, 'stop');
+
+is($car->go, 'Engine::go', '... got the right value from ->go');
+is($car->stop, 'Engine::stop', '... got the right value from ->stop');
+
+# -------------------------------------------------------------------
+# REGEXP handles
+# -------------------------------------------------------------------
+# and we support regexp delegation
+
+{
+    package Baz;
+    use Mouse;
+
+    sub foo { 'Baz::foo' }
+    sub bar { 'Baz::bar' }
+    sub boo { 'Baz::boo' }
+
+    package Baz::Proxy1;
+    use Mouse;
+
+    has 'baz' => (
+        is      => 'ro',
+        isa     => 'Baz',
+        default => sub { Baz->new },
+        handles => qr/.*/
+    );
+
+    package Baz::Proxy2;
+    use Mouse;
+
+    has 'baz' => (
+        is      => 'ro',
+        isa     => 'Baz',
+        default => sub { Baz->new },
+        handles => qr/.oo/
+    );
+
+    package Baz::Proxy3;
+    use Mouse;
+
+    has 'baz' => (
+        is      => 'ro',
+        isa     => 'Baz',
+        default => sub { Baz->new },
+        handles => qr/b.*/
+    );
+}
+
+{
+    my $baz_proxy = Baz::Proxy1->new;
+    isa_ok($baz_proxy, 'Baz::Proxy1');
+
+    can_ok($baz_proxy, 'baz');
+    isa_ok($baz_proxy->baz, 'Baz');
+
+    can_ok($baz_proxy, 'foo');
+    can_ok($baz_proxy, 'bar');
+    can_ok($baz_proxy, 'boo');
+
+    is($baz_proxy->foo, 'Baz::foo', '... got the right proxied return value');
+    is($baz_proxy->bar, 'Baz::bar', '... got the right proxied return value');
+    is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
+}
+{
+    my $baz_proxy = Baz::Proxy2->new;
+    isa_ok($baz_proxy, 'Baz::Proxy2');
+
+    can_ok($baz_proxy, 'baz');
+    isa_ok($baz_proxy->baz, 'Baz');
+
+    can_ok($baz_proxy, 'foo');
+    can_ok($baz_proxy, 'boo');
+
+    is($baz_proxy->foo, 'Baz::foo', '... got the right proxied return value');
+    is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
+}
+{
+    my $baz_proxy = Baz::Proxy3->new;
+    isa_ok($baz_proxy, 'Baz::Proxy3');
+
+    can_ok($baz_proxy, 'baz');
+    isa_ok($baz_proxy->baz, 'Baz');
+
+    can_ok($baz_proxy, 'bar');
+    can_ok($baz_proxy, 'boo');
+
+    is($baz_proxy->bar, 'Baz::bar', '... got the right proxied return value');
+    is($baz_proxy->boo, 'Baz::boo', '... got the right proxied return value');
+}
+
+# -------------------------------------------------------------------
+# ROLE handles
+# -------------------------------------------------------------------
+
+{
+    package Foo::Bar;
+    use Mouse::Role;
+
+    requires 'foo';
+    requires 'bar';
+
+    package Foo::Baz;
+    use Mouse;
+
+    sub foo { 'Foo::Baz::FOO' }
+    sub bar { 'Foo::Baz::BAR' }
+    sub baz { 'Foo::Baz::BAZ' }
+
+    package Foo::Thing;
+    use Mouse;
+
+    has 'thing' => (
+        is      => 'rw',
+        isa     => 'Foo::Baz',
+        handles => 'Foo::Bar',
+    );
+
+}
+
+{
+    my $foo = Foo::Thing->new(thing => Foo::Baz->new);
+    isa_ok($foo, 'Foo::Thing');
+    isa_ok($foo->thing, 'Foo::Baz');
+
+    ok($foo->meta->has_method('foo'), '... we have the method we expect');
+    ok($foo->meta->has_method('bar'), '... we have the method we expect');
+    ok(!$foo->meta->has_method('baz'), '... we dont have the method we expect');
+
+    is($foo->foo, 'Foo::Baz::FOO', '... got the right value');
+    is($foo->bar, 'Foo::Baz::BAR', '... got the right value');
+    is($foo->thing->baz, 'Foo::Baz::BAZ', '... got the right value');
+}
+
+# -------------------------------------------------------------------
+# AUTOLOAD & handles
+# -------------------------------------------------------------------
+
+{
+    package Foo::Autoloaded;
+    use Mouse;
+
+    sub AUTOLOAD {
+        my $self = shift;
+
+        my $name = our $AUTOLOAD;
+        $name =~ s/.*://; # strip fully-qualified portion
+
+        if (@_) {
+            return $self->{$name} = shift;
+        } else {
+            return $self->{$name};
+        }
+    }
+
+    package Bar::Autoloaded;
+    use Mouse;
+
+    has 'foo' => (
+        is      => 'rw',
+        default => sub { Foo::Autoloaded->new },
+        handles => { 'foo_bar' => 'bar' }
+    );
+
+    package Baz::Autoloaded;
+    use Mouse;
+
+    has 'foo' => (
+        is      => 'rw',
+        default => sub { Foo::Autoloaded->new },
+        handles => ['bar']
+    );
+
+    package Goorch::Autoloaded;
+    use Mouse;
+
+    ::dies_ok {
+        has 'foo' => (
+            is      => 'rw',
+            default => sub { Foo::Autoloaded->new },
+            handles => qr/bar/
+        );
+    } '... you cannot delegate to AUTOLOADED class with regexp';
+}
+
+# check HASH based delegation w/ AUTOLOAD
+
+{
+    my $bar = Bar::Autoloaded->new;
+    isa_ok($bar, 'Bar::Autoloaded');
+
+    ok($bar->foo, '... we have something in bar->foo');
+    isa_ok($bar->foo, 'Foo::Autoloaded');
+
+    # change the value ...
+
+    $bar->foo->bar(30);
+
+    # and make sure the delegation picks it up
+
+    is($bar->foo->bar, 30, '... bar->foo->bar returned the right (changed) value');
+    is($bar->foo_bar, 30, '... bar->foo_bar delegated correctly');
+
+    # change the value through the delegation ...
+
+    $bar->foo_bar(50);
+
+    # and make sure everyone sees it
+
+    is($bar->foo->bar, 50, '... bar->foo->bar returned the right (changed) value');
+    is($bar->foo_bar, 50, '... bar->foo_bar delegated correctly');
+
+    # change the object we are delegating too
+
+    my $foo = Foo::Autoloaded->new;
+    isa_ok($foo, 'Foo::Autoloaded');
+
+    $foo->bar(25);
+
+    is($foo->bar, 25, '... got the right foo->bar');
+
+    lives_ok {
+        $bar->foo($foo);
+    } '... assigned the new Foo to Bar->foo';
+
+    is($bar->foo, $foo, '... assigned bar->foo with the new Foo');
+
+    is($bar->foo->bar, 25, '... bar->foo->bar returned the right result');
+    is($bar->foo_bar, 25, '... and bar->foo_bar delegated correctly again');
+}
+
+# check ARRAY based delegation w/ AUTOLOAD
+
+{
+    my $baz = Baz::Autoloaded->new;
+    isa_ok($baz, 'Baz::Autoloaded');
+
+    ok($baz->foo, '... we have something in baz->foo');
+    isa_ok($baz->foo, 'Foo::Autoloaded');
+
+    # change the value ...
+
+    $baz->foo->bar(30);
+
+    # and make sure the delegation picks it up
+
+    is($baz->foo->bar, 30, '... baz->foo->bar returned the right (changed) value');
+    is($baz->bar, 30, '... baz->foo_bar delegated correctly');
+
+    # change the value through the delegation ...
+
+    $baz->bar(50);
+
+    # and make sure everyone sees it
+
+    is($baz->foo->bar, 50, '... baz->foo->bar returned the right (changed) value');
+    is($baz->bar, 50, '... baz->foo_bar delegated correctly');
+
+    # change the object we are delegating too
+
+    my $foo = Foo::Autoloaded->new;
+    isa_ok($foo, 'Foo::Autoloaded');
+
+    $foo->bar(25);
+
+    is($foo->bar, 25, '... got the right foo->bar');
+
+    lives_ok {
+        $baz->foo($foo);
+    } '... assigned the new Foo to Baz->foo';
+
+    is($baz->foo, $foo, '... assigned baz->foo with the new Foo');
+
+    is($baz->foo->bar, 25, '... baz->foo->bar returned the right result');
+    is($baz->bar, 25, '... and baz->foo_bar delegated correctly again');
+}
+
+# Check that removing attributes removes their handles methods also.
+{
+    {
+        package Quux;
+        use Mouse;
+        has foo => (
+            isa => 'Foo',
+            default => sub { Foo->new },
+            handles => { 'foo_bar' => 'bar' }
+        );
+    }
+    my $i = Quux->new;
+    ok($i->meta->has_method('foo_bar'), 'handles method foo_bar is present');
+    $i->meta->remove_attribute('foo');
+    ok(!$i->meta->has_method('foo_bar'), 'handles method foo_bar is removed');
+}
+
+# Make sure that a useful error message is thrown when the delegation target is
+# not an object
+{
+    my $i = Bar->new(foo => undef);
+    throws_ok { $i->foo_bar } qr/is not defined/,
+        'useful error from unblessed reference';
+
+    my $j = Bar->new(foo => []);
+    throws_ok { $j->foo_bar } qr/is not an object \(got 'ARRAY/,
+        'useful error from unblessed reference';
+
+    my $k = Bar->new(foo => "Foo");
+    lives_ok { $k->foo_baz } "but not for class name";
+}
diff --git a/t/020_attributes/failing/019_attribute_lazy_initializer.t b/t/020_attributes/failing/019_attribute_lazy_initializer.t
new file mode 100644 (file)
index 0000000..5e72276
--- /dev/null
@@ -0,0 +1,150 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 23;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'foo' => (
+        reader => 'get_foo',
+        writer => 'set_foo',
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'foo', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    has 'lazy_foo' => (
+        reader      => 'get_lazy_foo',
+        lazy        => 1,
+        default     => 10,
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'lazy_foo', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    has 'lazy_foo_w_type' => (
+        reader      => 'get_lazy_foo_w_type',
+        isa         => 'Int',
+        lazy        => 1,
+        default     => 20,
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'lazy_foo_w_type', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    has 'lazy_foo_builder' => (
+        reader      => 'get_lazy_foo_builder',
+        builder     => 'get_foo_builder',
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'lazy_foo_builder', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    has 'lazy_foo_builder_w_type' => (
+        reader      => 'get_lazy_foo_builder_w_type',
+        isa         => 'Int',
+        builder     => 'get_foo_builder_w_type',
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'lazy_foo_builder_w_type', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    sub get_foo_builder        { 100  }
+    sub get_foo_builder_w_type { 1000 }
+}
+
+{
+    my $foo = Foo->new(foo => 10);
+    isa_ok($foo, 'Foo');
+
+    is($foo->get_foo,             20, 'initial value set to 2x given value');
+    is($foo->get_lazy_foo,        20, 'initial lazy value set to 2x given value');
+    is($foo->get_lazy_foo_w_type, 40, 'initial lazy value with type set to 2x given value');
+    is($foo->get_lazy_foo_builder,        200, 'initial lazy value with builder set to 2x given value');
+    is($foo->get_lazy_foo_builder_w_type, 2000, 'initial lazy value with builder and type set to 2x given value');
+}
+
+{
+    package Bar;
+    use Mouse;
+
+    has 'foo' => (
+        reader => 'get_foo',
+        writer => 'set_foo',
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'foo', '... got the right name');
+
+            $callback->($value * 2);
+        },
+    );
+
+    __PACKAGE__->meta->make_immutable;
+}
+
+{
+    my $bar = Bar->new(foo => 10);
+    isa_ok($bar, 'Bar');
+
+    is($bar->get_foo, 20, 'initial value set to 2x given value');
+}
+
+{
+    package Fail::Bar;
+    use Mouse;
+
+    has 'foo' => (
+        reader => 'get_foo',
+        writer => 'set_foo',
+        isa    => 'Int',
+        initializer => sub {
+            my ($self, $value, $callback, $attr) = @_;
+
+            ::isa_ok($attr, 'Mouse::Meta::Attribute');
+            ::is($attr->name, 'foo', '... got the right name');
+
+            $callback->("Hello $value World");
+        },
+    );
+
+    __PACKAGE__->meta->make_immutable;
+}
+
+dies_ok {
+    Fail::Bar->new(foo => 10)
+} '... this fails, because initializer returns a bad type';
+
diff --git a/t/020_attributes/failing/021_method_generation_rules.t b/t/020_attributes/failing/021_method_generation_rules.t
new file mode 100644 (file)
index 0000000..2169780
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 17;
+use Test::Exception;
+
+
+
+=pod
+
+    is => rw, writer => _foo    # turns into (reader => foo, writer => _foo)
+    is => ro, writer => _foo    # turns into (reader => foo, writer => _foo) as before
+    is => rw, accessor => _foo  # turns into (accessor => _foo)
+    is => ro, accessor => _foo  # error, accesor is rw
+
+=cut
+
+sub make_class {
+    my ($is, $attr, $class) = @_;
+
+    eval "package $class; use Mouse; has 'foo' => ( is => '$is', $attr => '_foo' );";
+
+    return $@ ? die $@ : $class;
+}
+
+my $obj;
+my $class;
+
+$class = make_class('rw', 'writer', 'Test::Class::WriterRW');
+ok($class, "Can define attr with rw + writer");
+
+$obj = $class->new();
+
+can_ok($obj, qw/foo _foo/);
+lives_ok {$obj->_foo(1)} "$class->_foo is writer";
+is($obj->foo(), 1, "$class->foo is reader");
+dies_ok {$obj->foo(2)} "$class->foo is not writer"; # this should fail
+ok(!defined $obj->_foo(), "$class->_foo is not reader");
+
+$class = make_class('ro', 'writer', 'Test::Class::WriterRO');
+ok($class, "Can define attr with ro + writer");
+
+$obj = $class->new();
+
+can_ok($obj, qw/foo _foo/);
+lives_ok {$obj->_foo(1)} "$class->_foo is writer";
+is($obj->foo(), 1, "$class->foo is reader");
+dies_ok {$obj->foo(1)} "$class->foo is not writer";
+isnt($obj->_foo(), 1, "$class->_foo is not reader");
+
+$class = make_class('rw', 'accessor', 'Test::Class::AccessorRW');
+ok($class, "Can define attr with rw + accessor");
+
+$obj = $class->new();
+
+can_ok($obj, qw/_foo/);
+lives_ok {$obj->_foo(1)} "$class->_foo is writer";
+is($obj->_foo(), 1, "$class->foo is reader");
+
+dies_ok { make_class('ro', 'accessor', "Test::Class::AccessorRO"); } "Cant define attr with ro + accessor";
+
diff --git a/t/020_attributes/failing/022_legal_options_for_inheritance.t b/t/020_attributes/failing/022_legal_options_for_inheritance.t
new file mode 100644 (file)
index 0000000..2830506
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 2;
+
+
+
+{
+    package Bar::Meta::Attribute;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+
+    has 'my_legal_option' => (
+      isa => 'CodeRef',
+      is => 'rw',
+    );
+
+    around legal_options_for_inheritance => sub {
+      return (shift->(@_), qw/my_legal_option/);
+    };
+
+    package Bar;
+    use Mouse;
+
+    has 'bar' => (
+      metaclass       => 'Bar::Meta::Attribute',
+      my_legal_option => sub { 'Bar' },
+      is => 'bare',
+    );
+
+    package Bar::B;
+    use Mouse;
+
+    extends 'Bar';
+
+    has '+bar' => (
+      my_legal_option => sub { 'Bar::B' }
+    );
+}
+
+my $bar_attr = Bar::B->meta->get_attribute('bar');
+my ($legal_option) = grep {
+  $_ eq 'my_legal_option'
+} $bar_attr->legal_options_for_inheritance;
+is($legal_option, 'my_legal_option',
+  '... added my_legal_option as legal option for inheritance' );
+is($bar_attr->my_legal_option->(), 'Bar::B', '... overloaded my_legal_option');
diff --git a/t/020_attributes/failing/023_attribute_names.t b/t/020_attributes/failing/023_attribute_names.t
new file mode 100644 (file)
index 0000000..f98d556
--- /dev/null
@@ -0,0 +1,58 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Test::More tests => 8;
+use Test::Exception;
+
+my $exception_regex = qr/You must provide a name for the attribute/;
+{
+    package My::Role;
+    use Mouse::Role;
+
+    ::throws_ok {
+        has;
+    } $exception_regex, 'has; fails';
+
+    ::throws_ok {
+        has undef;
+    } $exception_regex, 'has undef; fails';
+
+    ::lives_ok {
+        has "" => (
+            is => 'bare',
+        );
+    } 'has ""; works now';
+
+    ::lives_ok {
+        has 0 => (
+            is => 'bare',
+        );
+    } 'has 0; works now';
+}
+
+{
+    package My::Class;
+    use Mouse;
+
+    ::throws_ok {
+        has;
+    } $exception_regex, 'has; fails';
+
+    ::throws_ok {
+        has undef;
+    } $exception_regex, 'has undef; fails';
+
+    ::lives_ok {
+        has "" => (
+            is => 'bare',
+        );
+    } 'has ""; works now';
+
+    ::lives_ok {
+        has 0 => (
+            is => 'bare',
+        );
+    } 'has 0; works now';
+}
+
diff --git a/t/020_attributes/failing/027_accessor_override_method.t b/t/020_attributes/failing/027_accessor_override_method.t
new file mode 100644 (file)
index 0000000..8285b69
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env perl
+use strict;
+use warnings;
+use Test::More;
+
+BEGIN {
+    eval "use Test::Output;";
+    plan skip_all => "Test::Output is required for this test" if $@;
+    plan tests => 5;
+}
+
+{
+    package Foo;
+    use Mouse;
+
+    sub get_a { }
+    sub set_b { }
+    sub has_c { }
+    sub clear_d { }
+    sub e { }
+}
+
+my $foo_meta = Foo->meta;
+stderr_like(sub { $foo_meta->add_attribute(a => (reader => 'get_a')) },
+            qr/^You are overwriting a locally defined method \(get_a\) with an accessor/, 'reader overriding gives proper warning');
+stderr_like(sub { $foo_meta->add_attribute(b => (writer => 'set_b')) },
+            qr/^You are overwriting a locally defined method \(set_b\) with an accessor/, 'writer overriding gives proper warning');
+stderr_like(sub { $foo_meta->add_attribute(c => (predicate => 'has_c')) },
+            qr/^You are overwriting a locally defined method \(has_c\) with an accessor/, 'predicate overriding gives proper warning');
+stderr_like(sub { $foo_meta->add_attribute(d => (clearer => 'clear_d')) },
+            qr/^You are overwriting a locally defined method \(clear_d\) with an accessor/, 'clearer overriding gives proper warning');
+stderr_like(sub { $foo_meta->add_attribute(e => (is => 'rw')) },
+            qr/^You are overwriting a locally defined method \(e\) with an accessor/, 'accessor overriding gives proper warning');
diff --git a/t/020_attributes/failing/028_no_slot_access.t b/t/020_attributes/failing/028_no_slot_access.t
new file mode 100644 (file)
index 0000000..12ff7b0
--- /dev/null
@@ -0,0 +1,90 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+{
+    package SomeAwesomeDB;
+
+    sub new_row { }
+    sub read    { }
+    sub write   { }
+}
+
+{
+    package MouseX::SomeAwesomeDBFields;
+
+    # implementation of methods not called in the example deliberately
+    # omitted
+
+    use Mouse::Role;
+
+    sub inline_create_instance {
+        my ( $self, $classvar ) = @_;
+
+        "bless SomeAwesomeDB::new_row(), $classvar";
+    }
+
+    sub inline_get_slot_value {
+        my ( $self, $invar, $slot ) = @_;
+
+        "SomeAwesomeDB::read($invar, \"$slot\")";
+    }
+
+    sub inline_set_slot_value {
+        my ( $self, $invar, $slot, $valexp ) = @_;
+
+        "SomeAwesomeDB::write($invar, \"$slot\", $valexp)";
+    }
+
+    sub inline_is_slot_initialized {
+        my ( $self, $invar, $slot ) = @_;
+
+        "1";
+    }
+
+    sub inline_initialize_slot {
+        my ( $self, $invar, $slot ) = @_;
+
+        "";
+    }
+
+    sub inline_slot_access {
+        die "inline_slot_access should not have been used";
+    }
+}
+
+{
+    package Toy;
+
+    use Mouse;
+    use Mouse::Util::MetaRole;
+
+    use Test::More tests => 3;
+    use Test::Exception;
+
+    Mouse::Util::MetaRole::apply_metaclass_roles(
+        for_class                => __PACKAGE__,
+        instance_metaclass_roles => ['MouseX::SomeAwesomeDBFields']
+    );
+
+    lives_ok {
+        has lazy_attr => (
+            is      => 'ro',
+            isa     => 'Bool',
+            lazy    => 1,
+            default => sub {0},
+        );
+    }
+    "Adding lazy accessor does not use inline_slot_access";
+
+    lives_ok {
+        has rw_attr => (
+            is => 'rw',
+        );
+    }
+    "Adding read-write accessor does not use inline_slot_access";
+
+    lives_ok { __PACKAGE__->meta->make_immutable; }
+    "Inling constructor does not use inline_slot_access";
+}
diff --git a/t/020_attributes/failing/031_delegation_and_modifiers.t b/t/020_attributes/failing/031_delegation_and_modifiers.t
new file mode 100644 (file)
index 0000000..2a8d62a
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 5;
+use Test::Exception;
+
+{
+    package Bar;
+    use Mouse;
+
+    sub baz   { 'Bar::baz' }
+    sub gorch { 'Bar::gorch' }
+
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (
+        is      => 'ro',
+        isa     => 'Bar',
+        lazy    => 1,
+        default => sub { Bar->new },
+        handles => [qw[ baz gorch ]]
+    );
+
+    package Foo::Extended;
+    use Mouse;
+
+    extends 'Foo';
+
+    has 'test' => (
+        is      => 'rw',
+        isa     => 'Bool',
+        default => sub { 0 },
+    );
+
+    around 'bar' => sub {
+        my $next = shift;
+        my $self = shift;
+
+        $self->test(1);
+        $self->$next();
+    };
+}
+
+my $foo = Foo::Extended->new;
+isa_ok($foo, 'Foo::Extended');
+isa_ok($foo, 'Foo');
+
+ok(!$foo->test, '... the test value has not been changed');
+
+is($foo->baz, 'Bar::baz', '... got the right delegated method');
+
+ok($foo->test, '... the test value has now been changed');
+
+
+
+
+
+
+
+
diff --git a/t/022-init-arg.t b/t/022-init-arg.t
deleted file mode 100644 (file)
index 9546e3d..0000000
+++ /dev/null
@@ -1,50 +0,0 @@
-#!/usr/bin/env perl
-use strict;
-use warnings;
-use Test::More tests => 11;
-
-do {
-    package Class;
-    use Mouse;
-
-    has name => (
-        is       => 'rw',
-        isa      => 'Str',
-        init_arg => 'key',
-        default  => 'default',
-    );
-};
-
-my $object = Class->new;
-is($object->name, 'default', 'accessor uses attribute name');
-is($object->{key}, undef, 'nothing in object->{init_arg}!');
-is($object->{name}, 'default', 'value is in object->{name}');
-
-my $object2 = Class->new(name => 'name', key => 'key');
-is($object2->name, 'key', 'attribute value is from name');
-is($object2->{key}, undef, 'no value for the init_arg');
-is($object2->{name}, 'key', 'value is in key from name');
-
-my $attr = $object2->meta->get_attribute('name');
-ok($attr, 'got the attribute object by name (not init_arg)');
-is($attr->name, 'name', 'name is name');
-is($attr->init_arg, 'key', 'init_arg is key');
-
-do {
-    package Foo;
-    use Mouse;
-
-    has name => (
-        is       => 'rw',
-        init_arg => undef,
-        default  => 'default',
-    );
-};
-
-my $foo = Foo->new(name => 'joe');
-is($foo->name, 'default', 'init_arg => undef ignores attribute name in the constructor');
-
-Foo->meta->make_immutable;
-
-my $bar = Foo->new(name => 'joe');
-is($bar->name, 'default', 'init_arg => undef ignores attribute name in the inlined constructor');
index b6acf2b..f18af27 100755 (executable)
@@ -6,6 +6,8 @@ use warnings;
 use Test::More tests => 26;
 use Test::Exception;
 
+use lib 't/lib';
+use Test::Mouse; # Mouse::Meta::Module->version
 use Mouse::Meta::Role;
 
 {
index 2501185..67dcf44 100755 (executable)
@@ -6,6 +6,9 @@ use warnings;
 use Test::More tests => 40;
 use Test::Exception;
 
+use lib 't/lib';
+use Test::Mouse; # Mouse::Meta::Module->version
+
 =pod
 
 NOTE:
@@ -6,6 +6,9 @@ use warnings;
 use Test::More tests => 32;
 use Test::Exception;
 
+use lib 't/lib';
+use Test::Mouse;
+
 =pod
 
 Check for repeated inheritance causing
@@ -88,10 +91,15 @@ ok(Role::Base2->meta->has_override_method_modifier('foo'), '... have the method
 ok(Role::Derived3->meta->has_override_method_modifier('foo'), '... have the method foo as expected');
 ok(Role::Derived4->meta->has_override_method_modifier('foo'), '... have the method foo as expected');
 ok(My::Test::Class2->meta->has_method('foo'), '... have the method foo as expected');
+{
+local $TODO = 'Not a Mouse::Meta::Method::Overriden';
 isa_ok(My::Test::Class2->meta->get_method('foo'), 'Mouse::Meta::Method::Overridden');
+}
 ok(My::Test::Class2::Base->meta->has_method('foo'), '... have the method foo as expected');
+{
+local $TODO = 'Not a Class::MOP::Method';
 isa_ok(My::Test::Class2::Base->meta->get_method('foo'), 'Class::MOP::Method');
-
+}
 is(My::Test::Class2::Base->foo, 'My::Test::Class2::Base', '... got the right value from method');
 is(My::Test::Class2->foo, 'My::Test::Class2::Base -> Role::Base::foo', '... got the right value from method');
 
@@ -141,10 +149,15 @@ ok(Role::Base3->meta->has_around_method_modifiers('foo'), '... have the method f
 ok(Role::Derived5->meta->has_around_method_modifiers('foo'), '... have the method foo as expected');
 ok(Role::Derived6->meta->has_around_method_modifiers('foo'), '... have the method foo as expected');
 ok(My::Test::Class3->meta->has_method('foo'), '... have the method foo as expected');
+{
+local $TODO = 'Not a Class::MOP::Method::Wrapped';
 isa_ok(My::Test::Class3->meta->get_method('foo'), 'Class::MOP::Method::Wrapped');
+}
 ok(My::Test::Class3::Base->meta->has_method('foo'), '... have the method foo as expected');
+{
+local $TODO = 'Not a Class::MOP::Method';
 isa_ok(My::Test::Class3::Base->meta->get_method('foo'), 'Class::MOP::Method');
-
+}
 is(My::Test::Class3::Base->foo, 'My::Test::Class3::Base', '... got the right value from method');
 is(My::Test::Class3->foo, 'Role::Base::foo(My::Test::Class3::Base)', '... got the right value from method');
 
@@ -58,8 +58,11 @@ ok(My::Class->meta->has_method($_), "we have a $_ method") for qw(foo baz bar ro
 }
 
 ok(My::OtherRole->meta->has_method($_), "we have a $_ method") for qw(foo baz role_bar);
+{
+local $TODO = 'auto requires resolution is not supported';
 ok(My::OtherRole->meta->requires_method('bar'), '... and the &bar method is required');
 ok(!My::OtherRole->meta->requires_method('role_bar'), '... and the &role_bar method is not required');
+}
 
 {
     package My::AliasingRole;
@@ -101,12 +104,11 @@ ok(!My::AliasingRole->meta->requires_method('bar'), '... and the &bar method is
     package My::Foo::Class::Broken;
     use Mouse;
 
-    ::throws_ok {
+    ::dies_ok {
         with 'Foo::Role' => { -alias => { 'foo' => 'foo_foo' }, -excludes => 'foo' },
              'Bar::Role' => { -alias => { 'foo' => 'foo_foo' }, -excludes => 'foo' },
              'Baz::Role';
-    } qr/Due to a method name conflict in roles 'Bar::Role' and 'Foo::Role', the method 'foo_foo' must be implemented or excluded by 'My::Foo::Class::Broken'/,
-      '... composed our roles correctly';
+    } '... composed our roles correctly';
 }
 
 {
@@ -135,8 +137,11 @@ ok(!My::Foo::Role->meta->requires_method('foo'), '... and the &foo method is not
 
 {
     package My::Foo::Role::Other;
+    use Test::More; # for $TODO
     use Mouse::Role;
 
+    local $TODO = 'not supported';
+
     ::lives_ok {
         with 'Foo::Role' => { -alias => { 'foo' => 'foo_foo' }, -excludes => 'foo' },
              'Bar::Role' => { -alias => { 'foo' => 'foo_foo' }, -excludes => 'foo' },
@@ -145,8 +150,10 @@ ok(!My::Foo::Role->meta->requires_method('foo'), '... and the &foo method is not
 }
 
 ok(!My::Foo::Role::Other->meta->has_method('foo_foo'), "we dont have a foo_foo method");
+{
+local $TODO = 'auto requires resolution is not supported';
 ok(My::Foo::Role::Other->meta->requires_method('foo_foo'), '... and the &foo method is required');
-
+}
 {
     package My::Foo::AliasOnly;
     use Mouse;
similarity index 78%
rename from t/030_roles/failing/018_runtime_roles_w_params.t
rename to t/030_roles/018_runtime_roles_w_params.t
index 3bce166..aa1a02b 100644 (file)
@@ -50,7 +50,10 @@ use Test::Exception;
 
     is($foo->bar, 'BAR', '... got the expect value');
     ok($foo->can('baz'), '... we have baz method now');
-    is($foo->baz, 'FOO-BAZ', '... got the expect value');
+    {
+        local $TODO = 'rebless_params is not implemented';
+        is($foo->baz, 'FOO-BAZ', '... got the expect value');
+    }
 }
 
 # with extra params ...
@@ -65,9 +68,15 @@ use Test::Exception;
         Bar->meta->apply($foo, (rebless_params => { bar => 'FOO-BAR', baz => 'FOO-BAZ' }))
     } '... this works';
 
-    is($foo->bar, 'FOO-BAR', '... got the expect value');
+    {
+        local $TODO = 'rebless params is not implemented';
+        is($foo->bar, 'FOO-BAR', '... got the expect value');
+    }
     ok($foo->can('baz'), '... we have baz method now');
-    is($foo->baz, 'FOO-BAZ', '... got the expect value');
+    {
+        local $TODO = 'rebless params is not implemented';
+        is($foo->baz, 'FOO-BAZ', '... got the expect value');
+    }
 }
 
 
@@ -4,6 +4,7 @@ use strict;
 use warnings;
 
 use Test::More tests => 17;
+use lib 't/lib';
 use Test::Mouse;
 
 {
index f678d2c..d852b17 100644 (file)
@@ -39,7 +39,7 @@ ok(My::OtherRole->meta->has_method($_), "we have a $_ method") for qw(foo bar ba
 
 ok(!My::OtherRole->meta->requires_method('foo'), '... and the &foo method is not required');
 ok(My::OtherRole->meta->requires_method('bar'), '... and the &bar method is required');
-use Data::Dumper; print Dumper(My::OtherRole->meta->{required_methods});
+
 {
     package Foo::Role;
     use Mouse::Role;
index 49ba0a3..0f00eb0 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use Test::More tests => 14;
 use Test::Exception;
 
-use Mouse::Meta::Role::Application::RoleSummation;
+#use Mouse::Meta::Role::Application::RoleSummation;
 use Mouse::Meta::Role::Composite;
 
 {
index ba4d3bc..c8b6f6b 100644 (file)
@@ -6,7 +6,7 @@ use warnings;
 use Test::More tests => 12;
 use Test::Exception;
 
-use Mouse::Meta::Role::Application::RoleSummation;
+#use Mouse::Meta::Role::Application::RoleSummation;
 use Mouse::Meta::Role::Composite;
 
 {
diff --git a/t/040_type_constraints/009_union_types_and_coercions.t b/t/040_type_constraints/009_union_types_and_coercions.t
new file mode 100755 (executable)
index 0000000..ca8fcab
--- /dev/null
@@ -0,0 +1,164 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More;
+use Test::Exception;
+
+BEGIN {
+    eval "use IO::String; use IO::File;";
+    plan skip_all => "IO::String and IO::File are required for this test" if $@;
+    plan tests => 28;
+}
+
+
+
+{
+    package Email::Mouse;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    use IO::String;
+
+    our $VERSION = '0.01';
+
+    # create subtype for IO::String
+
+    subtype 'IO::String'
+        => as 'Object'
+        => where { $_->isa('IO::String') };
+
+    coerce 'IO::String'
+        => from 'Str'
+            => via { IO::String->new($_) },
+        => from 'ScalarRef',
+            => via { IO::String->new($_) };
+
+    # create subtype for IO::File
+
+    subtype 'IO::File'
+        => as 'Object'
+        => where { $_->isa('IO::File') };
+
+    coerce 'IO::File'
+        => from 'FileHandle'
+            => via { bless $_, 'IO::File' };
+
+    # create the alias
+
+    my $st = subtype 'IO::StringOrFile' => as 'IO::String | IO::File';
+    #::diag $st->dump;
+
+    # attributes
+
+    has 'raw_body' => (
+        is      => 'rw',
+        isa     => 'IO::StringOrFile',
+        coerce  => 1,
+        default => sub { IO::String->new() },
+    );
+
+    sub as_string {
+        my ($self) = @_;
+        my $fh = $self->raw_body();
+
+        return do { local $/; <$fh> };
+    }
+}
+
+{
+    my $email = Email::Mouse->new;
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::String');
+
+    is($email->as_string, undef, '... got correct empty string');
+}
+
+{
+    my $email = Email::Mouse->new(raw_body => '... this is my body ...');
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::String');
+
+    is($email->as_string, '... this is my body ...', '... got correct string');
+
+    lives_ok {
+        $email->raw_body('... this is the next body ...');
+    } '... this will coerce correctly';
+
+    isa_ok($email->raw_body, 'IO::String');
+
+    is($email->as_string, '... this is the next body ...', '... got correct string');
+}
+
+{
+    my $str = '... this is my body (ref) ...';
+
+    my $email = Email::Mouse->new(raw_body => \$str);
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::String');
+
+    is($email->as_string, $str, '... got correct string');
+
+    my $str2 = '... this is the next body (ref) ...';
+
+    lives_ok {
+        $email->raw_body(\$str2);
+    } '... this will coerce correctly';
+
+    isa_ok($email->raw_body, 'IO::String');
+
+    is($email->as_string, $str2, '... got correct string');
+}
+
+{
+    my $io_str = IO::String->new('... this is my body (IO::String) ...');
+
+    my $email = Email::Mouse->new(raw_body => $io_str);
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::String');
+    is($email->raw_body, $io_str, '... and it is the one we expected');
+
+    is($email->as_string, '... this is my body (IO::String) ...', '... got correct string');
+
+    my $io_str2 = IO::String->new('... this is the next body (IO::String) ...');
+
+    lives_ok {
+        $email->raw_body($io_str2);
+    } '... this will coerce correctly';
+
+    isa_ok($email->raw_body, 'IO::String');
+    is($email->raw_body, $io_str2, '... and it is the one we expected');
+
+    is($email->as_string, '... this is the next body (IO::String) ...', '... got correct string');
+}
+
+{
+    my $fh;
+
+    open($fh, '<', $0) || die "Could not open $0";
+
+    my $email = Email::Mouse->new(raw_body => $fh);
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::File');
+
+    close($fh);
+}
+
+{
+    my $fh = IO::File->new($0);
+
+    my $email = Email::Mouse->new(raw_body => $fh);
+    isa_ok($email, 'Email::Mouse');
+
+    isa_ok($email->raw_body, 'IO::File');
+    is($email->raw_body, $fh, '... and it is the one we expected');
+}
+
+
+
diff --git a/t/040_type_constraints/015_enum.t b/t/040_type_constraints/015_enum.t
new file mode 100755 (executable)
index 0000000..48bdca6
--- /dev/null
@@ -0,0 +1,65 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More 'no_plan';
+
+use Scalar::Util ();
+
+use lib 't/lib';
+use Mouse::Util::TypeConstraints;
+use Test::Mouse; # for export_type_constraints_as_functions()
+
+enum Letter => 'a'..'z', 'A'..'Z';
+enum Language => 'Perl 5', 'Perl 6', 'PASM', 'PIR'; # any others? ;)
+enum Metacharacter => '*', '+', '?', '.', '|', '(', ')', '[', ']', '\\';
+
+my @valid_letters = ('a'..'z', 'A'..'Z');
+
+my @invalid_letters = qw/ab abc abcd/;
+push @invalid_letters, qw/0 4 9 ~ @ $ %/;
+push @invalid_letters, qw/l33t st3v4n 3num/;
+
+my @valid_languages = ('Perl 5', 'Perl 6', 'PASM', 'PIR');
+my @invalid_languages = ('perl 5', 'Python', 'Ruby', 'Perl 666', 'PASM++');
+# note that "perl 5" is invalid because case now matters
+
+my @valid_metacharacters = (qw/* + ? . | ( ) [ ] /, '\\');
+my @invalid_metacharacters = qw/< > & % $ @ ! ~ `/;
+push @invalid_metacharacters, qw/.* fish(sticks)? atreides/;
+push @invalid_metacharacters, '^1?$|^(11+?)\1+$';
+
+Mouse::Util::TypeConstraints->export_type_constraints_as_functions();
+
+ok(Letter($_), "'$_' is a letter") for @valid_letters;
+ok(!Letter($_), "'$_' is not a letter") for @invalid_letters;
+
+ok(Language($_), "'$_' is a language") for @valid_languages;
+ok(!Language($_), "'$_' is not a language") for @invalid_languages;
+
+ok(Metacharacter($_), "'$_' is a metacharacter") for @valid_metacharacters;
+ok(!Metacharacter($_), "'$_' is not a metacharacter")
+    for @invalid_metacharacters;
+
+# check anon enums
+
+my $anon_enum = enum \@valid_languages;
+isa_ok($anon_enum, 'Mouse::Meta::TypeConstraint');
+
+#is($anon_enum->name, '__ANON__', '... got the right name');
+#is($anon_enum->parent->name, 'Str', '... got the right parent name');
+
+ok($anon_enum->check($_), "'$_' is a language") for @valid_languages;
+
+
+#ok( !$anon_enum->equals( enum [qw(foo bar)] ), "doesn't equal a diff enum" );
+#ok( $anon_enum->equals( $anon_enum ), "equals itself" );
+#ok( $anon_enum->equals( enum \@valid_languages ), "equals duplicate" );
+
+#ok( !$anon_enum->is_subtype_of('Object'), 'enum not a subtype of Object');
+ok( !$anon_enum->is_a_type_of('Object'), 'enum not type of Object');
+
+#ok( !$anon_enum->is_subtype_of('ThisTypeDoesNotExist'), 'enum not a subtype of nonexistant type');
+ok( !$anon_enum->is_a_type_of('ThisTypeDoesNotExist'), 'enum not type of nonexistant type');
+
diff --git a/t/040_type_constraints/017_subtyping_union_types.t b/t/040_type_constraints/017_subtyping_union_types.t
new file mode 100755 (executable)
index 0000000..505e92c
--- /dev/null
@@ -0,0 +1,69 @@
+#!/usr/bin/perl\r
+\r
+use strict;\r
+use warnings;\r
+\r
+use Test::More tests => 19;\r
+use Test::Exception;\r
+\r
+BEGIN {\r
+    use_ok("Mouse::Util::TypeConstraints");\r
+}\r
+\r
+lives_ok {\r
+    subtype 'MyCollections' => as 'ArrayRef | HashRef';\r
+} '... created the subtype special okay';\r
+\r
+{\r
+    my $t = find_type_constraint('MyCollections');\r
+    isa_ok($t, 'Mouse::Meta::TypeConstraint');\r
+\r
+    is($t->name, 'MyCollections', '... name is correct');\r
+\r
+    my $p = $t->parent;\r
+#    isa_ok($p, 'Mouse::Meta::TypeConstraint::Union');\r
+    isa_ok($p, 'Mouse::Meta::TypeConstraint');\r
+\r
+    is($p->name, 'ArrayRef|HashRef', '... parent name is correct');\r
+\r
+    ok($t->check([]), '... validated it correctly');\r
+    ok($t->check({}), '... validated it correctly');\r
+    ok(!$t->check(1), '... validated it correctly');\r
+}\r
+\r
+lives_ok {\r
+    subtype 'MyCollectionsExtended'\r
+        => as 'ArrayRef|HashRef'\r
+        => where {\r
+            if (ref($_) eq 'ARRAY') {\r
+                return if scalar(@$_) < 2;\r
+            }\r
+            elsif (ref($_) eq 'HASH') {\r
+                return if scalar(keys(%$_)) < 2;\r
+            }\r
+            1;\r
+        };\r
+} '... created the subtype special okay';\r
+\r
+{\r
+    my $t = find_type_constraint('MyCollectionsExtended');\r
+    isa_ok($t, 'Mouse::Meta::TypeConstraint');\r
+\r
+    is($t->name, 'MyCollectionsExtended', '... name is correct');\r
+\r
+    my $p = $t->parent;\r
+#    isa_ok($p, 'Mouse::Meta::TypeConstraint::Union');\r
+    isa_ok($p, 'Mouse::Meta::TypeConstraint');\r
+\r
+    is($p->name, 'ArrayRef|HashRef', '... parent name is correct');\r
+\r
+    ok(!$t->check([]), '... validated it correctly');\r
+    ok($t->check([1, 2]), '... validated it correctly');\r
+\r
+    ok(!$t->check({}), '... validated it correctly');\r
+    ok($t->check({ one => 1, two => 2 }), '... validated it correctly');\r
+\r
+    ok(!$t->check(1), '... validated it correctly');\r
+}\r
+\r
+\r
diff --git a/t/040_type_constraints/025_type_coersion_on_lazy_attributes.t b/t/040_type_constraints/025_type_coersion_on_lazy_attributes.t
new file mode 100755 (executable)
index 0000000..9400f1a
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+{
+    package SomeClass;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    subtype 'DigitSix' => as 'Num'
+        => where { /^6$/ };
+    subtype 'TextSix' => as 'Str'
+        => where { /Six/i };
+
+    coerce 'TextSix'
+        => from 'DigitSix'
+        => via { confess("Cannot live without 6 ($_)") unless /^6$/; 'Six' };
+
+    has foo => (
+        is      => 'ro',
+        isa     => 'TextSix',
+        coerce  => 1,
+        default => 6,
+        lazy    => 1
+    );
+}
+
+is(SomeClass->new()->foo, 'Six');
+
+
diff --git a/t/050_metaclasses/001_custom_attr_meta_with_roles.t b/t/050_metaclasses/001_custom_attr_meta_with_roles.t
new file mode 100644 (file)
index 0000000..613e0f9
--- /dev/null
@@ -0,0 +1,43 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+use Test::Exception;
+
+
+
+{
+    package My::Custom::Meta::Attr;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+}
+
+{
+    package My::Fancy::Role;
+    use Mouse::Role;
+
+    has 'bling_bling' => (
+        metaclass => 'My::Custom::Meta::Attr',
+        is        => 'rw',
+        isa       => 'Str',
+    );
+}
+
+{
+    package My::Class;
+    use Mouse;
+
+    with 'My::Fancy::Role';
+}
+
+my $c = My::Class->new;
+isa_ok($c, 'My::Class');
+
+ok($c->meta->has_attribute('bling_bling'), '... got the attribute');
+
+isa_ok($c->meta->get_attribute('bling_bling'), 'My::Custom::Meta::Attr');
+
+
diff --git a/t/050_metaclasses/002_custom_attr_meta_as_role.t b/t/050_metaclasses/002_custom_attr_meta_as_role.t
new file mode 100644 (file)
index 0000000..106f19c
--- /dev/null
@@ -0,0 +1,22 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+use Test::Exception;
+
+;
+
+lives_ok {
+    package MouseX::Attribute::Test;
+    use Mouse::Role;
+} 'creating custom attribute "metarole" is okay';
+
+lives_ok {
+    package Mouse::Meta::Attribute::Custom::Test;
+    use Mouse;
+
+    extends 'Mouse::Meta::Attribute';
+    with 'MouseX::Attribute::Test';
+} 'custom attribute metaclass extending role is okay';
diff --git a/t/100_bugs/001_subtype_quote_bug.t b/t/100_bugs/001_subtype_quote_bug.t
new file mode 100644 (file)
index 0000000..406cafa
--- /dev/null
@@ -0,0 +1,32 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+
+=pod
+
+This is a test for a bug found by Purge on #moose:
+The code:
+
+  subtype Stuff
+    => as Object
+    => where { ... }
+
+will break if the Object:: namespace exists. So the
+solution is to quote 'Object', like so:
+
+  subtype Stuff
+    => as 'Object'
+    => where { ... }
+
+Mouse 0.03 did this, now it doesn't, so all should
+be well from now on.
+
+=cut
+
+{ package Object::Test; }
+
+package Foo;
+::use_ok('Mouse');
diff --git a/t/100_bugs/002_subtype_conflict_bug.t b/t/100_bugs/002_subtype_conflict_bug.t
new file mode 100644 (file)
index 0000000..7ae2de3
--- /dev/null
@@ -0,0 +1,13 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib 't/lib', 'lib';
+
+use Test::More tests => 2;
+
+
+
+use_ok('MyMouseA');
+use_ok('MyMouseB');
\ No newline at end of file
diff --git a/t/100_bugs/003_Moose_Object_error.t b/t/100_bugs/003_Moose_Object_error.t
new file mode 100644 (file)
index 0000000..6dedb64
--- /dev/null
@@ -0,0 +1,10 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use lib 't/lib', 'lib';
+
+use Test::More tests => 1;
+
+use_ok('MyMouseObject');
\ No newline at end of file
diff --git a/t/100_bugs/004_subclass_use_base_bug.t b/t/100_bugs/004_subclass_use_base_bug.t
new file mode 100644 (file)
index 0000000..33a7a44
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+
+
+=pod
+
+This just makes sure that the Bar gets
+a metaclass initialized for it correctly.
+
+=cut
+
+{
+    package Foo;
+    use Mouse;
+
+    package Bar;
+    use strict;
+    use warnings;
+
+    use base 'Foo';
+}
+
+my $bar = Bar->new;
+isa_ok($bar, 'Bar');
+isa_ok($bar, 'Foo');
\ No newline at end of file
diff --git a/t/100_bugs/005_inline_reader_bug.t b/t/100_bugs/005_inline_reader_bug.t
new file mode 100644 (file)
index 0000000..021c3ad
--- /dev/null
@@ -0,0 +1,31 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+use Test::Exception;
+
+
+
+=pod
+
+This was a bug, but it is fixed now. This
+test makes sure it does not creep back in.
+
+=cut
+
+{
+    package Foo;
+    use Mouse;
+
+    ::lives_ok {
+        has 'bar' => (
+            is      => 'ro',
+            isa     => 'Int',
+            lazy    => 1,
+            default => 10,
+        );
+    } '... this didnt die';
+}
+
diff --git a/t/100_bugs/007_reader_precedence_bug.t b/t/100_bugs/007_reader_precedence_bug.t
new file mode 100644 (file)
index 0000000..0f6d608
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+{
+    package Foo;
+    use Mouse;
+    has 'foo' => ( is => 'ro', reader => 'get_foo' );
+}
+
+{
+    my $foo = Foo->new(foo => 10);
+    my $reader = $foo->meta->get_attribute('foo')->reader;
+    is($reader, 'get_foo',
+       'reader => "get_foo" has correct presedence');
+    can_ok($foo, 'get_foo');
+    is($foo->$reader, 10, "Reader works as expected");
+}
+
+
+
+
diff --git a/t/100_bugs/009_augment_recursion_bug.t b/t/100_bugs/009_augment_recursion_bug.t
new file mode 100644 (file)
index 0000000..cd401d9
--- /dev/null
@@ -0,0 +1,49 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    sub foo { 'Foo::foo(' . (inner() || '') . ')' };
+
+    package Bar;
+    use Mouse;
+
+    extends 'Foo';
+
+    package Baz;
+    use Mouse;
+
+    extends 'Foo';
+
+    my $foo_call_counter;
+    augment 'foo' => sub {
+        die "infinite loop on Baz::foo" if $foo_call_counter++ > 1;
+        return 'Baz::foo and ' . Bar->new->foo;
+    };
+}
+
+my $baz = Baz->new();
+isa_ok($baz, 'Baz');
+isa_ok($baz, 'Foo');
+
+=pod
+
+When a subclass which augments foo(), calls a subclass which does not augment
+foo(), there is a chance for some confusion. If Mouse does not realize that
+Bar does not augment foo(), because it is in the call flow of Baz which does,
+then we may have an infinite loop.
+
+=cut
+
+is($baz->foo,
+  'Foo::foo(Baz::foo and Foo::foo())',
+  '... got the right value for 1 augmented subclass calling non-augmented subclass');
+
diff --git a/t/100_bugs/010_immutable_n_default_x2.t b/t/100_bugs/010_immutable_n_default_x2.t
new file mode 100644 (file)
index 0000000..72f6493
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    our $foo_default_called = 0;
+
+    has foo => (
+        is      => 'rw',
+        isa     => 'Str',
+        default => sub { $foo_default_called++; 'foo' },
+    );
+
+    our $bar_default_called = 0;
+
+    has bar => (
+        is      => 'rw',
+        isa     => 'Str',
+        lazy    => 1,
+        default => sub { $bar_default_called++; 'bar' },
+    );
+
+    __PACKAGE__->meta->make_immutable;
+}
+
+my $foo = Foo->new();
+
+is($Foo::foo_default_called, 1, "foo default was only called once during constructor");
+
+$foo->bar();
+
+is($Foo::bar_default_called, 1, "bar default was only called once when lazy attribute is accessed");
diff --git a/t/100_bugs/011_DEMOLISH_eats_exceptions.t b/t/100_bugs/011_DEMOLISH_eats_exceptions.t
new file mode 100644 (file)
index 0000000..c83a2ce
--- /dev/null
@@ -0,0 +1,153 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use FindBin;
+
+use Test::More tests => 144;
+use Test::Exception;
+
+use Mouse::Util::TypeConstraints;
+
+subtype 'FilePath'
+    => as 'Str'
+    # This used to try to _really_ check for a valid Unix or Windows
+    # path, but the regex wasn't quite right, and all we care about
+    # for the tests is that it rejects '/'
+    => where { $_ ne '/' };
+{
+    package Baz;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    has 'path' => (
+        is       => 'ro',
+        isa      => 'FilePath',
+        required => 1,
+    );
+
+    sub BUILD {
+        my ( $self, $params ) = @_;
+        confess $params->{path} . " does not exist"
+            unless -e $params->{path};
+    }
+
+    # Defining this causes the FIRST call to Baz->new w/o param to fail,
+    # if no call to ANY Mouse::Object->new was done before.
+    sub DEMOLISH {
+        my ( $self ) = @_;
+    }
+}
+
+{
+    package Qee;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    has 'path' => (
+        is       => 'ro',
+        isa      => 'FilePath',
+        required => 1,
+    );
+
+    sub BUILD {
+        my ( $self, $params ) = @_;
+        confess $params->{path} . " does not exist"
+            unless -e $params->{path};
+    }
+
+    # Defining this causes the FIRST call to Qee->new w/o param to fail...
+    # if no call to ANY Mouse::Object->new was done before.
+    sub DEMOLISH {
+        my ( $self ) = @_;
+    }
+}
+
+{
+    package Foo;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    has 'path' => (
+        is       => 'ro',
+        isa      => 'FilePath',
+        required => 1,
+    );
+
+    sub BUILD {
+        my ( $self, $params ) = @_;
+        confess $params->{path} . " does not exist"
+            unless -e $params->{path};
+    }
+
+    # Having no DEMOLISH, everything works as expected...
+}
+
+check_em ( 'Baz' );     #     'Baz plain' will fail, aka NO error
+check_em ( 'Qee' );     #     ok
+check_em ( 'Foo' );     #     ok
+
+check_em ( 'Qee' );     #     'Qee plain' will fail, aka NO error
+check_em ( 'Baz' );     #     ok
+check_em ( 'Foo' );     #     ok
+
+check_em ( 'Foo' );     #     ok
+check_em ( 'Baz' );     #     ok !
+check_em ( 'Qee' );     #     ok
+
+
+sub check_em {
+     my ( $pkg ) = @_;
+     my ( %param, $obj );
+
+     # Uncomment to see, that it is really any first call.
+     # Subsequents calls will not fail, aka giving the correct error.
+     {
+         local $@;
+         my $obj = eval { $pkg->new; };
+         ::like( $@, qr/is required/, "... $pkg plain" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+     {
+         local $@;
+         my $obj = eval { $pkg->new(); };
+         ::like( $@, qr/is required/, "... $pkg empty" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+     {
+         local $@;
+         my $obj = eval { $pkg->new ( notanattr => 1 ); };
+         ::like( $@, qr/is required/, "... $pkg undef" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+
+     {
+         local $@;
+         my $obj = eval { $pkg->new ( %param ); };
+         ::like( $@, qr/is required/, "... $pkg undef param" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+     {
+         local $@;
+         my $obj = eval { $pkg->new ( path => '/' ); };
+         ::like( $@, qr/does not pass the type constraint/, "... $pkg root path forbidden" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+     {
+         local $@;
+         my $obj = eval { $pkg->new ( path => '/this_path/does/not_exist' ); };
+         ::like( $@, qr/does not exist/, "... $pkg non existing path" );
+         ::is( $obj, undef, "... the object is undef" );
+     }
+     {
+         local $@;
+         my $obj = eval { $pkg->new ( path => $FindBin::Bin ); };
+         ::is( $@, '', "... $pkg no error" );
+         ::isa_ok( $obj, $pkg );
+         ::isa_ok( $obj, 'Mouse::Object' );
+         ::is( $obj->path, $FindBin::Bin, "... $pkg got the right value" );
+     }
+}
+
+1;
+
diff --git a/t/100_bugs/012_DEMOLISH_eats_mini.t b/t/100_bugs/012_DEMOLISH_eats_mini.t
new file mode 100644 (file)
index 0000000..454a0a5
--- /dev/null
@@ -0,0 +1,100 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 12;
+use Test::Exception;
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'bar' => (
+        is       => 'ro',
+        required => 1,
+    );
+
+    # Defining this causes the FIRST call to Baz->new w/o param to fail,
+    # if no call to ANY Mouse::Object->new was done before.
+    sub DEMOLISH {
+        my ( $self ) = @_;
+        # ... Mouse (kinda) eats exceptions in DESTROY/DEMOLISH";
+    }
+}
+
+{
+    my $obj = eval { Foo->new; };
+    like( $@, qr/is required/, "... Foo plain" );
+    is( $obj, undef, "... the object is undef" );
+}
+
+{
+    package Bar;
+
+    sub new { die "Bar died"; }
+
+    sub DESTROY {
+        die "Vanilla Perl eats exceptions in DESTROY too";
+    }
+}
+
+{
+    my $obj = eval { Bar->new; };
+    like( $@, qr/Bar died/, "... Bar plain" );
+    is( $obj, undef, "... the object is undef" );
+}
+
+{
+    package Baz;
+    use Mouse;
+
+    sub DEMOLISH {
+        $? = 0;
+    }
+}
+
+{
+    local $@ = 42;
+    local $? = 84;
+
+    {
+        Baz->new;
+    }
+
+    is( $@, 42, '$@ is still 42 after object is demolished without dying' );
+    is( $?, 84, '$? is still 84 after object is demolished without dying' );
+
+    local $@ = 0;
+
+    {
+        Baz->new;
+    }
+
+    is( $@, 0, '$@ is still 0 after object is demolished without dying' );
+
+    Baz->meta->make_immutable, redo
+        if Baz->meta->is_mutable
+}
+
+{
+    package Quux;
+    use Mouse;
+
+    sub DEMOLISH {
+        die "foo\n";
+    }
+}
+
+{
+    local $@ = 42;
+
+    eval { my $obj = Quux->new };
+
+    like( $@, qr/foo/, '$@ contains error from demolish when demolish dies' );
+
+    Quux->meta->make_immutable, redo
+        if Quux->meta->is_mutable
+}
+
diff --git a/t/100_bugs/013_lazybuild_required_undef.t b/t/100_bugs/013_lazybuild_required_undef.t
new file mode 100644 (file)
index 0000000..2c07718
--- /dev/null
@@ -0,0 +1,31 @@
+package Foo;
+use Mouse;
+
+## Problem:
+## lazy_build sets required => 1
+## required does not permit setting to undef
+
+## Possible solutions:
+#### remove required => 1
+#### check the attr to see if it accepts Undef (Maybe[], | Undef)
+#### or, make required accept undef and use a predicate test
+
+
+has 'foo' => ( isa => 'Int | Undef', is => 'rw', coerce => 1, lazy_build => 1 );
+has 'bar' => ( isa => 'Int | Undef', is => 'rw', coerce => 1 );
+
+sub _build_foo { undef }
+
+package main;
+use Test::More tests => 4;
+
+ok ( !defined(Foo->new->bar), 'NonLazyBuild: Undef default' );
+ok ( !defined(Foo->new->bar(undef)), 'NonLazyBuild: Undef explicit' );
+
+ok ( !defined(Foo->new->foo), 'LazyBuild: Undef default/lazy_build' );
+
+## This test fails at the time of creation.
+ok ( !defined(Foo->new->foo(undef)), 'LazyBuild: Undef explicit' );
+
+
+1;
similarity index 99%
rename from t/015-demolish.t
rename to t/100_bugs/014_DEMOLISHALL.t
index 123c2d2..f3cb306 100644 (file)
@@ -51,3 +51,4 @@ do {
 };
 
 is_deeply([splice @called], ['Child::DEMOLISHALL', 'Class::DEMOLISHALL', 'Child::DEMOLISH', 'Class::DEMOLISH']);
+
diff --git a/t/100_bugs/016_inheriting_from_roles.t b/t/100_bugs/016_inheriting_from_roles.t
new file mode 100644 (file)
index 0000000..269efcb
--- /dev/null
@@ -0,0 +1,23 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+use Test::Exception;
+
+
+
+{
+    package My::Role;
+    use Mouse::Role;
+}
+{
+    package My::Class;
+    use Mouse;
+
+    ::throws_ok {
+        extends 'My::Role';
+    } qr/You cannot inherit from a Mouse Role \(My\:\:Role\)/,
+    '... this croaks correctly';
+}
diff --git a/t/100_bugs/017_type_constraint_messages.t b/t/100_bugs/017_type_constraint_messages.t
new file mode 100644 (file)
index 0000000..4965eda
--- /dev/null
@@ -0,0 +1,73 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+use Test::Exception;
+
+
+
+# RT #37569
+
+{
+    package MyObject;
+    use Mouse;
+
+    package Foo;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    subtype 'MyArrayRef'
+       => as 'ArrayRef'
+       => where { defined $_->[0] }
+       => message { ref $_ ? "ref: ". ref $_ : 'scalar' }  # stringy
+    ;
+
+    subtype 'MyObjectType'
+       => as 'Object'
+       => where { $_->isa('MyObject') }
+       => message {
+          if ( $_->isa('SomeObject') ) {
+            return 'More detailed error message';
+          }
+          elsif ( blessed $_ ) {
+            return 'Well it is an object';
+          }
+          else {
+            return 'Doh!';
+          }
+       }
+    ;
+
+    type 'NewType'
+       => where { $_->isa('MyObject') }
+       => message { blessed $_ ? 'blessed' : 'scalar' }
+    ;
+
+    has 'obj' => ( is => 'rw', isa => 'MyObjectType' );
+    has 'ar'  => ( is => 'rw', isa => 'MyArrayRef' );
+    has 'nt'  => ( is => 'rw', isa => 'NewType' );
+}
+
+my $foo = Foo->new;
+my $obj = MyObject->new;
+
+throws_ok {
+    $foo->ar( [] );
+}
+qr/Attribute \(ar\) does not pass the type constraint because: ref: ARRAY/,
+    '... got the right error message';
+
+throws_ok {
+    $foo->obj($foo);    # Doh!
+}
+qr/Attribute \(obj\) does not pass the type constraint because: Well it is an object/,
+    '... got the right error message';
+
+throws_ok {
+    $foo->nt($foo);     # scalar
+}
+qr/Attribute \(nt\) does not pass the type constraint because: blessed/,
+    '... got the right error message';
+
similarity index 91%
rename from t/603-octal-defaults.t
rename to t/100_bugs/019_moose_octal_defaults.t
index 8e41449..1766946 100644 (file)
@@ -1,7 +1,5 @@
 #!/usr/bin/env perl
-use Test::More qw(no_plan);
-
-# copied straight out of Moose t/100/019
+use Test::More tests => 10;
 
 {
     my $package = qq{
@@ -35,7 +33,7 @@ use lib qw(lib);
 has id => (
     isa     => 'Str',
     is      => 'ro',
-    default => 017600, 
+    default => 017600,
 );
 
 no Mouse;
@@ -58,7 +56,7 @@ use lib qw(lib);
 has id => (
     isa     => 'Str',
     is      => 'ro',
-    default => 0xFF,  
+    default => 0xFF,
 );
 
 no Mouse;
@@ -81,7 +79,7 @@ use lib qw(lib);
 has id => (
     isa     => 'Str',
     is      => 'ro',
-    default => '0xFF',  
+    default => '0xFF',
 );
 
 no Mouse;
@@ -104,7 +102,7 @@ use lib qw(lib);
 has id => (
     isa     => 'Str',
     is      => 'ro',
-    default => '0 but true',  
+    default => '0 but true',
 );
 
 no Mouse;
diff --git a/t/100_bugs/020_super_recursion.t b/t/100_bugs/020_super_recursion.t
new file mode 100644 (file)
index 0000000..ff691f9
--- /dev/null
@@ -0,0 +1,67 @@
+use strict;
+use warnings;
+
+use Test::More tests => 3;
+
+{
+    package A;
+    use Mouse;
+
+    sub foo {
+        ::BAIL_OUT('A::foo called twice') if $main::seen{'A::foo'}++;
+        return 'a';
+    }
+
+    sub bar {
+        ::BAIL_OUT('A::bar called twice') if $main::seen{'A::bar'}++;
+        return 'a';
+    }
+
+    sub baz {
+        ::BAIL_OUT('A::baz called twice') if $main::seen{'A::baz'}++;
+        return 'a';
+    }
+}
+
+{
+    package B;
+    use Mouse;
+    extends qw(A);
+
+    sub foo {
+        ::BAIL_OUT('B::foo called twice') if $main::seen{'B::foo'}++;
+        return 'b' . super();
+    }
+
+    sub bar {
+        ::BAIL_OUT('B::bar called twice') if $main::seen{'B::bar'}++;
+        return 'b' . ( super() || '' );
+    }
+
+    override baz => sub {
+        ::BAIL_OUT('B::baz called twice') if $main::seen{'B::baz'}++;
+        return 'b' . super();
+    };
+}
+
+{
+    package C;
+    use Mouse;
+    extends qw(B);
+
+    sub foo { return 'c' . ( super() || '' ) }
+
+    override bar => sub {
+        ::BAIL_OUT('C::bar called twice') if $main::seen{'C::bar'}++;
+        return 'c' . super();
+    };
+
+    override baz => sub {
+        ::BAIL_OUT('C::baz called twice') if $main::seen{'C::baz'}++;
+        return 'c' . super();
+    };
+}
+
+is( C->new->foo, 'c' );
+is( C->new->bar, 'cb' );
+is( C->new->baz, 'cba' );
diff --git a/t/100_bugs/021_DEMOLISHALL_shortcutted.t b/t/100_bugs/021_DEMOLISHALL_shortcutted.t
new file mode 100644 (file)
index 0000000..ba1833e
--- /dev/null
@@ -0,0 +1,32 @@
+## This test ensures that sub DEMOLISHALL fires even if there is no sub DEMOLISH
+## Currently fails because of a bad optimization in DESTROY
+## Feb 12, 2009 -- Evan Carroll me@evancarroll.com
+package Role::DemolishAll;
+use Mouse::Role;
+our $ok = 0;
+
+sub BUILD { $ok = 0 };
+after 'DEMOLISHALL' => sub { $Role::DemolishAll::ok++ };
+
+package DemolishAll::WithoutDemolish;
+use Mouse;
+with 'Role::DemolishAll';
+
+package DemolishAll::WithDemolish;
+use Mouse;
+with 'Role::DemolishAll';
+sub DEMOLISH {};
+
+
+package main;
+use Test::More tests => 2;
+
+my $m = DemolishAll::WithDemolish->new;
+undef $m;
+is ( $Role::DemolishAll::ok, 1, 'DemolishAll w/ explicit DEMOLISH sub' );
+
+$m = DemolishAll::WithoutDemolish->new;
+undef $m;
+is ( $Role::DemolishAll::ok, 1, 'DemolishAll wo/ explicit DEMOLISH sub' );
+
+1;
diff --git a/t/100_bugs/022_role_caller.t b/t/100_bugs/022_role_caller.t
new file mode 100644 (file)
index 0000000..6df661d
--- /dev/null
@@ -0,0 +1,25 @@
+package MyRole;
+
+use Mouse::Role;
+
+sub foo { return (caller(0))[3] }
+
+no Mouse::Role;
+
+package MyClass1; use Mouse; with 'MyRole'; no Mouse;
+package MyClass2; use Mouse; with 'MyRole'; no Mouse;
+
+package main;
+
+use Test::More tests => 4;
+
+{
+  local $TODO = 'Role composition does not clone methods yet';
+  is(MyClass1->foo, 'MyClass1::foo',
+    'method from role has correct name in caller()');
+  is(MyClass2->foo, 'MyClass2::foo',
+    'method from role has correct name in caller()');
+}
+
+isnt(MyClass1->foo, "MyClass2::foo", "role method is not confused with other class" );
+isnt(MyClass2->foo, "MyClass1::foo", "role method is not confused with other class" );
diff --git a/t/100_bugs/025_universal_methods_wrappable.t b/t/100_bugs/025_universal_methods_wrappable.t
new file mode 100644 (file)
index 0000000..c995172
--- /dev/null
@@ -0,0 +1,29 @@
+use strict;
+use warnings;
+
+use Test::Exception;
+use Test::More tests => 2;
+
+{
+
+    package FakeBar;
+    use Mouse::Role;
+
+    around isa => sub {
+        my ( $orig, $self, $v ) = @_;
+        return 1 if $v eq 'Bar';
+        return $orig->( $self, $v );
+    };
+
+    package Foo;
+    use Mouse;
+
+    use Test::More; # for $TODO
+
+    local $TODO = 'UNIVERSAL methods should be wrappable';
+
+    ::lives_ok { with 'FakeBar' } 'applied role';
+
+    my $foo = Foo->new;
+    ::isa_ok $foo, 'Bar';
+}
diff --git a/t/100_bugs/026_create_anon_recursion.t b/t/100_bugs/026_create_anon_recursion.t
new file mode 100644 (file)
index 0000000..c1f9159
--- /dev/null
@@ -0,0 +1,23 @@
+use strict;
+use warnings;
+
+use Test::More tests => 1;
+use Test::Exception;
+
+use Mouse::Meta::Class;
+
+$SIG{__WARN__} = sub { die if shift =~ /recurs/ };
+
+TODO:
+{
+#    local $TODO
+#        = 'Loading Mouse::Meta::Class without loading Mouse.pm causes weird problems';
+
+    my $meta;
+    lives_ok {
+        $meta = Mouse::Meta::Class->create_anon_class(
+            superclasses => [ 'Mouse::Object', ],
+        );
+    }
+    'Class is created successfully';
+}
diff --git a/t/100_bugs/027_constructor_object_overload.t b/t/100_bugs/027_constructor_object_overload.t
new file mode 100644 (file)
index 0000000..0dfba1c
--- /dev/null
@@ -0,0 +1,19 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Test::More tests => 1;
+
+{
+    package Foo;
+
+    use Mouse;
+
+    use overload '""' => sub {''};
+
+    sub bug { 'plenty' }
+
+    __PACKAGE__->meta->make_immutable;
+}
+
+ok(Foo->new()->bug(), 'call constructor on object reference with overloading');
+
diff --git a/t/100_bugs/failing/006_handles_foreign_class_bug.t b/t/100_bugs/failing/006_handles_foreign_class_bug.t
new file mode 100644 (file)
index 0000000..c48d9d5
--- /dev/null
@@ -0,0 +1,111 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 15;
+use Test::Exception;
+
+{
+    package Foo;
+
+    sub new {
+        bless({}, 'Foo')
+    }
+
+    sub a { 'Foo::a' }
+}
+
+{
+    package Bar;
+    use Mouse;
+
+    ::lives_ok {
+        has 'baz' => (
+            is      => 'ro',
+            isa     => 'Foo',
+            lazy    => 1,
+            default => sub { Foo->new() },
+            handles => qr/^a$/,
+        );
+    } '... can create the attribute with delegations';
+
+}
+
+my $bar;
+lives_ok {
+    $bar = Bar->new;
+} '... created the object ok';
+isa_ok($bar, 'Bar');
+
+is($bar->a, 'Foo::a', '... got the right delgated value');
+
+my @w;
+$SIG{__WARN__} = sub { push @w, "@_" };
+{
+    package Baz;
+    use Mouse;
+
+    ::lives_ok {
+        has 'bar' => (
+            is      => 'ro',
+            isa     => 'Foo',
+            lazy    => 1,
+            default => sub { Foo->new() },
+            handles => qr/.*/,
+        );
+    } '... can create the attribute with delegations';
+
+}
+
+is(@w, 0, "no warnings");
+
+
+my $baz;
+lives_ok {
+    $baz = Baz->new;
+} '... created the object ok';
+isa_ok($baz, 'Baz');
+
+is($baz->a, 'Foo::a', '... got the right delgated value');
+
+
+
+
+
+@w = ();
+
+{
+    package Blart;
+    use Mouse;
+
+    ::lives_ok {
+        has 'bar' => (
+            is      => 'ro',
+            isa     => 'Foo',
+            lazy    => 1,
+            default => sub { Foo->new() },
+            handles => [qw(a new)],
+        );
+    } '... can create the attribute with delegations';
+
+}
+
+{
+    local $TODO = "warning not yet implemented";
+
+    is(@w, 1, "one warning");
+    like($w[0], qr/not delegating.*new/i, "warned");
+}
+
+
+
+my $blart;
+lives_ok {
+    $blart = Blart->new;
+} '... created the object ok';
+isa_ok($blart, 'Blart');
+
+is($blart->a, 'Foo::a', '... got the right delgated value');
+
+
diff --git a/t/100_bugs/failing/018_immutable_metaclass_does_role.t b/t/100_bugs/failing/018_immutable_metaclass_does_role.t
new file mode 100644 (file)
index 0000000..4f4b03f
--- /dev/null
@@ -0,0 +1,92 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 36;
+use Test::Exception;
+
+
+
+BEGIN {
+    package MyRole;
+    use Mouse::Role;
+
+    requires 'foo';
+
+    package MyMetaclass;
+    use Mouse qw(extends with);
+    extends 'Mouse::Meta::Class';
+       with 'MyRole';
+
+    sub foo { 'i am foo' }
+}
+
+{
+    package MyClass;
+    use metaclass ('MyMetaclass');
+    use Mouse;
+}
+
+my $mc = MyMetaclass->initialize('MyClass');
+isa_ok($mc, 'MyMetaclass');
+
+ok($mc->meta->does_role('MyRole'), '... the metaclass does the role');
+
+is(MyClass->meta, $mc, '... these metas are the same thing');
+is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
+
+my $a = MyClass->new;
+ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
+ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
+
+lives_ok {
+    MyClass->meta->make_immutable;
+} '... make MyClass immutable okay';
+
+is(MyClass->meta, $mc, '... these metas are still the same thing');
+is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
+
+ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
+ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
+
+lives_ok {
+    MyClass->meta->make_mutable;
+} '... make MyClass mutable okay';
+
+is(MyClass->meta, $mc, '... these metas are still the same thing');
+is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
+
+ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
+ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
+
+lives_ok {
+    MyMetaclass->meta->make_immutable;
+} '... make MyClass immutable okay';
+
+is(MyClass->meta, $mc, '... these metas are still the same thing');
+is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
+
+ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
+ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
+
+lives_ok {
+    MyClass->meta->make_immutable;
+} '... make MyClass immutable okay';
+
+is(MyClass->meta, $mc, '... these metas are still the same thing');
+is(MyClass->meta->meta, $mc->meta, '... these meta-metas are the same thing');
+
+ok( $a->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( $a->meta->foo, 'i am foo', '... foo method returns expected value' );
+ok( MyClass->meta->meta->does_role('MyRole'), 'metaclass does MyRole' );
+is( MyClass->meta->foo, 'i am foo', '... foo method returns expected value' );
+
diff --git a/t/100_bugs/failing/023_DEMOLISH_fails_without_metaclass.t b/t/100_bugs/failing/023_DEMOLISH_fails_without_metaclass.t
new file mode 100644 (file)
index 0000000..a038456
--- /dev/null
@@ -0,0 +1,34 @@
+use strict;
+use warnings;
+
+use Test::More tests => 2;
+use Test::Exception;
+
+{
+    package MyClass;
+    use Mouse;
+
+    sub DEMOLISH { }
+}
+
+my $object = MyClass->new;
+
+# Removing the metaclass simulates the case where the metaclass object
+# goes out of scope _before_ the object itself, which under normal
+# circumstances only happens during global destruction.
+Class::MOP::remove_metaclass_by_name('MyClass');
+
+# The bug happened when DEMOLISHALL called
+# Class::MOP::class_of($object) and did not get a metaclass object
+# back.
+lives_ok { $object->DESTROY }
+'can call DESTROY on an object without a metaclass object in the CMOP cache';
+
+
+MyClass->meta->make_immutable;
+Class::MOP::remove_metaclass_by_name('MyClass');
+
+# The bug didn't manifest for immutable objects, but this test should
+# help us prevent it happening in the future.
+lives_ok { $object->DESTROY }
+'can call DESTROY on an object without a metaclass object in the CMOP cache (immutable version)';
diff --git a/t/100_bugs/failing/024_anon_method_metaclass.t b/t/100_bugs/failing/024_anon_method_metaclass.t
new file mode 100644 (file)
index 0000000..e8f639b
--- /dev/null
@@ -0,0 +1,48 @@
+use strict;
+use warnings;
+use Test::More tests => 10;
+
+{
+    package Ball;
+    use Mouse;
+}
+
+{
+    package Arbitrary::Roll;
+    use Mouse::Role;
+}
+
+my $method_meta = Mouse::Meta::Class->create_anon_class(
+    superclasses => ['Mouse::Meta::Method'],
+    roles        => ['Arbitrary::Roll'],
+);
+
+# For comparing identity without actually keeping $original_meta around
+my $original_meta = "$method_meta";
+
+my $method_class = $method_meta->name;
+
+my $method_object = $method_class->wrap(
+    sub {'ok'},
+    associated_metaclass => Ball->meta,
+    package_name         => 'Ball',
+    name                 => 'bounce',
+);
+
+Ball->meta->add_method( bounce => $method_object );
+
+for ( 1, 2 ) {
+    is( Ball->bounce, 'ok', "method still exists on Ball" );
+    is( Ball->meta->get_method('bounce')->meta->name, $method_class,
+        "method's package still exists" );
+
+    is( Ball->meta->get_method('bounce'), $method_object,
+        'original method object is preserved' );
+
+    is( Ball->meta->get_method('bounce')->meta . '', $original_meta,
+        "method's metaclass still exists" );
+    ok( Ball->meta->get_method('bounce')->meta->does_role('Arbitrary::Roll'),
+        "method still does Arbitrary::Roll" );
+
+    undef $method_meta;
+}
index 1c561ae..2c89b94 100644 (file)
@@ -6,6 +6,8 @@ use warnings;
 use Test::More tests => 15;
 use Test::Exception;
 
+use lib 't/lib';
+use Test::Mouse; # Mouse::Meta::Module->version
 use Mouse::Meta::Role;
 
 
diff --git a/t/300_immutable/004_inlined_constructors_n_types.t b/t/300_immutable/004_inlined_constructors_n_types.t
new file mode 100755 (executable)
index 0000000..afee861
--- /dev/null
@@ -0,0 +1,63 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 10;
+use Test::Exception;
+
+=pod
+
+This tests to make sure that the inlined constructor
+has all the type constraints in order, even in the
+cases when there is no type constraint available, such
+as with a Class::MOP::Attribute object.
+
+=cut
+
+{
+    package Foo;
+    use Mouse;
+    use Mouse::Util::TypeConstraints;
+
+    coerce 'Int' => from 'Str' => via { length $_ ? $_ : 69 };
+
+    has 'foo' => (is => 'rw', isa => 'Int');
+    has 'baz' => (is => 'rw', isa => 'Int');
+    has 'zot' => (is => 'rw', isa => 'Int', init_arg => undef);
+    has 'moo' => (is => 'rw', isa => 'Int', coerce => 1, default => '', required => 1);
+    has 'boo' => (is => 'rw', isa => 'Int', coerce => 1, builder => '_build_boo', required => 1);
+
+    sub _build_boo { '' }
+
+    Foo->meta->add_attribute(
+        Mouse::Meta::Attribute->new(
+            'bar' => (
+                accessor => 'bar',
+            )
+        )
+    );
+}
+
+for (1..2) {
+    my $is_immutable   = Foo->meta->is_immutable;
+    my $mutable_string = $is_immutable ? 'immutable' : 'mutable';
+    lives_ok {
+        my $f = Foo->new(foo => 10, bar => "Hello World", baz => 10, zot => 4);
+        is($f->moo, 69, "Type coercion works as expected on default ($mutable_string)");
+        is($f->boo, 69, "Type coercion works as expected on builder ($mutable_string)");
+    } "... this passes the constuctor correctly ($mutable_string)";
+
+    lives_ok {
+        Foo->new(foo => 10, bar => "Hello World", baz => 10, zot => "not an int");
+    } "... the constructor doesn't care about 'zot' ($mutable_string)";
+
+    dies_ok {
+        Foo->new(foo => "Hello World", bar => 100, baz => "Hello World");
+    } "... this fails the constuctor correctly ($mutable_string)";
+
+    Foo->meta->make_immutable(debug => 0) unless $is_immutable;
+}
+
+
+
diff --git a/t/300_immutable/005_multiple_demolish_inline.t b/t/300_immutable/005_multiple_demolish_inline.t
new file mode 100755 (executable)
index 0000000..7b70107
--- /dev/null
@@ -0,0 +1,46 @@
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+
+use Test::More tests => 5;
+use Test::Exception;
+
+
+
+{
+    package Foo;
+    use Mouse;
+
+    has 'foo' => (is => 'rw', isa => 'Int');
+
+    sub DEMOLISH { }
+}
+
+{
+    package Bar;
+    use Mouse;
+
+    extends qw(Foo);
+    has 'bar' => (is => 'rw', isa => 'Int');
+
+    sub DEMOLISH { }
+}
+
+lives_ok {
+    Bar->new();
+} 'Bar->new()';
+
+lives_ok {
+    Bar->meta->make_immutable;
+} 'Bar->meta->make_immutable';
+
+is( Bar->meta->get_method('DESTROY')->package_name, 'Bar',
+    'Bar has a DESTROY method in the Bar class (not inherited)' );
+
+lives_ok {
+    Foo->meta->make_immutable;
+} 'Foo->meta->make_immutable';
+
+is( Foo->meta->get_method('DESTROY')->package_name, 'Foo',
+    'Foo has a DESTROY method in the Bar class (not inherited)' );
index 76e5bcb..7d561e0 100644 (file)
@@ -1,7 +1,7 @@
 use strict;
 use warnings;
 use Mouse ();
-use Test::More tests => 19;
+use Test::More tests => 23;
 use Test::Exception;
 
 # error handling
@@ -83,3 +83,19 @@ ok !$anon_pkg1->can('meta');
 
 ok $anon_pkg2->can('meta'), 'cache => 1 makes it immortal';
 
+my $obj;
+{
+    my $anon = Mouse::Meta::Class->create_anon_class(superclasses => ['Mouse::Object']);
+    lives_ok{ $anon->make_immutable() } 'make anon class immutable';
+    $obj = $anon->name->new();
+}
+
+SKIP:{
+    skip 'Moose has a bug', 3 if 'Mouse' eq 'Moose';
+
+    isa_ok $obj, 'Mouse::Object';
+    can_ok $obj, 'meta';
+    lives_and{
+        isa_ok $obj->meta, 'Mouse::Meta::Class';
+    };
+}
index b912815..c60e45c 100644 (file)
@@ -16,7 +16,7 @@ use Test::More tests => 18;
 eval {
     Foo->new( bar => +{} );
 };
-like($@, qr/^Attribute \(bar\) does not pass the type constraint because: Validation failed for 'Str\|Baz\|Undef' failed with value HASH\(\w+\)/, 'type constraint and coercion failed')
+like($@, qr/^Attribute \(bar\) does not pass the type constraint because: Validation failed for 'Baz\|Str\|Undef' failed with value HASH\(\w+\)/, 'type constraint and coercion failed')
     or diag "\$@='$@'";
 
 eval {
@@ -86,7 +86,7 @@ is $foo->foo, 'Name', 'foo is Name';
 }
 
 eval { Funk->new( foo => 'aaa' ) };
-like $@, qr/Attribute \(foo\) does not pass the type constraint because: Validation failed for 'Type3\|KLASS\|Undef' failed with value aaa/;
+like $@, qr/Attribute \(foo\) does not pass the type constraint because: Validation failed for 'KLASS\|Type3\|Undef' failed with value aaa/;
 
 my $k = Funk->new;
 ok $k, 'got an object 4';
diff --git a/t/990_deprecated/001-export_to_level.t b/t/990_deprecated/001-export_to_level.t
new file mode 100644 (file)
index 0000000..e14ab10
--- /dev/null
@@ -0,0 +1,27 @@
+#!/usr/bin/perl
+use strict;
+use warnings;
+use Test::More tests => 3;
+
+BEGIN{
+    package Foo;
+    use Mouse;
+
+    sub import{
+        shift;
+        Mouse->export_to_level(1, @_);
+    }
+    $INC{'Foo.pm'}++;
+}
+
+package A;
+use Test::More;
+
+use Foo qw(has);
+
+ok defined(&has), "export_to_level (DEPRECATED)";
+
+
+ok!defined(&Bar::has), "export (DEPRECATED)";
+Mouse->export('Bar', 'has');
+ok defined(&Bar::has), "export (DEPRECATED)";
diff --git a/t/lib/Bar.pm b/t/lib/Bar.pm
new file mode 100755 (executable)
index 0000000..c9d0ab0
--- /dev/null
@@ -0,0 +1,10 @@
+
+package Bar;
+use Mouse;
+use Mouse::Util::TypeConstraints;
+
+type Baz => where { 1 };
+
+subtype Bling => as Baz => where { 1 };
+
+1;
\ No newline at end of file
diff --git a/t/lib/Foo.pm b/t/lib/Foo.pm
new file mode 100755 (executable)
index 0000000..6cbac0f
--- /dev/null
@@ -0,0 +1,7 @@
+
+package Foo;
+use Mouse;
+
+has 'bar' => (is => 'rw');
+
+1;
\ No newline at end of file
diff --git a/t/lib/MyMouseA.pm b/t/lib/MyMouseA.pm
new file mode 100644 (file)
index 0000000..10ddc13
--- /dev/null
@@ -0,0 +1,7 @@
+package MyMouseA;
+
+use Mouse;
+
+has 'b' => (is => 'rw', isa => 'MyMouseB');
+
+1;
\ No newline at end of file
diff --git a/t/lib/MyMouseB.pm b/t/lib/MyMouseB.pm
new file mode 100644 (file)
index 0000000..542ae00
--- /dev/null
@@ -0,0 +1,5 @@
+package MyMouseB;
+
+use Mouse;
+
+1;
\ No newline at end of file
diff --git a/t/lib/MyMouseObject.pm b/t/lib/MyMouseObject.pm
new file mode 100644 (file)
index 0000000..d60a6f4
--- /dev/null
@@ -0,0 +1,7 @@
+package MyMouseObject;
+
+use strict;
+use warnings;
+use base 'Mouse::Object';
+
+1;
\ No newline at end of file
index 8d219dd..93b4946 100644 (file)
@@ -55,7 +55,49 @@ sub has_attribute_ok ($$;$) {
 
 # Moose compatible methods/functions
 
-package Mouse::Util::TypeConstraints;
+package
+    Mouse::Meta::Module;
+
+sub version   { no strict 'refs'; ${shift->name.'::VERSION'}   }
+sub authority { no strict 'refs'; ${shift->name.'::AUTHORITY'} }
+sub identifier {
+    my $self = shift;
+    return join '-' => (
+       $self->name,
+        ($self->version   || ()),
+        ($self->authority || ()),
+    );
+}
+
+package
+    Mouse::Meta::Role;
+
+for my $modifier_type (qw/before after around/) {
+    my $modifier = "${modifier_type}_method_modifiers";
+    my $has_method_modifiers = sub{
+        my($self, $method_name) = @_;
+        my $m = $self->{$modifier}->{$method_name};
+        return $m && @{$m} != 0;
+    };
+
+    no strict 'refs';
+    *{ 'has_' . $modifier_type . '_method_modifiers' } = $has_method_modifiers;
+}
+
+
+sub has_override_method_modifier {
+    my ($self, $method_name) = @_;
+    return exists $self->{override_method_modifiers}->{$method_name};
+}
+
+sub get_method_modifier_list {
+    my($self, $modifier_type) = @_;
+
+    return keys %{ $self->{$modifier_type . '_method_modifiers'} };
+}
+
+package
+    Mouse::Util::TypeConstraints;
 
 use Mouse::Util::TypeConstraints ();
 
@@ -63,7 +105,7 @@ sub export_type_constraints_as_functions { # TEST ONLY
     my $into = caller;
 
     foreach my $type( list_all_type_constraints() ) {
-        my $tc = find_type_constraint($type)->{_compiled_type_constraint};
+        my $tc = find_type_constraint($type)->_compiled_type_constraint;
         my $as = $into . '::' . $type;
 
         no strict 'refs';
@@ -72,11 +114,15 @@ sub export_type_constraints_as_functions { # TEST ONLY
     return;
 }
 
-package Mouse::Meta::Attribute;
+package
+    Mouse::Meta::Attribute;
 
 sub applied_traits{            $_[0]->{traits} } # TEST ONLY
 sub has_applied_traits{ exists $_[0]->{traits} } # TEST ONLY
 
+sub has_documentation{ exists $_[0]->{documentation} } # TEST ONLY
+sub documentation{            $_[0]->{documentation} } # TEST ONLY
+
 1;
 
 __END__