{"id":581,"date":"2015-09-27T16:13:37","date_gmt":"2015-09-27T21:13:37","guid":{"rendered":"http:\/\/www.aibistin.com\/?p=581"},"modified":"2023-03-12T16:16:08","modified_gmt":"2023-03-12T20:16:08","slug":"using-moo-continued","status":"publish","type":"post","link":"https:\/\/www.aibistin.com\/?p=581","title":{"rendered":"Using Moo (continued)"},"content":{"rendered":"<p>Continuing from\u00a0my last post \u00a0<a href=\"http:\/\/www.aibistin.com\/?p=568\">Using Moo<\/a>,\u00a0 I will add a little more functionality to my File::Info class. \u00a0At first I was going to subclass it, but then I decided to use\u00a0\u00a0<a href=\"https:\/\/metacpan.org\/pod\/Moo::Role\">Moo::Role<\/a>\u00a0instead. <a href=\"https:\/\/metacpan.org\/pod\/Moo::Role\">Moo::Role<\/a>\u00a0which is based on\u00a0<a href=\"https:\/\/metacpan.org\/pod\/Role::Tiny\">Role::Tiny<\/a>. Roles are a really convenient way of adding extra functionality to your class.<\/p>\n<pre class=\"lang:perl decode:true\" title=\"File::Info with some more attributes and roles.\">package File::Info;\nuse Scalar::Util qw\/blessed looks_like_number\/;\nuse Moo;\nuse v5.16;\n#-------------------------------------------------------------------------------\n#  Base class to provide some useful information about a given file.\n#-------------------------------------------------------------------------------\nuse Path::Tiny;\nuse Carp;\nuse File::stat;\nuse namespace::clean;    # clean imported functions from your namespace.\n#-------------------------------------------------------------------------------\n#  Attributes\n#-------------------------------------------------------------------------------\nhas file =&gt; (\n    is  =&gt; 'ro',\n    isa =&gt; sub {\n        #--- Because I like Path::Tiny\n        Carp::croak(\n            qq{'in_file' needs to be an existing file and a Path::Tiny Object!})\n          unless ( ref $_[0] eq 'Path::Tiny' and $_[0]-&gt;exists );\n    },\n    coerce =&gt; sub {\n        return $_[0]\n          if ( ref $_[0] eq 'Path::Tiny' );\n        return path( $_[0] );\n    },\n    required =&gt; 1\n);\n\nhas file_stat =&gt; (\n    is  =&gt; 'rwp',\n    isa =&gt; sub {\n        Carp::croak(qq{$_[0] is not a File::stat::stat!})\n          unless ( $_[0] and ( ref $_[0] ) );\n    },\n);\n\nhas size_bytes =&gt; (\n    is  =&gt; 'rwp',\n    isa =&gt; sub {\n        Carp::croak(qq{'bytes' value $_[0] is not numeric!})\n          unless ( looks_like_number( $_[0] ) );\n    },\n    lazy    =&gt; 1,\n    builder =&gt; sub {\n        $_[0]-&gt;file_stat-&gt;size;\n    },\n);\n\nhas mod_time_seconds =&gt; (\n    is  =&gt; 'ro',\n    isa =&gt; sub {\n        Carp::croak(qq{$_[0] is not a number!})\n          unless ( looks_like_number( $_[0] ) );\n    },\n    lazy    =&gt; 1,\n    builder =&gt; sub {\n        $_[0]-&gt;file_stat-&gt;mtime;\n    },\n);\n\n#--- Moo roles to print bytes\/seconds in a more readable form\nwith qw{MakeBytesPretty MakeSecondsPretty};\n\n#-------------------------------------------------------------------------------\n#  Builders and Triggers\n#-------------------------------------------------------------------------------\nsub BUILD {\n    my ($self) = @_;\n    $self-&gt;_set_file_stat( File::stat::stat( $self-&gt;file ) )\n      or Carp::croak( qq{File stat failed to get the file stat for, }\n          . $self-&gt;file\n          . qq{!: $!} );\n}\n1;<\/pre>\n<p>And the Roles&#8230;<\/p>\n<pre class=\"lang:perl decode:true\" title=\"Moo Role MakeBytesPretty with method 'make_file_size_pretty'\">package MakeBytesPretty;\nuse Moo::Role; #--- Makes this a role, and not a class. \nuse v5.16;\nuse namespace::clean;\n\nrequires 'size_bytes'; #-- Make sure that this attribute exists somewhare. \n#-------------------------------------------------------------------------------\n#  Moo role to prints bytes a little more pretty.\n#-------------------------------------------------------------------------------\nsub make_file_size_pretty {\n    my ($self) = @_;\n    my $bytes = $self-&gt;size_bytes;\n\n    return ( not $bytes )\n      ? 0\n      : ( $bytes &lt;= 1024 ) ? sprintf( \"%0.02f\", $bytes )\n      . \" Bytes\"\n\n      : ( $bytes &lt;= 1048576 ) ? sprintf( \"%0.02f\", ( $bytes \/ 1024 ) ) . \" KB\"\n\n      : ( $bytes &lt;= 1073741824 )\n      ? sprintf( \"%0.02f\", ( $bytes \/ 1048576 ) ) . \" MB\"\n\n      : ( $bytes &lt;= 1099511627776 )\n      ? sprintf( \"%0.02f\", ( $bytes \/ 1073741824 ) ) . \" GB\"\n\n      : ( $bytes &lt;= 1125899906842624 )\n      ? sprintf( \"%0.02f\", ( $bytes \/ 1099511627776 ) ) . \" TB\"\n\n      : ( $bytes &lt;= 1152921504606846976 )\n\n      ? sprintf( \"%0.02f\", ( $bytes \/ 1125899906842624 ) ) . \" PB\"    # Petabyte\n\n      : sprintf( \"%0.02f\", ( $bytes \/ 1152921504606846976 ) ) . \" EB\" # Exabyte\n}\n#-------------------------------------------------------------------------------\n#  End\n#-------------------------------------------------------------------------------\n1;<\/pre>\n<pre class=\"lang:perl decode:true\" title=\"Moo Role MakeSecondsPretty with method 'make_mod_time_pretty'\">package MakeSecondsPretty;\nuse Moo::Role;\nuse v5.16;\nuse namespace::clean;\n\nrequires 'mod_time_seconds';\n#-------------------------------------------------------------------------------\n#  Moo role to print time 'Seconds' in a more pretty way.\n#-------------------------------------------------------------------------------\nsub make_mod_time_pretty {\n    my ( $self ) = @_;\n    my $seconds = $self-&gt;mod_time_seconds;\n    return ( not $seconds )\n      ? 0\n      : ( $seconds &lt;= 60 ) ? sprintf( \"%0.02f\", $seconds )\n      . \" Seconds\"\n\n      : ( $seconds &lt;= 3600 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 60 ) ) . \" Minutes\"\n\n      : ( $seconds &lt;= 3600 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 3600 ) ) . \" Hours\"\n\n      : ( $seconds &lt;= 86400 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 86400 ) ) . \" Days\"\n\n      : sprintf( \"%0.02f\", ( $seconds \/ 604800 ) ) . \" Weeks\";\n}\n#-------------------------------------------------------------------------------\n#  End\n#-------------------------------------------------------------------------------\n1;<\/pre>\n<p>As before, it&#8217;s a good idea to write a test script to make sure all is ok. This test script isn&#8217;t overly thorough, but it&#8217;s good enough for this demonstration.<\/p>\n<pre class=\"lang:perl decode:true\" title=\"file_test.t for testing File::Info\">use Test::Modern;    #-- I wonder will it be modern in 10 years time??\nuse FindBin qw($Bin);\nuse lib qq{$Bin\/..\/lib};    #-- Where File::Info is located.\nuse File::Info;\nuse Scalar::Util qw\/blessed looks_like_number\/;\n\nmy $fi = object_ok(\n    sub { File::Info-&gt;new( file =&gt; qq{IMAG0029.jpg} ) },\n    '$fi',\n    isa =&gt; [qw(Moo::Object )],\n    can =&gt; [\n        qw(  file file_stat size_bytes mod_time_seconds make_file_size_pretty\n          make_mod_time_pretty )\n    ],\n    clean =&gt; 1,\n    more  =&gt; sub {\n        my $file_info_obj = shift;\n        isa_ok( $file_info_obj-&gt;file, 'Path::Tiny',\n            q{File::Info 'file' is a Path::Tiny object.} );\n        is( $file_info_obj-&gt;file-&gt;basename,\n            qq{IMAG0029.jpg},\n            q{File::Info 'file' has the correct file basename.} );\n        isa_ok( $file_info_obj-&gt;file_stat, q{File::stat},\n            qq{File::Info 'file_stat' is a 'File::stat'.} );\n        like( $file_info_obj-&gt;file_stat-&gt;size,\n            qr\/^[0-9]+$\/,\n            q{File::Info 'file_stat-&gt;size' returns a numeric file size.} );\n        like( $file_info_obj-&gt;size_bytes,\n            qr\/^[0-9]+$\/, qq{File::Info 'size_bytes' is numeric.} );\n        like(\n            $file_info_obj-&gt;make_file_size_pretty,\n            qr\/^d+?.d+ (bytes|KB|MB|GB|TB|PB|EB)$\/, qq{\n            File::Info bytes 'make_file_size_pretty' \n            prints a file size followed by the size Unit.}\n        );\n        like( $file_info_obj-&gt;mod_time_seconds, qr\/^d+$\/,\n            qq{File::Info 'mod_time_seconds' prints the file modification time.}\n        );\n        like(\n            $file_info_obj-&gt;make_mod_time_pretty,\n            qr\/^d+?.d+ (Seconds|Minutes|Hours|Days|Weeks)$\/, qq{\n            File::Info bytes 'make_mod_timepretty' \n            prints the file mod time followed by the time Unit.}\n        );\n    },\n);\n\ndone_testing();<\/pre>\n<p>Run the test script &#8230;<\/p>\n<pre class=\"theme:solarized-dark nums:false lang:default decode:true\" title=\"Run file_test.t\">Moo &gt; prove -v t\/test_file.t \nt\/test_file.t .. \n    # Subtest: $fi ok\n    ok 1 - instantiate $fi\n    ok 2 - $fi is blessed\n    ok 3 - '$fi' isa 'Moo::Object'\n    ok 4 - File::Info-&gt;can(...)\n    ok 5 - File::Info contains no imported functions\n        # Subtest: more tests for $fi\n        ok 1 - 'File::Info 'file' is a Path::Tiny object.' isa 'Path::Tiny'\n        ok 2 - File::Info 'file' has the correct file basename.\n        ok 3 - 'File::Info 'file_stat' is a 'File::stat'.' isa 'File::stat'\n        ok 4 - File::Info 'file_stat-&gt;size' returns a numeric file size.\n        ok 5 - File::Info 'size_bytes' is numeric.\n        ok 6 - \n        #             File::Info bytes 'make_file_size_pretty' \n        #             prints a file size followed by the size Unit.\n        ok 7 - File::Info 'mod_time_seconds' prints the file modification time.\n        ok 8 - \n        #             File::Info bytes 'make_mod_timepretty' \n        #             prints the file mod time followed by the time Unit.\n        ok 9 - no exception thrown by additional tests\n        1..9\n    ok 6 - more tests for $fi\n    1..6\nok 1 - $fi ok\nok 2 - no (unexpected) warnings (via done_testing)\n1..2\nok\nAll tests successful.\nFiles=1, Tests=2,  1 wallclock secs ( 0.03 usr  0.00 sys +  0.08 cusr  0.02 csys =  0.13 CPU)\nResult: PASS\n[17:10 - 1.06]\n<\/pre>\n<p>Ok. Looks fine for now.<\/p>\n<p>However, I would like to change the module around a little. I want to be able to print the size and age of a given file in a somewhat readable fashion.<\/p>\n<p>I removed the &#8216;mod_time_seconds&#8217; attribute\u00a0and replaced it with &#8216;mod_time_moment&#8217; . This attribute will store the file&#8217;s modification time as a Time::Moment object. This will add a little more versatility to the module.\u00a0<a href=\"https:\/\/metacpan.org\/pod\/Time::Moment\">Time::Moment<\/a>\u00a0is a handy and fast time manipulation module that&#8217;s relatively new on the block. Other modules that I could have used would be,\u00a0<a href=\"https:\/\/metacpan.org\/pod\/Time::Piece\">Time::Piece<\/a>\u00a0which I use a lot also, and \u00a0the venerable\u00a0<a href=\"https:\/\/metacpan.org\/pod\/DateTime\">DateTime<\/a>\u00a0module, which is excellent for serious date calculations involving time zones, durations etc.<\/p>\n<p>I\u00a0also\u00a0added two new methods to get the time since the last file modification. One will return\u00a0the elapsed time in seconds and the other will return\u00a0the elapsed time in a more readable form.<\/p>\n<p>Here is the updated File::Info module&#8230;<\/p>\n<pre class=\"lang:perl decode:true\" title=\"File::Info module with updates.\">package File::Info;\nuse Scalar::Util qw\/blessed looks_like_number\/;\nuse Moo;\nuse v5.16;\n\n#-------------------------------------------------------------------------------\n#  Base class to provide some useful information about a given file.\n#-------------------------------------------------------------------------------\nuse Path::Tiny;\nuse Carp;\nuse File::stat;\nuse Time::Moment;\nuse Time::Piece;\nuse namespace::clean;    # clean imported functions from your namespace.\n\n#-------------------------------------------------------------------------------\n#  Attributes\n#-------------------------------------------------------------------------------\nhas file =&gt; (\n    is  =&gt; 'ro',\n    isa =&gt; sub {\n\n        #--- Because I like Path::Tiny\n        Carp::croak(\n            qq{'in_file' needs to be an existing file and a Path::Tiny Object!})\n          unless ( ref $_[0] eq 'Path::Tiny' and $_[0]-&gt;exists );\n    },\n    coerce =&gt; sub {\n        return $_[0]\n          if ( ref $_[0] eq 'Path::Tiny' );\n        return path( $_[1] );\n    },\n    required =&gt; 1\n);\n\nhas file_stat =&gt; (\n    is  =&gt; 'rwp',\n    isa =&gt; sub {\n        Carp::croak(qq{$_[0] is not a File::stat::stat!})\n          unless ( $_[0] and ( ref $_[0] ) );\n    },\n);\n\nhas size_bytes =&gt; (\n    is  =&gt; 'rwp',\n    isa =&gt; sub {\n        Carp::croak(qq{'bytes' value $_[0] is not numeric!})\n          unless ( looks_like_number( $_[0] ) );\n    },\n    lazy    =&gt; 1,\n    builder =&gt; sub {\n        $_[0]-&gt;file_stat-&gt;size;\n    },\n);\n\n#--- The file's last modification time\n#    as a Time::Moment Object\nhas mod_time_moment =&gt; (\n    is  =&gt; 'ro',\n    isa =&gt; sub {\n        Carp::croak(\n            qq{Bad 'mod_time_moment' because $_[0] is not a Time::Moment!})\n          unless ( ref $_[0] eq q{Time::Moment} );\n    },\n    lazy    =&gt; 1,\n    builder =&gt; sub {\n        #--- Time::Piece To keep it in the local time zone\n   Time::Moment-&gt;from_object( scalar Time::Piece::localtime($_[0]-&gt;file_stat-&gt;mtime ) \n        );\n    },\n);\n\n#--- Moo roles to print bytes\/seconds in a more readable form\nwith qw{MakeBytesPretty MakeSecondsPretty};\n\n#-------------------------------------------------------------------------------\n#  Builders and Triggers\n#-------------------------------------------------------------------------------\nsub BUILD {\n    my ($self) = @_;\n    $self-&gt;_set_file_stat( File::stat::stat( $self-&gt;file ) )\n      or Carp::croak( qq{File stat failed to get the file stat for, }\n          . $self-&gt;file\n          . qq{!: $!} );\n}\n\n#-------------------------------------------------------------------------------\n#  Methods\n#-------------------------------------------------------------------------------\n#--- The file age since its last modification expressed in seconds.\nsub seconds_since_mod {\n    my ($self) = @_;\n    return ( Time::Moment-&gt;now()-&gt;epoch - $self-&gt;mod_time_moment-&gt;epoch );\n}\n\nsub time_since_mod_pretty {\n    my ($self) = @_;\n    return $self-&gt;make_seconds_pretty( $self-&gt;seconds_since_mod );\n}\n\n#-------------------------------------------------------------------------------\n1;\n<\/pre>\n<p>I also made some changes to the role MakeSecondsPretty.pm. I removed the &#8216;requires&#8217; \u00a0statement and renamed the only method to &#8216;make_seconds_pretty&#8217; . I also changed this method slightly.<\/p>\n<pre class=\"lang:perl decode:true\" title=\"MakeSecondsPretty role modifications. \">sub make_seconds_pretty {\n    my ( $self , $seconds ) = @_;\n    return ( not $seconds )\n      ? 0\n      : ( $seconds &lt;= 60 ) ? sprintf( \"%0.02f\", $seconds )\n      . \" Seconds\"\n\n      : ( $seconds &lt;= 3600 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 60 ) ) . \" Minutes\"\n\n      : ( $seconds &lt;= 86400 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 3600 ) ) . \" Hours\"\n\n      : ( $seconds &lt;= 604800 )\n      ? sprintf( \"%0.02f\", ( $seconds \/ 86400 ) ) . \" Days\"\n\n      : sprintf( \"%0.02f\", ( $seconds \/ 604800 ) ) . \" Weeks\";\n}\n<\/pre>\n<p>The testing script also has to be changed to reflect changes in the module.<\/p>\n<pre class=\"lang:perl decode:true\" title=\"test_file.t modifications.\">my $fi = object_ok(\n    sub { File::Info-&gt;new( file =&gt; qq{IMAG0029.jpg} ) },\n    '$fi',\n    isa =&gt; [qw(Moo::Object )],\n    can =&gt; [\n        qw(  file file_stat size_bytes mod_time_moment make_seconds_pretty\n          seconds_since_mod time_since_mod_pretty )\n    ],\n    clean =&gt; 1,\n    more  =&gt; sub {\n        my $file_info_obj = shift;\n        isa_ok( $file_info_obj-&gt;file, 'Path::Tiny',\n            q{File::Info 'file' is a Path::Tiny object.} );\n        is( $file_info_obj-&gt;file-&gt;basename,\n            qq{IMAG0029.jpg},\n            q{File::Info 'file' has the correct file basename.} );\n        isa_ok( $file_info_obj-&gt;file_stat, q{File::stat},\n            qq{File::Info 'file_stat' is a 'File::stat'.} );\n        like( $file_info_obj-&gt;file_stat-&gt;size,\n            qr\/^[0-9]+$\/,\n            q{File::Info 'file_stat-&gt;size' returns a numeric file size.} );\n        like( $file_info_obj-&gt;size_bytes,\n            qr\/^[0-9]+$\/, qq{File::Info 'size_bytes' is numeric.} );\n        like(\n            $file_info_obj-&gt;make_file_size_pretty,\n            qr\/^d+?.d+ (bytes|KB|MB|GB|TB|PB|EB)$\/, qq{\n            File::Info bytes 'make_file_size_pretty' \n            prints a file size followed by the size Unit.}\n        );\n        isa_ok( $file_info_obj-&gt;mod_time_moment, q{Time::Moment},\n            qq{File::Info 'mod_time_moment' returns a Time::Moment object.}\n        );\n        like( $file_info_obj-&gt;seconds_since_mod, qr\/^d+$\/,\n            qq{File::Info 'seconds_since_mod' returns a numeric.}\n        );\n        like(\n           $file_info_obj-&gt;time_since_mod_pretty(),\n            qr\/^d+?.d+ (Seconds|Minutes|Hours|Days|Weeks)$\/, qq{\n            File::Info 'time_since_mod_pretty' uses 'make_seconds_pretty' \n            to print the 'seconds_since_mod' as time followed by the time Unit.}\n        );\n    },\n);\n<\/pre>\n<p>Run the test again&#8230;<\/p>\n<pre class=\"theme:solarized-dark nums:false nums-toggle:false lang:default decode:true \" title=\"test_file.t output\">Moo &gt; prove -v t\/test_file.t \nt\/test_file.t .. \n    # Subtest: $fi ok\n    ok 1 - instantiate $fi\n    ok 2 - $fi is blessed\n    ok 3 - '$fi' isa 'Moo::Object'\n    ok 4 - File::Info-&gt;can(...)\n    ok 5 - File::Info contains no imported functions\n        # Subtest: more tests for $fi\n        ok 1 - 'File::Info 'file' is a Path::Tiny object.' isa 'Path::Tiny'\n        ok 2 - File::Info 'file' has the correct file basename.\n        ok 3 - 'File::Info 'file_stat' is a 'File::stat'.' isa 'File::stat'\n        ok 4 - File::Info 'file_stat-&gt;size' returns a numeric file size.\n        ok 5 - File::Info 'size_bytes' is numeric.\n        ok 6 - \n        #             File::Info bytes 'make_file_size_pretty' \n        #             prints a file size followed by the size Unit.\n        ok 7 - 'File::Info 'mod_time_moment' returns a Time::Moment object.' isa 'Time::Moment'\n        ok 8 - File::Info 'seconds_since_mod' returns a numeric.\n        ok 9 - \n        #             File::Info 'time_since_mod_pretty' uses 'make_seconds_pretty' \n        #             to print the 'seconds_since_mod' as time followed by the time Unit.\n        ok 10 - no exception thrown by additional tests\n        1..10\n    ok 6 - more tests for $fi\n    1..6\nok 1 - $fi ok\nok 2 - no (unexpected) warnings (via done_testing)\n1..2\nok\nAll tests successful.\nFiles=1, Tests=2,  1 wallclock secs ( 0.03 usr  0.01 sys +  0.10 cusr  0.02 csys =  0.16 CPU)\nResult: PASS\n[22:28 - 0.34]\n<\/pre>\n<p>Ok!<\/p>\n<p>For my next post. I will use my module in a script with the help of \u00a0<a href=\"https:\/\/metacpan.org\/pod\/MooX::Options\">MooX::Options<\/a>.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Continuing from\u00a0my last post \u00a0Using Moo,\u00a0 I will add a little more functionality to my File::Info class. \u00a0At first I was going to subclass it, but then I decided to use\u00a0\u00a0Moo::Role\u00a0instead. Moo::Role\u00a0which is based on\u00a0Role::Tiny. Roles are a really convenient way of adding extra functionality to your class. package File::Info; use Scalar::Util qw\/blessed looks_like_number\/; use [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[109,107,108,101],"tags":[37,34,29,32,30,20,31,33,36,38],"class_list":["post-581","post","type-post","status-publish","format-standard","hentry","category-files","category-moo","category-oop","category-perl","tag-datetime","tag-file","tag-moo","tag-moorole","tag-pathtiny","tag-perl","tag-testmodern","tag-testing","tag-timemoment","tag-timepiece"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/581","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=581"}],"version-history":[{"count":17,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/581\/revisions"}],"predecessor-version":[{"id":906,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/581\/revisions\/906"}],"wp:attachment":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=581"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=581"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=581"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}