File Coverage

blib/lib/MooX/Role/SEOTags.pm
Criterion Covered Total %
statement 82 83 98.8
branch 6 10 60.0
condition 4 9 44.4
subroutine 19 19 100.0
pod 15 15 100.0
total 126 136 92.6


line stmt bran cond sub pod time code
1              
2             package MooX::Role::SEOTags;
3              
4             =head1 NAME
5              
6             MooX::Role::SEOTags - A role for generating SEO meta tags (OpenGraph, Twitter, etc)
7              
8             =head1 SYNOPSIS
9              
10             package MyWebPage;
11             use Moo;
12             with 'MooX::Role::SEOTags';
13              
14             has og_title => (is => 'ro', required => 1);
15             has og_type => (is => 'ro', required => 1);
16             has og_description => (is => 'ro', required => 1);
17             has og_url => (is => 'ro', required => 1);
18             has og_image => (is => 'ro'); # optional
19              
20             # And later, in the code that builds the website
21              
22             my $page = MyWebPage->new(
23             og_title => "My Page Title",
24             og_type => "website",
25             og_description => 'This is a lovely website',
26             og_url => "https://example.com/my-page",
27             og_image => "https://example.com/image.jpg",
28             );
29              
30             # Print tags separately
31              
32             print $page->title_tag;
33             print $page->canonical_tag;
34             print $page->og_tags;
35              
36             # Or print all tags at once
37              
38             print $page->tags;
39              
40             =head1 DESCRIPTION
41              
42             This role provides methods to generate SEO meta tags for web pages, including OpenGraph and Twitter tags. It
43             requires the consuming class to implement the following attributes or methods:
44              
45             =over 4
46              
47             =item * og_title - The title of the page
48              
49             =item * og_type - The type of the page (e.g., "website", "article")
50              
51             =item * og_description - The description of the page
52              
53             =item * og_url - The canonical URL of the page
54              
55             =item * og_image - The URL of an image representing the content (optional)
56              
57             =back
58              
59             The role provides methods to generate individual tags as well as a method to
60             generate all tags at once.
61              
62             =head1 METHODS
63              
64             =head2 title_tag
65              
66             Returns the HTML title tag.
67              
68             =head2 canonical_tag
69              
70             Returns the HTML canonical link tag.
71              
72             =head2 description_tag
73              
74             Returns the HTML meta description tag.
75              
76             =head2 og_title_tag
77              
78             Returns the OpenGraph title meta tag.
79              
80             =head2 og_type_tag
81              
82             Returns the OpenGraph type meta tag.
83              
84             =head2 og_description_tag
85              
86             Returns the OpenGraph description meta tag.
87              
88             =head2 og_url_tag
89              
90             Returns the OpenGraph URL meta tag.
91              
92             =head2 og_image_tag
93              
94             Returns the OpenGraph image meta tag if the og_image attribute is provided.
95              
96             =head2 og_tags
97              
98             Returns all OpenGraph meta tags as a single string.
99              
100             =head2 twitter_card_tag
101              
102             Returns the Twitter card meta tag.
103              
104             =head2 twitter_title_tag
105              
106             Returns the Twitter title meta tag.
107              
108             =head2 twitter_description_tag
109              
110             Returns the Twitter description meta tag.
111              
112             =head2 twitter_image_tag
113              
114             Returns the Twitter image meta tag if the og_image attribute is provided.
115              
116             =head2 twitter_tags
117              
118             Returns all Twitter meta tags as a single string.
119              
120             =head2 tags
121              
122             Returns all tags (title, canonical, and OpenGraph) as a single string.
123              
124             =cut
125              
126 3     3   620230 use feature qw[signatures];
  3         6  
  3         4812  
127 3     3   519 use Moo::Role;
  3         11995  
  3         43  
128 3     3   2995 use HTML::Tiny;
  3         10548  
  3         3091  
129              
130             our $VERSION = '1.0.1';
131              
132             requires qw[og_title og_type og_description og_url];
133              
134             has _html => (
135             is => 'ro',
136             default => sub { HTML::Tiny->new(mode => 'html') },
137             );
138              
139 18     18   123 sub _tag($self, $tag, @args) {
  18         30  
  18         31  
  18         36  
  18         30  
140 18         79 return $self->_html->$tag(@args);
141             }
142              
143 2     2 1 2023 sub title_tag($self) {
  2         5  
  2         4  
144 2         13 return $self->_tag('title', {}, $self->og_title);
145             }
146              
147 2     2 1 2019 sub canonical_tag($self) {
  2         5  
  2         4  
148 2         13 return $self->_tag('link', { rel => 'canonical', href => $self->og_url });
149             }
150              
151 1     1 1 92 sub description_tag($self) {
  1         3  
  1         2  
152 1         8 return $self->_tag('meta', { name => "description", content => $self->og_description });
153             }
154              
155 2     2 1 2536 sub og_title_tag($self) {
  2         4  
  2         5  
156 2         8 return $self->_tag('meta', { property => "og:title", content => $self->og_title });
157             }
158              
159 2     2 1 4432 sub og_type_tag($self) {
  2         5  
  2         20  
160 2         15 return $self->_tag('meta', { property => "og:type", content => $self->og_type });
161             }
162              
163 1     1 1 84 sub og_description_tag($self) {
  1         3  
  1         2  
164 1         4 return $self->_tag('meta', { property => "og:description", content => $self->og_description });
165             }
166              
167 1     1 1 83 sub og_url_tag($self) {
  1         2  
  1         2  
168 1         5 return $self->_tag('meta', { property => "og:url", content => $self->og_url });
169             }
170              
171 2     2 1 193 sub og_image_tag($self) {
  2         5  
  2         4  
172 2 50       17 return '' unless $self->can('og_image');
173 2         12 my $img = $self->og_image;
174 2 100 66     23 return '' unless defined $img && length $img;
175 1         6 return $self->_tag('meta', { property => "og:image", content => $img });
176             }
177              
178 1     1 1 88 sub og_tags($self) {
  1         3  
  1         2  
179 1   33     5 return join "\n", $self->og_title_tag,
180             $self->og_type_tag,
181             $self->og_description_tag,
182             $self->og_url_tag,
183             ($self->og_image_tag || ());
184             }
185              
186 2     2 1 5282 sub twitter_card_tag($self) {
  2         6  
  2         3  
187 2         4 my $card_type;
188 2 50       22 if ($self->can('twitter_card_type')) {
189 0         0 $card_type = $self->twitter_card_type;
190             } else {
191 2 50       10 $card_type = $self->can('og_image') ? 'summary_large_image' : 'summary';
192             }
193 2         13 return $self->_tag('meta', { name => "twitter:card", content =>$card_type });
194             }
195              
196 2     2 1 7213 sub twitter_title_tag($self) {
  2         5  
  2         6  
197 2         12 return $self->_tag('meta', { name => "twitter:title", content => $self->og_title });
198             }
199              
200 1     1 1 109 sub twitter_description_tag($self) {
  1         3  
  1         3  
201 1         5 return $self->_tag('meta', { name => "twitter:description", content => $self->og_description });
202             }
203              
204 1     1 1 83 sub twitter_image_tag($self) {
  1         3  
  1         1  
205 1 50       7 return unless $self->can('og_image');
206 1         11 return $self->_tag('meta', { name => "twitter:image", content => $self->og_image });
207             }
208              
209 1     1 1 115 sub twitter_tags($self) {
  1         2  
  1         2  
210 1   33     5 return join "\n", $self->twitter_card_tag,
211             $self->twitter_title_tag,
212             $self->twitter_description_tag,
213             ($self->twitter_image_tag || ());
214             }
215              
216 1     1 1 2540 sub tags($self) {
  1         16  
  1         2  
217 1         7 return join "\n", $self->title_tag,
218             $self->description_tag,
219             $self->canonical_tag,
220             $self->og_tags,
221             $self->twitter_tags;
222             }
223              
224             1;
225              
226             =head1 AUTHOR
227              
228             Dave Cross <dave@perlhacks.com>
229              
230             =head1 COPYRIGHT
231              
232             Copyright (c) 2025 Magnum Solutions Ltd. This program is free software; you can redistribute
233             it and/or modify it under the same terms as Perl itself.
234              
235             =cut