├── .gitignore
├── .perltidyrc
├── Changes
├── LICENSE
├── README
├── api.psgi
├── app.psgi
├── author
└── bin
│ ├── copy_assets
│ ├── tweak_bootstrap
│ └── update_cpanfile
├── bin
├── daily_tasks
├── grep
├── hourly_tasks
├── maint
│ ├── check_revision
│ ├── migrate
│ ├── setup
│ ├── show_ddl
│ └── status
├── run
├── run_all
└── run_tasks
├── cpanfile
├── lib
└── WWW
│ ├── CPANTS.pm
│ └── CPANTS
│ ├── API.pm
│ ├── API
│ ├── Context.pm
│ ├── Controller
│ │ ├── OpenApi.pm
│ │ └── Root.pm
│ ├── Model.pm
│ ├── Model
│ │ ├── Acme.pm
│ │ ├── Acme
│ │ │ └── CPANAuthors
│ │ │ │ ├── Authors.pm
│ │ │ │ ├── Modules.pm
│ │ │ │ └── Stats
│ │ │ │ ├── ActiveAuthors.pm
│ │ │ │ ├── NewAuthors.pm
│ │ │ │ ├── NewReleases.pm
│ │ │ │ └── Releases.pm
│ │ ├── V4.pm
│ │ ├── V4
│ │ │ ├── Search.pm
│ │ │ └── Table
│ │ │ │ ├── CPANDistributionsBy.pm
│ │ │ │ ├── FailsIn.pm
│ │ │ │ ├── Ranking.pm
│ │ │ │ ├── Recent.pm
│ │ │ │ ├── RecentBy.pm
│ │ │ │ ├── ReleasesOf.pm
│ │ │ │ └── ReverseDependenciesOf.pm
│ │ ├── V5.pm
│ │ └── V5
│ │ │ ├── Author.pm
│ │ │ ├── Author
│ │ │ ├── CPANDistributions.pm
│ │ │ ├── Feed.pm
│ │ │ └── RecentReleases.pm
│ │ │ ├── Dist
│ │ │ ├── Common.pm
│ │ │ ├── Errors.pm
│ │ │ ├── Files.pm
│ │ │ ├── Metadata.pm
│ │ │ ├── Overview.pm
│ │ │ ├── Prereq.pm
│ │ │ ├── Releases.pm
│ │ │ └── UsedBy.pm
│ │ │ ├── Kwalitee.pm
│ │ │ ├── Kwalitee
│ │ │ ├── Fail.pm
│ │ │ └── Indicator.pm
│ │ │ ├── Ranking.pm
│ │ │ ├── Recent.pm
│ │ │ ├── Release
│ │ │ ├── Common.pm
│ │ │ ├── Errors.pm
│ │ │ ├── Files.pm
│ │ │ ├── Metadata.pm
│ │ │ ├── Overview.pm
│ │ │ ├── Prereq.pm
│ │ │ ├── Releases.pm
│ │ │ └── UsedBy.pm
│ │ │ ├── Search.pm
│ │ │ └── Status.pm
│ ├── Plugin
│ │ └── DoesAccept.pm
│ └── Util
│ │ ├── Format.pm
│ │ └── Validate.pm
│ ├── Bin
│ ├── Context.pm
│ ├── Runner.pm
│ ├── Task.pm
│ └── Task
│ │ ├── Acme
│ │ ├── UpdateModules.pm
│ │ └── UpdateStats.pm
│ │ ├── Analyze.pm
│ │ ├── Analyze
│ │ ├── UpdateErrors.pm
│ │ ├── UpdateKwalitee.pm
│ │ ├── UpdateProvides.pm
│ │ ├── UpdateRequiresAndUses.pm
│ │ └── UpdateResources.pm
│ │ ├── AnalyzeAll.pm
│ │ ├── Enqueue.pm
│ │ ├── EnqueueTasks.pm
│ │ ├── Grep.pm
│ │ ├── Maint
│ │ ├── CheckRevision.pm
│ │ ├── CheckVersions.pm
│ │ ├── Cleanup.pm
│ │ ├── Migrate.pm
│ │ ├── Setup.pm
│ │ ├── ShowDDL.pm
│ │ └── ShowStatus.pm
│ │ ├── Notify.pm
│ │ ├── ParseRecentJson.pm
│ │ ├── PostProcess.pm
│ │ ├── PostProcess
│ │ ├── UpdateAuthorStats.pm
│ │ ├── UpdateCaches.pm
│ │ ├── UpdateRanking.pm
│ │ ├── UpdateReverseDependency.pm
│ │ └── UpdateSecurityAdvisories.pm
│ │ ├── RegisterNew.pm
│ │ ├── RunTasks.pm
│ │ ├── Traverse.pm
│ │ ├── Traverse
│ │ ├── UpdateAuthorJson.pm
│ │ ├── UpdateCPANStatus.pm
│ │ └── UpdateDistributions.pm
│ │ ├── UpdateCPANIndices.pm
│ │ └── UpdateCPANIndices
│ │ ├── PackagesDetails.pm
│ │ ├── Permissions.pm
│ │ └── Whois.pm
│ ├── DB.pm
│ ├── DB
│ ├── Handle.pm
│ ├── Handle
│ │ ├── MySQL.pm
│ │ └── SQLite.pm
│ ├── Table.pm
│ └── Table
│ │ ├── AcmeAuthors.pm
│ │ ├── AcmeModules.pm
│ │ ├── AcmeStats.pm
│ │ ├── Analysis.pm
│ │ ├── Authors.pm
│ │ ├── Distributions.pm
│ │ ├── Errors.pm
│ │ ├── Kwalitee.pm
│ │ ├── PackagesDetails.pm
│ │ ├── Permissions.pm
│ │ ├── Provides.pm
│ │ ├── Queue.pm
│ │ ├── RequiresAndUses.pm
│ │ ├── Resources.pm
│ │ ├── Tasks.pm
│ │ ├── Uploads.pm
│ │ └── Whois.pm
│ ├── Model
│ ├── Archive.pm
│ ├── CPAN.pm
│ ├── CPAN
│ │ ├── Packages.pm
│ │ ├── Perms.pm
│ │ ├── Recent.pm
│ │ └── Whois.pm
│ ├── Kwalitee.pm
│ ├── PidFile.pm
│ ├── Revision.pm
│ ├── TableGuard.pm
│ ├── TempDir.pm
│ └── Timer.pm
│ ├── Role
│ ├── API
│ │ └── Model
│ │ │ └── V5
│ │ │ ├── Author
│ │ │ └── Status.pm
│ │ │ └── Dist
│ │ │ └── GetUid.pm
│ ├── CPAN
│ │ ├── Index.pm
│ │ ├── Indices.pm
│ │ └── Path.pm
│ ├── Logger.pm
│ ├── OpenApiSchemaBuilder.pm
│ ├── Options.pm
│ └── Task
│ │ └── FixAnalysis.pm
│ ├── Test.pm
│ ├── Test
│ ├── Fixture.pm
│ ├── Selenium.pm
│ └── TestPAN.pm
│ ├── Util
│ ├── CoreList.pm
│ ├── Datetime.pm
│ ├── Diff.pm
│ ├── Distname.pm
│ ├── HideInternal.pm
│ ├── JSON.pm
│ ├── Loader.pm
│ ├── Parallel.pm
│ ├── Path.pm
│ ├── PathUid.pm
│ └── Version.pm
│ ├── Web.pm
│ └── Web
│ ├── Context.pm
│ ├── Controller.pm
│ ├── Controller
│ ├── Author.pm
│ ├── Dist.pm
│ ├── Home.pm
│ ├── Kwalitee.pm
│ ├── Ranking.pm
│ ├── Recent.pm
│ ├── Release.pm
│ ├── Root.pm
│ ├── Search.pm
│ └── Stats.pm
│ ├── Plugin
│ ├── Helpers.pm
│ └── Hooks.pm
│ └── Util
│ ├── Badge.pm
│ └── URL.pm
├── package.json
├── public
├── bootstrap
│ ├── css
│ │ ├── bootstrap-theme.css
│ │ ├── bootstrap-theme.css.map
│ │ ├── bootstrap-theme.min.css
│ │ ├── bootstrap-theme.min.css.map
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ ├── bootstrap.min.css
│ │ └── bootstrap.min.css.map
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ └── js
│ │ ├── bootstrap.js
│ │ ├── bootstrap.min.js
│ │ └── npm.js
├── cpants
│ └── css
│ │ └── cpants.css
├── datatables-responsive
│ ├── css
│ │ ├── responsive.bootstrap.css
│ │ ├── responsive.dataTables.css
│ │ ├── responsive.foundation.css
│ │ └── responsive.jqueryui.css
│ └── js
│ │ ├── dataTables.responsive.js
│ │ ├── responsive.bootstrap.js
│ │ ├── responsive.foundation.js
│ │ └── responsive.jqueryui.js
├── datatables
│ ├── css
│ │ ├── jquery.dataTables.css
│ │ └── jquery.dataTables.min.css
│ ├── images
│ │ ├── Sorting icons.psd
│ │ ├── favicon.ico
│ │ ├── sort_asc.png
│ │ ├── sort_asc_disabled.png
│ │ ├── sort_both.png
│ │ ├── sort_desc.png
│ │ └── sort_desc_disabled.png
│ └── js
│ │ ├── jquery.dataTables.js
│ │ └── jquery.dataTables.min.js
├── font-awesome
│ ├── css
│ │ ├── font-awesome.css
│ │ ├── font-awesome.css.map
│ │ └── font-awesome.min.css
│ └── fonts
│ │ ├── FontAwesome.otf
│ │ ├── fontawesome-webfont.eot
│ │ ├── fontawesome-webfont.svg
│ │ ├── fontawesome-webfont.ttf
│ │ ├── fontawesome-webfont.woff
│ │ └── fontawesome-webfont.woff2
├── highcharts
│ ├── adapters
│ │ ├── standalone-framework.js
│ │ └── standalone-framework.src.js
│ ├── highcharts.js
│ ├── modules
│ │ ├── broken-axis.js
│ │ ├── broken-axis.src.js
│ │ ├── canvas-tools.js
│ │ ├── canvas-tools.src.js
│ │ ├── data.js
│ │ ├── data.src.js
│ │ ├── drilldown.js
│ │ ├── drilldown.src.js
│ │ ├── exporting.js
│ │ ├── exporting.src.js
│ │ ├── funnel.js
│ │ ├── funnel.src.js
│ │ ├── heatmap.js
│ │ ├── heatmap.src.js
│ │ ├── no-data-to-display.js
│ │ ├── no-data-to-display.src.js
│ │ ├── solid-gauge.js
│ │ ├── solid-gauge.src.js
│ │ ├── treemap.js
│ │ └── treemap.src.js
│ └── themes
│ │ ├── dark-blue.js
│ │ ├── dark-green.js
│ │ ├── dark-unica.js
│ │ ├── gray.js
│ │ ├── grid-light.js
│ │ ├── grid.js
│ │ ├── sand-signika.js
│ │ └── skies.js
├── html5shiv
│ ├── html5shiv-printshiv.js
│ ├── html5shiv-printshiv.min.js
│ ├── html5shiv.js
│ └── html5shiv.min.js
├── jquery-jsonview
│ ├── jquery.jsonview.css
│ └── jquery.jsonview.js
├── jquery-migrate
│ ├── jquery-migrate.js
│ ├── jquery-migrate.min.js
│ └── jquery-migrate.min.map
├── jquery
│ ├── core.js
│ ├── jquery.js
│ ├── jquery.min.js
│ ├── jquery.min.map
│ ├── jquery.slim.js
│ ├── jquery.slim.min.js
│ └── jquery.slim.min.map
├── respond-js
│ ├── respond.matchmedia.addListener.min.js
│ ├── respond.matchmedia.addListener.src.js
│ ├── respond.min.js
│ └── respond.src.js
├── sprintf-js
│ ├── angular-sprintf.min.js
│ ├── angular-sprintf.min.js.map
│ ├── sprintf.min.js
│ └── sprintf.min.js.map
├── strftime
│ └── strftime-min.js
└── swagger-ui
│ ├── swagger-ui-bundle.js
│ ├── swagger-ui-bundle.js.map
│ ├── swagger-ui-standalone-preset.js
│ ├── swagger-ui-standalone-preset.js.map
│ ├── swagger-ui.css
│ └── swagger-ui.css.map
├── t
├── 00_load.t
├── 10_tasks.t
├── analysis
│ └── required_perl.t
├── api_model
│ ├── acme
│ │ └── cpan_authors
│ │ │ ├── authors.t
│ │ │ ├── modules.t
│ │ │ └── stats
│ │ │ ├── active_authors.t
│ │ │ ├── new_authors.t
│ │ │ ├── new_releases.t
│ │ │ └── releases.t
│ ├── v4
│ │ ├── search.t
│ │ └── table
│ │ │ ├── cpan_distributions_by.t
│ │ │ ├── fails_in.t
│ │ │ ├── ranking.t
│ │ │ ├── recent.t
│ │ │ ├── recent_by.t
│ │ │ ├── releases_of.t
│ │ │ └── reverse_dependencies_of.t
│ └── v5
│ │ ├── author.t
│ │ ├── author
│ │ ├── cpan_distributions.t
│ │ ├── feed.t
│ │ └── recent_releases.t
│ │ ├── dist
│ │ ├── common.t
│ │ ├── errors.t
│ │ ├── files.t
│ │ ├── metadata.t
│ │ ├── overview.t
│ │ ├── prereq.t
│ │ ├── releases.t
│ │ └── used_by.t
│ │ ├── kwalitee.t
│ │ ├── kwalitee
│ │ ├── fail.t
│ │ └── indicator.t
│ │ ├── ranking.t
│ │ ├── recent.t
│ │ ├── search.t
│ │ └── status.t
├── kwalitee
│ ├── buildtool_not_executable.t
│ ├── configure_prereq_matches_use.t
│ ├── consistent_version.t
│ ├── distname_matches_name_in_meta.t
│ ├── extractable.t
│ ├── extracts_nicely.t
│ ├── has_abstract_in_pod.t
│ ├── has_buildtool.t
│ ├── has_changelog.t
│ ├── has_human_readable_license.t
│ ├── has_known_license_in_source_file.t
│ ├── has_license_in_source_file.t
│ ├── has_manifest.t
│ ├── has_meta_json.t
│ ├── has_meta_yml.t
│ ├── has_proper_version.t
│ ├── has_readme.t
│ ├── has_separate_license_file.t
│ ├── has_tests.t
│ ├── has_tests_in_t_dir.t
│ ├── has_version.t
│ ├── main_module_version_matches_dist_version.t
│ ├── manifest_matches_dist.t
│ ├── meta_json_conforms_to_known_spec.t
│ ├── meta_json_is_parsable.t
│ ├── meta_yml_conforms_to_known_spec.t
│ ├── meta_yml_declares_perl_version.t
│ ├── meta_yml_has_license.t
│ ├── meta_yml_has_provides.t
│ ├── meta_yml_has_repository_resource.t
│ ├── meta_yml_is_parsable.t
│ ├── no_abstract_stub_in_pod.t
│ ├── no_broken_auto_install.t
│ ├── no_broken_module_install.t
│ ├── no_dot_dirs.t
│ ├── no_dot_underscore_files.t
│ ├── no_files_to_be_skipped.t
│ ├── no_generated_files.t
│ ├── no_invalid_versions.t
│ ├── no_local_dirs.t
│ ├── no_maniskip_error.t
│ ├── no_missing_files_in_provides.t
│ ├── no_mymeta_files.t
│ ├── no_pax_headers.t
│ ├── no_pod_errors.t
│ ├── no_stdin_for_prompting.t
│ ├── no_symlinks.t
│ ├── no_unauthorized_packages.t
│ ├── portable_filenames.t
│ ├── prereq_matches_use.t
│ ├── proper_libs.t
│ ├── test_prereq_matches_use.t
│ ├── use_strict.t
│ ├── use_warnings.t
│ └── valid_signature.t
├── root.t
└── web
│ ├── author.t
│ ├── author
│ └── ignore_backpan_latest.t
│ ├── dist.t
│ ├── dist
│ ├── latest_dev_and_stable.t
│ └── pod_escapes.t
│ ├── home.t
│ ├── kwalitee.t
│ ├── ranking.t
│ ├── recent.t
│ ├── search.t
│ └── stats.t
├── templates
├── _notice.html.ep
├── author.html.ep
├── author
│ └── _sidebar.html.ep
├── dist
│ ├── _sidebar.html.ep
│ ├── _tab.html.ep
│ ├── errors.html.ep
│ ├── files.html.ep
│ ├── metadata.html.ep
│ ├── overview.html.ep
│ ├── prereq.html.ep
│ ├── releases.html.ep
│ └── used_by.html.ep
├── exception.html.ep
├── home.html.ep
├── kwalitee.html.ep
├── kwalitee
│ ├── _sidebar.html.ep
│ ├── fail.html.ep
│ └── indicator.html.ep
├── layouts
│ └── layout.html.ep
├── mojolicious
│ └── plugin
│ │ └── openapi
│ │ └── layout.html.ep
├── not_found.html.ep
├── ranking
│ ├── _sidebar.html.ep
│ ├── five_or_more.html.ep
│ └── less_than_five.html.ep
├── recent.html.ep
├── search.html.ep
├── stats.html.ep
└── stats
│ ├── _sidebar.html.ep
│ └── module_install.html.ep
├── web.psgi
├── xt
├── api_model.t
├── cpanfile.t
├── kwalitee.t
├── perlcritic.t
├── util
│ └── validate.t
└── web.t
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | *.bak
2 | *.old
3 | /tmp/
4 | /etc/
5 | /log/
6 | /db/
7 | /backup/
8 | /local/
9 | /cover_db/
10 | /nytprof*
11 | /etc/config.pl
12 | blib/
13 | tmp/
14 | Makefile
15 | !Makefile.PL
16 | pm_to_blib
17 | MYMETA.*
18 | *.bat
19 | /*.pl
20 | /*.zip
21 | .perl-version
22 | $~*
23 | /node_modules/
24 | /qc.dat
25 | /public/img/
26 |
--------------------------------------------------------------------------------
/.perltidyrc:
--------------------------------------------------------------------------------
1 | -b
2 | -bbt=0
3 | -bext=/
4 | -blbs=0
5 | -boa
6 | -boc
7 | -bok
8 | -bol
9 | -bot
10 | -bt=1
11 | -cab=3
12 | -ce
13 | -ci=4
14 | -cti=0
15 | -i=4
16 | -kis
17 | -l=0
18 | -nbbc
19 | -nicb
20 | -nicp
21 | -nsfs
22 | -nsts
23 | -ohbr
24 | -opr
25 | -osbr
26 | -otr
27 | -pt=2
28 | -sbt=2
29 | -sct
30 | -sot
31 | -wn
32 |
--------------------------------------------------------------------------------
/Changes:
--------------------------------------------------------------------------------
1 | Revision history for WWW-CPANTS
2 |
3 | 5.00 2020/07/19
4 | - public release
5 |
6 | 4.00 2016/05/15
7 | - public release
8 |
9 | 1.00 2012/12/24
10 | - public release
11 |
12 | 0.01 2012/08/22
13 | - initial import
14 | - forked from Module-CPANTS-Site
15 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | WWW-CPANTS
2 | ==========
3 |
4 | Yet another CPANTS frontend.
5 |
6 | HOW TO USE
7 | ----------
8 |
9 | 1. Clone git://github.com/cpants/www-cpants into somewhere.
10 | 2. Install dependencies by "cpanm --installdeps ." or "carton install"
11 | 3. Run "prove -lwr t/" to make sure if it works.
12 | 4. Run "perl bin/run_all --workers 3".
13 | 5. Run "plackup (or starman, etc) app.psgi".
14 | 6. Register bin/run_all to your crontab.
15 |
16 | I advise you not to "install" this into your Perl lib directories.
17 |
18 | COPYRIGHT AND LICENSE
19 | ---------------------
20 |
21 | Copyright (C) 2012- Kenichi Ishigaki
22 |
23 | This program is free software; you can redistribute it and/or
24 | modify it under the same terms as Perl itself.
25 |
26 |
--------------------------------------------------------------------------------
/api.psgi:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use lib "$FindBin::Bin/lib";
4 | use lib glob "extlib/*/lib";
5 | use WWW::CPANTS::API;
6 | use Plack::Builder;
7 |
8 | my $api = WWW::CPANTS::API->new;
9 |
10 | builder {
11 | enable 'ReverseProxy';
12 | $api->start('psgi');
13 | };
14 |
--------------------------------------------------------------------------------
/app.psgi:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use lib "$FindBin::Bin/lib";
4 | use lib glob "extlib/*/lib";
5 | use WWW::CPANTS::Web;
6 | use WWW::CPANTS::API;
7 | use Plack::Builder;
8 | use Plack::App::URLMap;
9 |
10 | my $web = WWW::CPANTS::Web->new;
11 | my $api = WWW::CPANTS::API->new;
12 | my $map = Plack::App::URLMap->new;
13 | $map->map("/" => $web->start('psgi'));
14 | $map->map("/api" => $api->start('psgi'));
15 |
16 | builder {
17 | enable 'ReverseProxy';
18 | $map->to_app;
19 | };
20 |
--------------------------------------------------------------------------------
/bin/daily_tasks:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks(qw(
9 | EnqueueTasks
10 | Maint::CheckRevision
11 | Traverse
12 | PostProcess::UpdateReverseDependency
13 | PostProcess::UpdateSecurityAdvisories
14 | PostProcess::UpdateCaches
15 | Acme::UpdateModules
16 | Acme::UpdateStats
17 | Maint::Cleanup
18 | ));
19 |
--------------------------------------------------------------------------------
/bin/grep:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Grep');
9 |
--------------------------------------------------------------------------------
/bin/hourly_tasks:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks(qw(
9 | EnqueueTasks
10 | UpdateCPANIndices
11 | ParseRecentJson
12 | Enqueue
13 | AnalyzeAll
14 | PostProcess::UpdateAuthorStats
15 | PostProcess::UpdateRanking
16 | PostProcess::UpdateCaches
17 | ));
18 |
--------------------------------------------------------------------------------
/bin/maint/check_revision:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Maint::CheckRevision');
9 |
--------------------------------------------------------------------------------
/bin/maint/migrate:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Maint::Migrate');
9 |
--------------------------------------------------------------------------------
/bin/maint/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Maint::Setup');
9 |
--------------------------------------------------------------------------------
/bin/maint/show_ddl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Maint::ShowDDL');
9 |
--------------------------------------------------------------------------------
/bin/maint/status:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('Maint::ShowStatus');
9 |
--------------------------------------------------------------------------------
/bin/run:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks;
9 |
--------------------------------------------------------------------------------
/bin/run_all:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks(qw(
9 | Maint::Setup
10 | Maint::CheckRevision
11 | UpdateCPANIndices
12 | Traverse
13 | Enqueue
14 | AnalyzeAll
15 | PostProcess
16 | Acme::UpdateModules
17 | Acme::UpdateStats
18 | Maint::Cleanup
19 | Notify
20 | ));
21 |
--------------------------------------------------------------------------------
/bin/run_tasks:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env perl
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use FindBin;
5 | use lib "$FindBin::Bin/../lib";
6 | use WWW::CPANTS::Bin::Runner;
7 |
8 | WWW::CPANTS::Bin::Runner->new->run_tasks('RunTasks');
9 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API;
2 |
3 | use Mojo::Base 'Mojolicious', -signatures;
4 | use Digest::MD5 qw/md5_hex/;
5 | use WWW::CPANTS::Util::Loader;
6 | use WWW::CPANTS::API::Context;
7 | use String::CamelCase qw/camelize/;
8 | use Mock::MonkeyPatch;
9 | use JSON::Validator;
10 | use JSON::Validator::Schema::OpenAPIv3;
11 |
12 | has 'ctx' => \&_build_ctx;
13 |
14 | sub startup ($app) {
15 | if ($ENV{CPANTS_API_DEBUG} or $^O eq 'MSWin32') {
16 | $app->mode('development');
17 | } else {
18 | $app->mode('production');
19 | }
20 | $app->secrets([md5_hex($$ . time)]);
21 |
22 | $app->{mock} = Mock::MonkeyPatch->patch(
23 | 'JSON::Validator::Schema::OpenAPIv3::base_url' => sub ($self, $url = undef) {
24 | # No need to modify on the fly
25 | return Mojo::URL->new($self->get('/servers/0/url') || '');
26 | },
27 | );
28 |
29 | my $r = $app->routes->under('/')->to('root#check_maintenance');
30 |
31 | for my $path (qw/v5 v4 acme/) {
32 | my %conf = (
33 | schema => 'v3',
34 | url => $app->build_openapi_schema($path),
35 | route => $r->under("/$path")->to("open_api#$path"),
36 | );
37 | $conf{default_response_code} = [];
38 | $app->plugin(OpenAPI => \%conf);
39 | }
40 |
41 | $r->get('/')->to('open_api#root');
42 |
43 | $app->plugin('WWW::CPANTS::API::Plugin::DoesAccept');
44 | }
45 |
46 | sub _build_ctx ($self) {
47 | WWW::CPANTS::API::Context->new;
48 | }
49 |
50 | sub build_openapi_schema ($self, $path) {
51 | my $name = ucfirst($path);
52 | my $class = "WWW::CPANTS::API::Model::$name";
53 | use_module($class)->build_schema($self->ctx);
54 | }
55 |
56 | 1;
57 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Context.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Context;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use WWW::CPANTS;
6 | use WWW::CPANTS::DB;
7 | use WWW::CPANTS::Util::Loader;
8 | use WWW::CPANTS::Model::Kwalitee;
9 | use Mojo::URL;
10 |
11 | with qw(
12 | WWW::CPANTS::Role::Logger
13 | WWW::CPANTS::Role::Options
14 | );
15 |
16 | our @OPTIONS = (
17 | 'trace:1',
18 | 'verbose|v',
19 | );
20 |
21 | has 'db' => \&_build_db;
22 | has 'quiet' => \&_build_quiet;
23 | has 'kwalitee' => \&_build_kwalitee;
24 | has 'api_base' => \&_build_api_base;
25 |
26 | sub _build_db ($self) {
27 | WWW::CPANTS::DB->new(trace => $self->trace);
28 | }
29 |
30 | sub _build_quiet ($self) {
31 | return if $self->verbose;
32 | WWW::CPANTS->instance->quiet;
33 | }
34 |
35 | sub _build_kwalitee ($self) {
36 | WWW::CPANTS::Model::Kwalitee->new;
37 | }
38 |
39 | sub _build_api_base ($self) {
40 | my $base = WWW::CPANTS->instance->config->{api_base} // '/api/';
41 | $base .= '/' unless substr($base, -1, 1) eq '/';
42 | Mojo::URL->new($base);
43 | }
44 |
45 | 1;
46 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Controller/Root.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Controller::Root;
2 |
3 | use Mojo::Base 'Mojolicious::Controller', -signatures;
4 | use WWW::CPANTS::Util::JSON;
5 | use WWW::CPANTS;
6 |
7 | sub check_maintenance ($c) {
8 | if (WWW::CPANTS->instance->is_under_maintenance) {
9 | $c->stash("cpants.has_notice" => 1);
10 | $c->stash("cpants.notice_maintenance" => 1);
11 | }
12 | my $json = slurp_json('Task::AnalyzeAll');
13 | $c->stash('last_analyzed', $json->{last_executed});
14 | return 1;
15 | }
16 |
17 | 1;
18 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Model/Acme.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Model::Acme;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::API::Model', -signatures;
5 | use constant api_info => +{
6 | title => 'Acme::CPANAuthors API',
7 | version => '1.0',
8 | description => 'This API is mostly used internally to build the [Acme::CPANAuthors](https://acme.cpanauthors.org/) web frontend.',
9 | };
10 |
11 | with qw/WWW::CPANTS::Role::OpenApiSchemaBuilder/;
12 |
13 | 1;
14 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Model/V4.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Model::V4;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::API::Model', -signatures;
5 | use constant api_info => +{
6 | title => 'CPANTS API',
7 | version => '4.0; deprecated',
8 | description => 'This API is mostly used internally to build the older version of [CPANTS](https://cpants.cpanauthors.org/) web frontend.',
9 | };
10 |
11 | with qw/WWW::CPANTS::Role::OpenApiSchemaBuilder/;
12 |
13 | 1;
14 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Model/V5.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Model::V5;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::API::Model', -signatures;
5 | use constant api_info => +{
6 | title => 'CPANTS API',
7 | version => '5.0',
8 | description => 'This API is mostly used internally to build the [CPANTS](https://cpants.cpanauthors.org/) web frontend.',
9 | };
10 |
11 | with qw/WWW::CPANTS::Role::OpenApiSchemaBuilder/;
12 |
13 | 1;
14 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Model/V5/Release/Common.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Model::V5::Release::Common;
2 |
3 | use Mojo::Base 'WWW::CPANTS::API::Model::V5::Dist::Common', -signatures;
4 |
5 | 1;
6 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Model/V5/Status.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Model::V5::Status;
2 |
3 | use Mojo::Base 'WWW::CPANTS::API::Model', -signatures;
4 | use WWW::CPANTS::API::Util::Validate;
5 | use WWW::CPANTS::Util::JSON;
6 |
7 | sub operation ($self) {
8 | +{
9 | tags => ['Status'],
10 | description => 'Returns CPANTS status',
11 | parameters => [],
12 | responses => {
13 | 200 => {
14 | description => 'CPANTS status',
15 | content => {
16 | 'application/json' => {
17 | schema => {
18 | type => 'object',
19 | properties => {
20 | maintenance => { type => 'integer' },
21 | last_analyzed => {
22 | description => 'Epoch time',
23 | type => 'integer',
24 | },
25 | },
26 | },
27 | },
28 | },
29 | },
30 | },
31 | };
32 | }
33 |
34 | sub _load ($self, $params = {}) {
35 | my $maintenance = WWW::CPANTS->instance->is_under_maintenance;
36 |
37 | my $json = slurp_json('Task::AnalyzeAll');
38 | my $last_analyzed = $json->{last_executed};
39 |
40 | return {
41 | maintenance => $maintenance,
42 | last_analyzed => $last_analyzed,
43 | };
44 | }
45 |
46 | 1;
47 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Plugin/DoesAccept.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Plugin::DoesAccept;
2 |
3 | use Mojo::Base 'Mojolicious::Plugin', -signatures;
4 |
5 | sub register ($self, $app, $conf) {
6 | $app->helper(does_accept => \&does_accept);
7 | }
8 |
9 | sub does_accept ($c, $type) {
10 | my @accepts = split ',', $c->req->headers->accept // '';
11 | for my $accept (@accepts) {
12 | return 1 if $accept =~ /\A$type(;|$)/;
13 | }
14 | return;
15 | }
16 |
17 | 1;
18 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Util/Format.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Util::Format;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 |
6 | our @EXPORT = qw(
7 | percent decimal kwalitee_score
8 | release_availability
9 | );
10 |
11 | sub kwalitee_score ($score) { $score ? 0 + sprintf '%.2f', $score : 0 }
12 |
13 | sub decimal ($decimal) {
14 | 0 + sprintf '%0.2f', int(($decimal || 0) * 100 + 0.5) / 100;
15 | }
16 |
17 | sub percent ($numerator, $denominator) {
18 | decimal(($numerator || 0) / ($denominator || 100) * 100);
19 | }
20 |
21 | sub release_availability ($release) {
22 | return "" unless ref $release eq 'HASH';
23 | return "Latest Dev" if $release->{latest} && !$release->{stable};
24 | return "Latest" if $release->{latest} && $release->{stable};
25 | return "CPAN" if $release->{cpan};
26 | return "BackPAN";
27 | }
28 |
29 | 1;
30 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/API/Util/Validate.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::API::Util::Validate;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 |
6 | our @EXPORT = qw(
7 | is_int is_alphanum is_dist is_path is_pause_id
8 | is_availability_type
9 | );
10 |
11 | sub is_int ($value, $length = 19) {
12 | return unless defined $value;
13 | return unless $value =~ /\A[0-9]+\z/ && length($value) <= $length;
14 | return $value;
15 | }
16 |
17 | sub is_alphanum ($value, $length = 255) {
18 | return unless defined $value;
19 | return unless $value =~ /\A[0-9A-Za-z_\.\-\+]+\z/ && length($value) <= $length;
20 | return $value;
21 | }
22 |
23 | sub is_dist ($value, $length = 255) {
24 | return unless defined $value;
25 | return unless $value =~ /\A[0-9A-Za-z_\.\-\+]+\z/ && length($value) <= $length;
26 | return $value;
27 | }
28 |
29 | sub is_path ($value, $length = 255) {
30 | return unless defined $value;
31 | return unless $value =~ /\A[0-9A-Za-z_\.\-\/\+]+\z/ && length($value) <= $length;
32 | return $value;
33 | }
34 |
35 | sub is_pause_id ($value, $length = 9) {
36 | return unless defined $value;
37 | return unless length($value) <= $length;
38 | $value = uc $value;
39 | return unless $value =~ /\A[A-Z][A-Z0-9][0-9A-Z\-]*\z/;
40 | return $value;
41 | }
42 |
43 | my %availability = map { $_ => 1 } qw/latest cpan backpan/;
44 | sub is_availability_type ($value) {
45 | return unless defined $value;
46 | return $value if exists $availability{$value};
47 | }
48 |
49 | 1;
50 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Context.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Context;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use WWW::CPANTS;
6 | use WWW::CPANTS::DB;
7 | use WWW::CPANTS::Util::Loader;
8 | use WWW::CPANTS::Model::CPAN;
9 | use WWW::CPANTS::Model::Kwalitee;
10 | use Path::Tiny ();
11 |
12 | with qw(
13 | WWW::CPANTS::Role::Logger
14 | WWW::CPANTS::Role::Options
15 | );
16 |
17 | our @OPTIONS = (
18 | 'dry_run|dry-run',
19 | 'all',
20 | 'force',
21 | 'trace:1',
22 | 'verbose|v',
23 | );
24 |
25 | has 'db' => \&_build_db;
26 | has 'cpan' => \&_build_cpan;
27 | has 'backpan' => \&_build_backpan;
28 | has 'quiet' => \&_build_quiet;
29 | has 'kwalitee' => \&_build_kwalitee;
30 |
31 | sub _build_db ($self) {
32 | WWW::CPANTS::DB->new(trace => $self->trace);
33 | }
34 |
35 | sub _build_cpan ($self) {
36 | WWW::CPANTS::Model::CPAN->new(
37 | path => WWW::CPANTS->instance->cpan_path,
38 | );
39 | }
40 |
41 | sub _build_backpan ($self) {
42 | WWW::CPANTS::Model::CPAN->new(
43 | path => WWW::CPANTS->instance->backpan_path,
44 | );
45 | }
46 |
47 | sub _build_kwalitee ($self) {
48 | WWW::CPANTS::Model::Kwalitee->new;
49 | }
50 |
51 | sub _build_quiet ($self) {
52 | return if $self->verbose;
53 | WWW::CPANTS->instance->quiet;
54 | }
55 |
56 | sub new_db ($self) {
57 | $self->db(_build_db($self));
58 | $self->db;
59 | }
60 |
61 | sub load_task ($self, $name) {
62 | my $task_class = use_module("WWW::CPANTS::Bin::Task::$name");
63 | $task_class->new(name => $name, ctx => $self);
64 | }
65 |
66 | sub task_names ($self) {
67 | my @names = sort keys submodules("WWW::CPANTS::Bin::Task")->%*;
68 | \@names;
69 | }
70 |
71 | 1;
72 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Analyze/UpdateKwalitee.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Analyze::UpdateKwalitee;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
5 | use WWW::CPANTS::Util::Datetime qw/year/;
6 |
7 | our @READ = qw/Analysis/;
8 | our @WRITE = qw/Kwalitee/;
9 |
10 | with qw/WWW::CPANTS::Role::Task::FixAnalysis/;
11 |
12 | sub update ($self, $uid, $stash) {
13 | return unless exists $stash->{kwalitee};
14 |
15 | return if $self->dry_run;
16 |
17 | $self->ctx->kwalitee->set_scores($stash);
18 |
19 | my $pause_id = $stash->{author};
20 | my $distribution = $stash->{dist};
21 | my $kwalitee = $stash->{kwalitee};
22 |
23 | $self->db->table('Kwalitee')->update_kwalitee($uid, $pause_id, $distribution, year($stash->{released_epoch}), $kwalitee);
24 | }
25 |
26 | 1;
27 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Analyze/UpdateResources.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Analyze::UpdateResources;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
5 | use WWW::CPANTS::Util::JSON;
6 |
7 | our @WRITE = qw/Resources/;
8 |
9 | with qw/WWW::CPANTS::Role::Task::FixAnalysis/;
10 |
11 | sub update ($self, $uid, $stash) {
12 | return unless exists $stash->{meta_yml};
13 | return unless ref $stash->{meta_yml} eq 'HASH';
14 | return unless exists $stash->{meta_yml}{resources};
15 | return if $self->dry_run;
16 |
17 | my $pause_id = $stash->{author};
18 | my $resources = $stash->{meta_yml}{resources};
19 | $resources = {} unless ref $resources eq 'HASH';
20 |
21 | my $repository = '';
22 | if ($resources->{repository}) {
23 | if (ref $resources->{repository} eq 'HASH') {
24 | $repository = $resources->{repository}{web} // $resources->{repository}{url};
25 | } elsif (!ref $resources->{repository}) {
26 | $repository = $resources->{repository};
27 | }
28 | }
29 | my $bugtracker = '';
30 | if ($resources->{bugtracker}) {
31 | if (ref $resources->{bugtracker} eq 'HASH') {
32 | $bugtracker = $resources->{bugtracker}{web};
33 | } elsif (!ref $resources->{bugtracker}) {
34 | $bugtracker = $resources->{bugtracker};
35 | }
36 | }
37 |
38 | $self->db->table('Resources')->update_resources($uid, $pause_id, encode_json($resources), $repository, $bugtracker);
39 | }
40 |
41 | 1;
42 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/EnqueueTasks.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::EnqueueTasks;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | our @READ = qw/Tasks/;
6 | our @WRITE = qw/Tasks/;
7 |
8 | sub run ($self, @args) {
9 | return unless @args;
10 |
11 | my @rows = map {
12 | +{
13 | name => $_,
14 | created_at => time,
15 | pid => 0,
16 | }
17 | } @args;
18 |
19 | my $table = $self->db->table('Tasks');
20 |
21 | for my $task (@{ $table->select_all_running_tasks // [] }) {
22 | my $pid = $task->{pid};
23 | next if kill 0, $pid;
24 | $self->log(warn => "deleting $task->{name} from queue (pid $pid is gone)");
25 | $table->force_remove($task->{id});
26 | }
27 |
28 | $table->bulk_insert(\@rows, { ignore => 1 });
29 | }
30 |
31 | 1;
32 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/CheckRevision.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::CheckRevision;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use WWW::CPANTS::Model::Revision;
5 | use version;
6 |
7 | has 'revision' => \&_build_revision;
8 |
9 | sub _build_revision ($self) {
10 | WWW::CPANTS::Model::Revision->new;
11 | }
12 |
13 | sub run ($self, @args) {
14 | my $diff = $self->revision->check;
15 | if ($diff) {
16 | $self->log(notice => $diff);
17 | $self->revision->save;
18 | }
19 |
20 | my $packages = $self->cpan->packages;
21 | my $versions = $self->revision->data->{versions};
22 | for my $module (sort keys %$versions) {
23 | next if $module eq 'Archive::Zip'; ## Recent versions are broken
24 | my $installed = $versions->{$module};
25 | my $latest = $packages->find($module)->{version};
26 | if (version->parse($latest) > version->parse($installed)) {
27 | $self->log(notice => "$module: $latest (installed: $installed)");
28 | }
29 | }
30 | }
31 |
32 | 1;
33 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/Cleanup.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::Cleanup;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use WWW::CPANTS::Util::Datetime;
5 | use WWW::CPANTS;
6 | use Syntax::Keyword::Try;
7 |
8 | sub run ($self, @args) {
9 | my $app_root = WWW::CPANTS->instance->app_root;
10 |
11 | for my $target (qw/analyze test/) {
12 | my $tmp_dir = $app_root->child("tmp/$target");
13 | next unless -d $tmp_dir;
14 |
15 | for my $dir ($tmp_dir->children) {
16 | my $path = $dir->relative($app_root);
17 | if ($dir->stat->mtime < days_ago(1)->epoch) {
18 | try {
19 | $dir->remove_tree({ safe => 0 });
20 | $self->log(info => "Remove $path");
21 | } catch {
22 | $self->log(error => "Failed to remove $path: $@");
23 | };
24 | }
25 | }
26 | }
27 | }
28 |
29 | 1;
30 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/Migrate.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::Migrate;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | sub run ($self, @args) {
6 | my $db = $self->db;
7 | for my $name ($db->table_names) {
8 | my $table = $db->table($name);
9 | next unless $table->is_setup;
10 | $table->migrate;
11 | }
12 | }
13 |
14 | 1;
15 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/Setup.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::Setup;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | sub run ($self, @args) {
6 | my $db = $self->db;
7 | for my $name ($db->table_names) {
8 | my $table = $db->table($name);
9 | if ($table->is_setup) {
10 | $self->log(debug => "$name is ready");
11 | next;
12 | }
13 |
14 | $self->log(info => "setup $name");
15 | $table->setup;
16 | $table->migrate;
17 | }
18 | }
19 |
20 | 1;
21 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/ShowDDL.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::ShowDDL;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | our @OPTIONS = (
6 | 'table=s',
7 | );
8 |
9 | sub run ($self, @args) {
10 | my $table = $self->table or return;
11 | say $self->db->table($table)->schema;
12 | }
13 |
14 | 1;
15 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Maint/ShowStatus.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Maint::ShowStatus;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use WWW::CPANTS::Util::Datetime;
5 | use WWW::CPANTS::Model::Revision;
6 |
7 | has 'revision' => \&_build_revision;
8 |
9 | sub _build_revision ($self) {
10 | WWW::CPANTS::Model::Revision->new;
11 | }
12 |
13 | sub run ($self, @args) {
14 | $self->show_task_status;
15 | $self->show_cpants_revision;
16 | $self->show_queue_status;
17 | }
18 |
19 | sub show_task_status ($self) {
20 | my $table = $self->db->table('Tasks');
21 | return unless $table->is_setup;
22 |
23 | for my $task (@{ $table->select_all_running_tasks // [] }) {
24 | say sprintf "%s is running, started at %s (pid: %i)",
25 | $task->{name}, ymdhms($task->{started_at}), $task->{pid};
26 | }
27 | for my $task (@{ $table->select_all_waiting_tasks // [] }) {
28 | say sprintf "%s is waiting, created at %s",
29 | $task->{name}, ymdhms($task->{created_at});
30 | }
31 | }
32 |
33 | sub show_cpants_revision ($self) {
34 | my $cpants_revision = $self->revision->id;
35 | say "CPANTS revision: $cpants_revision";
36 |
37 | my $table = $self->db->table('Analysis');
38 | return unless $table->is_setup;
39 |
40 | if (my $uploads_with_older_revisions = $table->count_older_revisions($cpants_revision)) {
41 | say "$uploads_with_older_revisions rows with older revisions";
42 | }
43 | }
44 |
45 | sub show_queue_status ($self) {
46 | my $table = $self->db->table('Queue');
47 | return unless $table->is_setup;
48 |
49 | my $queued = $table->count;
50 | say "$queued rows in queue";
51 | }
52 |
53 | 1;
54 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Notify.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Notify;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use Furl::HTTP;
5 | use IO::Socket::SSL;
6 | use JSON::XS;
7 | use WWW::CPANTS;
8 |
9 | sub run ($self, @args) {
10 | my $config = WWW::CPANTS->instance->config->{notify} or return;
11 | if (my $slack = $config->{slack}) {
12 | my $ua = Furl::HTTP->new(
13 | ssl_opts => { SSL_verify_mode => SSL_VERIFY_NONE() },
14 | );
15 | my $to = $slack->{to} // '@here';
16 | my ($protocol, $code, $msg, $headers, $body) = $ua->post(
17 | $slack->{hook},
18 | ['Content-Type' => 'application/json'],
19 | encode_json({ text => "<$to> $0 has ended" }),
20 | );
21 | }
22 | }
23 |
24 | 1;
25 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/PostProcess.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::PostProcess;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | has subtasks => \&_build_subtasks;
6 |
7 | sub _build_subtasks ($self) {
8 | my @subtask_names = qw(
9 | UpdateAuthorStats
10 | UpdateSecurityAdvisories
11 | UpdateReverseDependency
12 | UpdateRanking
13 | UpdateCaches
14 | );
15 | [map { $self->subtask("PostProcess\::$_") } @subtask_names];
16 | }
17 |
18 | sub run ($self, @args) {
19 | $_->ctx($self->ctx)->run(@args) for $self->subtasks->@*;
20 | }
21 |
22 | 1;
23 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/PostProcess/UpdateAuthorStats.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::PostProcess::UpdateAuthorStats;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | our @READ = qw/Uploads/;
6 | our @WRITE = qw/Authors/;
7 |
8 | sub run ($self, @args) {
9 | $self->_update_release_stats;
10 | $self->_update_last_analyzed_at;
11 | }
12 |
13 | sub _update_release_stats ($self) {
14 | my $db = $self->db;
15 | my $authors = $db->table('Authors');
16 |
17 | my $rows = $db->table('Uploads')->select_release_stats;
18 | for my $row (@$rows) {
19 | my $pause_id = delete $row->{pause_id};
20 | $authors->update_release_stats($pause_id, $row);
21 | }
22 | }
23 |
24 | sub _update_last_analyzed_at ($self) {
25 | my $db = $self->db;
26 | my $authors = $db->table('Authors');
27 |
28 | my $started_at = $ENV{CPANTS_RUNNER_STARTED_AT} // 0;
29 | my $rows = $db->table('Analysis')->select_last_analyzed_at_since($started_at);
30 | for my $row (@$rows) {
31 | my $pause_id = delete $row->{author};
32 | $authors->update_last_analyzed_at($pause_id, $row->{last_analyzed_at});
33 | }
34 | }
35 |
36 | 1;
37 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/PostProcess/UpdateCaches.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::PostProcess::UpdateCaches;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use WWW::CPANTS::API::Context;
5 | use WWW::CPANTS::Util::Loader;
6 |
7 | sub run ($self, @args) {
8 | @args = submodule_names("WWW::CPANTS::API::Model") unless @args;
9 |
10 | my $ctx = WWW::CPANTS::API::Context->new(verbose => $self->ctx->verbose);
11 | for my $name (@args) {
12 | my $module = use_module("WWW::CPANTS::API::Model::$name");
13 | my $model = $module->new(ctx => $ctx);
14 | $model->save and $self->log(info => "cached $name");
15 | }
16 | }
17 |
18 | 1;
19 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/RunTasks.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::RunTasks;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | our @READS = qw/Tasks/;
6 | our @WRITES = qw/Tasks/;
7 |
8 | sub run ($self, @args) {
9 | my $tasks = $self->db->table('Tasks');
10 | while (my $task_info = $tasks->next) {
11 | my $subtask = $self->subtask($task_info->{name});
12 | $subtask->run;
13 | $tasks->remove($task_info->{id});
14 | }
15 | }
16 |
17 | 1;
18 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/Traverse/UpdateCPANStatus.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::Traverse::UpdateCPANStatus;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | our @READ = qw/Authors Uploads/;
6 | our @WRITE = qw/Uploads Kwalitee/;
7 |
8 | has 'changed' => sub ($self) { +{} };
9 | has 'uid_map' => sub ($self) { +{} };
10 |
11 | sub run ($self, @args) {
12 | # FIXME
13 | }
14 |
15 | sub load_uids_for ($self, $pause_id) {
16 | my %map = map { $_ => 0 } $self->db->table('Uploads')->select_all_cpan_uids_by_author($pause_id)->@*;
17 | $self->uid_map(\%map);
18 | $self->changed({});
19 | }
20 |
21 | sub has_uid ($self, $uid) {
22 | exists $self->uid_map->{$uid} ? 1 : 0;
23 | }
24 |
25 | sub mark ($self, $uid) {
26 | if (!$self->uid_map->{$uid}) {
27 | $self->changed->{$uid} = 1;
28 | }
29 | $self->uid_map->{$uid} = 1;
30 | }
31 |
32 | sub removed_uids ($self) {
33 | grep { !$self->uid_map->{$_} } keys $self->uid_map->%*;
34 | }
35 |
36 | sub changed_cpan_uids ($self) {
37 | grep { $self->changed->{$_} } keys $self->uid_map->%*;
38 | }
39 |
40 | sub mark_backpan ($self) {
41 | my $db = $self->db;
42 | if (my @removed_uids = $self->removed_uids) {
43 | $db->table('Uploads')->mark_backpan(\@removed_uids);
44 | $db->table('Kwalitee')->mark_backpan(\@removed_uids);
45 | }
46 | }
47 |
48 | sub mark_cpan ($self) {
49 | my $db = $self->db;
50 | if (my @cpan_uids = $self->changed_cpan_uids) {
51 | $db->table('Uploads')->mark_cpan(\@cpan_uids);
52 | $db->table('Kwalitee')->mark_cpan(\@cpan_uids);
53 | }
54 | }
55 |
56 | 1;
57 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/UpdateCPANIndices.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::UpdateCPANIndices;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 |
5 | has subtasks => \&_build_subtasks;
6 |
7 | sub _build_subtasks ($self) {
8 | my @subtask_names = qw(
9 | PackagesDetails
10 | Permissions
11 | Whois
12 | );
13 | [map { $self->subtask("UpdateCPANIndices\::$_") } @subtask_names];
14 | }
15 |
16 | sub run ($self, @args) {
17 | $_->ctx($self->ctx)->run(@args) for $self->subtasks->@*;
18 | }
19 |
20 | 1;
21 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/UpdateCPANIndices/PackagesDetails.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::UpdateCPANIndices::PackagesDetails;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use Syntax::Keyword::Try;
5 |
6 | our @WRITE = qw/PackagesDetails/;
7 |
8 | sub run ($self, @args) {
9 | my $table = $self->db->table('PackagesDetails');
10 |
11 | $self->log(info => "updating packages.details");
12 |
13 | try {
14 | my $txn = $table->txn;
15 | $table->truncate;
16 | $table->bulk_insert($self->cpan->packages->list);
17 | $txn->commit;
18 | } catch {
19 | $self->log(error => $@)
20 | }
21 |
22 | $self->log(info => "updated packages.details");
23 | }
24 |
25 | 1;
26 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/UpdateCPANIndices/Permissions.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::UpdateCPANIndices::Permissions;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use Syntax::Keyword::Try;
5 |
6 | our @WRITE = qw/Permissions/;
7 |
8 | sub run ($self, @args) {
9 | my $table = $self->db->table('Permissions');
10 |
11 | $self->log(info => "updating permissions");
12 |
13 | try {
14 | my $txn = $table->txn;
15 | $table->truncate;
16 | $table->bulk_insert($self->cpan->perms->list);
17 | $txn->commit;
18 | } catch {
19 | $self->log(error => $@)
20 | }
21 |
22 | $self->log(info => "updated permissions");
23 | }
24 |
25 | 1;
26 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Bin/Task/UpdateCPANIndices/Whois.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Bin::Task::UpdateCPANIndices::Whois;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Bin::Task', -signatures;
4 | use Syntax::Keyword::Try;
5 |
6 | our @WRITE = qw/Authors Whois/;
7 |
8 | sub run ($self, @args) {
9 | $self->log(info => "updating whois");
10 | $self->update_whois;
11 | $self->update_authors;
12 | $self->log(info => "updated whois");
13 | }
14 |
15 | sub update_whois ($self) {
16 | my $table = $self->db->table('Whois');
17 |
18 | try {
19 | my $txn = $table->txn;
20 | $table->truncate;
21 | $table->bulk_insert($self->cpan->whois->list);
22 | $txn->commit;
23 | } catch {
24 | $self->log(error => $@)
25 | }
26 | }
27 |
28 | sub update_authors ($self, @args) {
29 | my $table = $self->db->table('Authors');
30 |
31 | $table->bulk_insert($self->cpan->whois->list, { ignore => 1 });
32 | }
33 |
34 | 1;
35 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB;
2 |
3 | use Mojo::Base -base, -signatures;
4 | use WWW::CPANTS::Util::Loader;
5 |
6 | has 'handle' => \&_build_handle;
7 | has 'cache' => sub ($self) { +{} };
8 | has 'pid' => sub ($self) { $$ };
9 |
10 | sub _build_handle ($self) {
11 | my $config = WWW::CPANTS->instance->config->{db};
12 | my $name = $config->{handle_class} // "SQLite";
13 | my $module = use_module("WWW::CPANTS::DB::Handle::$name");
14 |
15 | $module->new(
16 | name => $name,
17 | config => $config->{$name},
18 | trace => $self->trace,
19 | );
20 | }
21 |
22 | sub table ($self, $name) {
23 | $self->check_pid;
24 | if (!exists $self->cache->{$name}) {
25 | my $table = use_module("WWW::CPANTS::DB::Table::$name")->new;
26 | $table->handle($self->handle->connect($table));
27 | $self->cache->{$name} = $table;
28 | }
29 | $self->cache->{$name};
30 | }
31 |
32 | sub trace ($self, $value = undef) {
33 | $value //= $ENV{CPANTS_API_TRACE};
34 | if (defined $value) {
35 | $self->{trace} = $value;
36 | for my $name (keys $self->cache->%*) {
37 | $self->cache->{$name}->handle->trace($value);
38 | }
39 | }
40 | $self->{trace};
41 | }
42 |
43 | sub table_names ($self) {
44 | submodule_names("WWW::CPANTS::DB::Table");
45 | }
46 |
47 | sub check_pid ($self) {
48 | return if $self->pid == $$;
49 | $self->cache({});
50 | $self->pid($$);
51 | }
52 |
53 | 1;
54 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/AcmeAuthors.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::AcmeAuthors;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
5 | use Syntax::Keyword::Try;
6 |
7 | with qw/WWW::CPANTS::Role::Logger/;
8 |
9 | sub columns ($self) { (
10 | [module_id => '_acme_id_'],
11 | [pause_id => '_pause_id_'],
12 | ) }
13 |
14 | sub indices ($self) { ({ unique => 1, columns => [qw/module_id pause_id/] }) }
15 |
16 | sub insert_for_module_id ($self, $module_id, $authors) {
17 | my $sql = <<~';';
18 | INSERT INTO acme_authors (module_id, pause_id) VALUES (?, ?);
19 | ;
20 | my @rows = map { [$module_id, $_] } @$authors;
21 | my $sth = $self->prepare($sql);
22 | my $txn = $self->txn;
23 | try {
24 | $sth->execute(@$_) for @rows;
25 | $txn->commit;
26 | } catch {
27 | my $error = $@;
28 | $self->log(error => $error);
29 | $txn->rollback;
30 | }
31 | }
32 |
33 | sub remove_by_module_id ($self, $module_id) {
34 | my $sql = <<~';';
35 | DELETE FROM acme_authors WHERE module_id = ?
36 | ;
37 | $self->do($sql, $module_id);
38 | }
39 |
40 | sub select_authors_by_module_id ($self, $module_id) {
41 | my $sql = <<~';';
42 | SELECT pause_id FROM acme_authors WHERE module_id = ?
43 | ;
44 | $self->select_all_col($sql, $module_id);
45 | }
46 |
47 | sub select_module_ids_by_pause_id ($self, $pause_id) {
48 | my $sql = <<~';';
49 | SELECT module_id FROM acme_authors WHERE pause_id = ?
50 | ;
51 | $self->select_all_col($sql, $pause_id);
52 | }
53 |
54 | 1;
55 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/AcmeModules.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::AcmeModules;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [module_id => '_acme_id_', primary => 1],
7 | [module => '_module_name_'],
8 | [version => '_version_string_'],
9 | [released => '_epoch_'],
10 | [authors => 'integer'],
11 | [updated_at => '_epoch_'],
12 | ) }
13 |
14 | sub indices ($self) { ({ unique => 1, columns => [qw/module/] }) }
15 |
16 | sub has_module_id ($self, $module_id) {
17 | my $sql = <<~';';
18 | SELECT 1 FROM acme_modules WHERE module_id = ?
19 | ;
20 | $self->select_col($sql, $module_id);
21 | }
22 |
23 | sub insert_module ($self, $module_id, $module, $version, $released, $authors, $mtime) {
24 | my $sql = <<~';';
25 | INSERT INTO acme_modules (module_id, module, version, released, authors, updated_at)
26 | VALUES (?, ?, ?, ?, ?, ?)
27 | ;
28 | $self->do($sql, $module_id, $module, $version, $released, $authors, $mtime);
29 | }
30 |
31 | sub update_module ($self, $module_id, $module, $version, $released, $authors, $mtime) {
32 | my $sql = <<~';';
33 | UPDATE acme_modules
34 | SET version = ?, released = ?, authors = ?, updated_at = ?
35 | WHERE module_id = ?
36 | ;
37 | $self->do($sql, $version, $released, $authors, $mtime, $module_id);
38 | }
39 |
40 | sub remove_by_module_id ($self, $module_id) {
41 | my $sql = <<~';';
42 | DELETE FROM acme_modules WHERE module_id = ?
43 | ;
44 | $self->do($sql, $module_id);
45 | }
46 |
47 | sub select_modules ($self) {
48 | my $sql = <<~';';
49 | SELECT module_id, module, version, released, authors FROM acme_modules;
50 | ;
51 | $self->select_all($sql);
52 | }
53 |
54 | 1;
55 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/PackagesDetails.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::PackagesDetails;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [uid => '_upload_id_'],
7 | [module => '_module_name_'],
8 | [version => '_version_string_'],
9 | [path => '_cpan_path_'],
10 | [dist => '_dist_name_'],
11 | ) }
12 |
13 | sub indices ($self) { (
14 | [qw/module/],
15 | ) }
16 |
17 | sub select_all_by_modules ($self, $modules) {
18 | my $sql = <<~';';
19 | SELECT * FROM packages_details
20 | WHERE module IN (:modules)
21 | ;
22 | $self->select_all($sql, [modules => $modules]);
23 | }
24 |
25 | sub select_unique_dists_by_modules ($self, $modules) {
26 | my $sql = <<~';';
27 | SELECT DISTINCT(id), path FROM packages_details
28 | WHERE id IS NOT NULL AND module IN (:modules)
29 | ;
30 | $self->select_all($sql, [modules => $modules]);
31 | }
32 |
33 | 1;
34 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/Permissions.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::Permissions;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [module => '_module_name_'],
7 | [pause_id => '_pause_id_'],
8 | [type => 'varchar(1)'],
9 | ) }
10 |
11 | sub indices ($self) { (
12 | [qw/pause_id/],
13 | [qw/module/],
14 | ) }
15 |
16 | 1;
17 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/Provides.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::Provides;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [uid => '_upload_id_', primary => 1],
7 | [pause_id => '_pause_id_'],
8 | [modules => '_json_'],
9 | [provides => '_json_'],
10 | [special_files => '_json_'],
11 | [unauthorized => '_json_'],
12 | ) }
13 |
14 | sub select_by_uid ($self, $uid) {
15 | my $sql = <<~';';
16 | SELECT * FROM provides WHERE uid = ?
17 | ;
18 | $self->select($sql, $uid);
19 | }
20 |
21 | sub delete_by_uids ($self, $uids) {
22 | my $sql = <<~';';
23 | DELETE FROM provides WHERE uid IN (:uids)
24 | ;
25 | $self->delete($sql, [uids => $uids]);
26 | }
27 |
28 | sub update_provides ($self, $uid, $pause_id, $modules, $provides, $special_files, $unauthorized) {
29 | my $sql = <<~';';
30 | UPDATE provides
31 | SET
32 | pause_id = ?,
33 | modules = ?,
34 | provides = ?,
35 | special_files = ?,
36 | unauthorized = ?
37 | WHERE uid = ?
38 | ;
39 | $self->update($sql, $pause_id, $modules, $provides, $special_files, $unauthorized, $uid);
40 | }
41 |
42 | 1;
43 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/RequiresAndUses.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::RequiresAndUses;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [uid => '_upload_id_', primary => 1],
7 | [pause_id => '_pause_id_'],
8 | [requires => '_json_'],
9 | [uses => '_json_'],
10 | ) }
11 |
12 | sub select_requires_by_uid ($self, $uid) {
13 | my $sql = <<~';';
14 | SELECT requires FROM requires_and_uses WHERE uid = ?
15 | ;
16 | $self->select_col($sql, $uid);
17 | }
18 |
19 | sub update_requires_and_uses ($self, $uid, $pause_id, $requires, $uses) {
20 | my $sql = <<~';';
21 | UPDATE requires_and_uses
22 | SET pause_id = ?, requires = ?, uses = ?
23 | WHERE uid = ?
24 | ;
25 | $self->update($sql, $pause_id, $requires, $uses, $uid);
26 | }
27 |
28 | sub delete_by_uids ($self, $uids) {
29 | my $sql = <<~';';
30 | DELETE FROM requires_and_uses WHERE uid IN (:uids)
31 | ;
32 | $self->delete($sql, [uids => $uids]);
33 | }
34 |
35 | 1;
36 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/Resources.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::Resources;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [uid => '_upload_id_', primary => 1],
7 | [pause_id => '_pause_id_'],
8 | [resources => '_json_'],
9 | [repository_url => 'varchar(255)'],
10 | [bugtracker_url => 'varchar(255)'],
11 | ) }
12 |
13 | sub select_by_uid ($self, $uid) {
14 | my $sql = <<~';';
15 | SELECT * FROM resources WHERE uid = ?
16 | ;
17 | $self->select($sql, $uid);
18 | }
19 |
20 | sub delete_by_uids ($self, $uids) {
21 | my $sql = <<~';';
22 | DELETE FROM resources WHERE uid IN (:uids)
23 | ;
24 | $self->delete($sql, [uids => $uids]);
25 | }
26 |
27 | sub update_resources ($self, $uid, $pause_id, $resources, $repository, $bugtracker) {
28 | my $sql = <<~';';
29 | UPDATE resources
30 | SET pause_id = ?, resources = ?, repository_url = ?, bugtracker_url = ?
31 | WHERE uid = ?
32 | ;
33 | $self->update($sql, $pause_id, $resources, $repository, $bugtracker, $uid);
34 | }
35 |
36 | 1;
37 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/DB/Table/Whois.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::DB::Table::Whois;
2 |
3 | use Mojo::Base 'WWW::CPANTS::DB::Table', -signatures;
4 |
5 | sub columns ($self) { (
6 | [pause_id => '_pause_id_', primary => 1],
7 | [name => 'varchar(255)', character_set => 'utf8mb4'],
8 | [ascii_name => 'varchar(255)'],
9 | [email => 'varchar(255)'],
10 | [homepage => 'varchar(255)'],
11 | [has_cpandir => '_bool_', default => 0],
12 | [introduced => '_epoch_'],
13 | [year => '_year_'],
14 | [nologin => '_bool_', default => 0],
15 | [deleted => '_bool_', default => 0],
16 | [system => '_bool_', default => 0],
17 | ) }
18 |
19 | sub select_all_by_pause_ids ($self, $pause_ids) {
20 | my $sql = <<~";";
21 | SELECT * FROM whois WHERE pause_id IN (:pause_ids)
22 | ;
23 | $self->select_all($sql, [pause_ids => $pause_ids]);
24 | }
25 |
26 | sub author_stats_of_the_year ($self, $year, $pause_ids) {
27 | my $cond = $year ? '=' : '!=';
28 | my $sql = <<~";";
29 | SELECT COUNT(*) AS new_authors FROM whois
30 | WHERE year $cond ? AND pause_id IN (:pause_ids)
31 | ;
32 | $self->select($sql, $year, [pause_ids => $pause_ids]);
33 | }
34 |
35 | 1;
36 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/CPAN.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::CPAN;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use Path::Tiny ();
6 |
7 | has 'path' => \&_path_is_required;
8 | has 'root' => \&_build_root;
9 |
10 | with qw(
11 | WWW::CPANTS::Role::CPAN::Indices
12 | WWW::CPANTS::Role::CPAN::Path
13 | );
14 |
15 | sub _path_is_required ($self) {
16 | Carp::confess "path is required";
17 | }
18 |
19 | sub _build_root ($self) {
20 | my $root = Path::Tiny::path($self->path);
21 | Carp::croak "$root does not exist" unless -d $root;
22 | Carp::croak "$root seems not a CPAN mirror" unless -d $root->child("authors/id");
23 | $root;
24 | }
25 |
26 | sub preload_indices ($self) {
27 | $_->preload for $self->indices->@*;
28 | }
29 |
30 | 1;
31 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/CPAN/Perms.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::CPAN::Perms;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 |
6 | has 'path' => 'modules/06perms.txt';
7 | has 'perms' => \&_build_perms;
8 |
9 | with qw/WWW::CPANTS::Role::CPAN::Index/;
10 |
11 | sub _build_perms ($self) {
12 | my $file = $self->fetch;
13 | my $fh = $file->openr;
14 | my %perms;
15 | my $seen_header;
16 | while (<$fh>) {
17 | chomp;
18 | if (!$seen_header) {
19 | if (/^$/) {
20 | $seen_header = 1;
21 | }
22 | next;
23 | }
24 |
25 | my ($module, $pause_id, $type) = split /,/;
26 | if (!$module or !$pause_id or !$type) { ## Broken for whatever reasons
27 | $self->log(warn => "06perms is broken");
28 | close $fh;
29 | unlink $self->file;
30 | return $self->_build_perms;
31 | }
32 | $perms{ lc $module }{ uc $pause_id } = {
33 | module => $module,
34 | pause_id => $pause_id,
35 | type => $type,
36 | };
37 | }
38 | \%perms;
39 | }
40 |
41 | sub list ($self) {
42 | my @rows = map { values %$_ } values $self->perms->%*;
43 | \@rows;
44 | }
45 |
46 | sub can_upload ($self, $pause_id, $module) {
47 | $pause_id = uc $pause_id;
48 | $module = lc $module;
49 |
50 | return 1 if !exists $self->perms->{$module};
51 | return 1 if exists $self->perms->{$module}{$pause_id};
52 | return;
53 | }
54 |
55 | sub preload ($self) { $self->perms }
56 |
57 | 1;
58 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/CPAN/Recent.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::CPAN::Recent;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use WWW::CPANTS::Util::JSON;
6 | use Class::Method::Modifiers;
7 |
8 | with qw/WWW::CPANTS::Role::CPAN::Index/;
9 |
10 | has 'root' => \&_root_is_required;
11 | has 'type' => '6h';
12 | has 'path' => \&_build_path;
13 | has 'file' => \&_build_file;
14 | has 'distributions' => \&_build_distributions;
15 |
16 | our %Types = map { $_ => 1 } qw/1M 1Q 1W 1Y 1d 1h 6h Z/;
17 |
18 | before 'type' => sub ($self, @args) {
19 | return unless @args;
20 | my $type = $args[0];
21 | Carp::croak "Invalid type: $type" unless exists $Types{$type};
22 | };
23 |
24 | sub _root_is_required ($self) {
25 | Carp::confess "root is required";
26 | }
27 |
28 | sub _build_path ($self) {
29 | my $type = $self->type;
30 | return "RECENT-$type.json";
31 | }
32 |
33 | sub _build_file ($self) {
34 | $self->root->child($self->path);
35 | }
36 |
37 | sub _build_distributions ($self) {
38 | my $file = $self->fetch;
39 |
40 | decode_json($file->slurp_utf8)->{recent};
41 | }
42 |
43 | sub preload ($self) { $self->distributions }
44 |
45 | 1;
46 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/CPAN/Whois.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::CPAN::Whois;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use Parse::CPAN::Whois;
6 | use WWW::CPANTS::Util::Datetime;
7 |
8 | has 'path' => 'authors/00whois.xml';
9 | has 'authors' => \&_build_authors;
10 |
11 | with qw/WWW::CPANTS::Role::CPAN::Index/;
12 |
13 | our %SYSTEM_USER = map { $_ => 1 } qw(
14 | ADOPTME
15 | HANDOFF
16 | NEEDHELP
17 | NOXFER
18 | PAUSE
19 | RECAPTCHA
20 | );
21 |
22 | sub _build_authors ($self) {
23 | my $file = $self->fetch;
24 |
25 | $XML::SAX::ParserPackage = "XML::LibXML::SAX";
26 | my %authors;
27 | for my $author (Parse::CPAN::Whois->new("$file")->authors) {
28 | my $pause_id = $author->{id};
29 |
30 | $authors{$pause_id} = {
31 | pause_id => $pause_id,
32 | name => $author->{fullname},
33 | ascii_name => $author->{asciiname} // $author->{fullname},
34 | email => $author->{email} // '',
35 | homepage => $author->{homepage} // '',
36 | has_cpandir => $author->{has_cpandir} // 0,
37 | introduced => $author->{introduced} // 0,
38 | year => year($author->{introduced} // 0),
39 | nologin => $author->{nologin} // 0,
40 | deleted => $author->{deleted} // 0,
41 | system => (exists $SYSTEM_USER{$pause_id} ? 1 : 0),
42 | };
43 | }
44 | \%authors;
45 | }
46 |
47 | sub list ($self) {
48 | my @rows = values $self->authors->%*;
49 | \@rows;
50 | }
51 |
52 | sub preload ($self) { $self->authors }
53 |
54 | 1;
55 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/PidFile.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::PidFile;
2 |
3 | use Mojo::Base -base, -signatures;
4 | use WWW::CPANTS::Util::Path;
5 |
6 | has 'id' => \&_id_is_required;
7 | has 'file' => \&_build_file;
8 | has 'pid' => sub ($self) { $$ };
9 |
10 | sub _id_is_required ($self) {
11 | Carp::confess "id is required";
12 | }
13 |
14 | sub _build_file ($self) {
15 | my $id = $self->id;
16 | cpants_path("tmp/pid/$id.pid");
17 | }
18 |
19 | sub exists ($self) {
20 | -f $self->file ? 1 : 0;
21 | }
22 |
23 | sub slurp ($self) {
24 | $self->exists ? $self->file->slurp : undef;
25 | }
26 |
27 | sub spew ($self, $pid) {
28 | $self->file->parent->mkpath;
29 | $self->file->spew($pid);
30 | }
31 |
32 | sub DESTROY ($self) {
33 | return unless $self->pid eq $$;
34 | my $pid = $self->slurp or return;
35 | $self->file->remove if $pid eq $self->pid;
36 | }
37 |
38 | 1;
39 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/TableGuard.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::TableGuard;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use WWW::CPANTS::Util::Path;
6 |
7 | has 'dir' => \&_build_dir;
8 | has 'pid' => sub ($self) { $$ };
9 | has 'files';
10 |
11 | with qw/WWW::CPANTS::Role::Logger/;
12 |
13 | sub _build_dir ($self) {
14 | my $dir = cpants_path("tmp/table_guard");
15 | $dir->mkpath unless -d $dir;
16 | $dir;
17 | }
18 |
19 | sub check ($self, @tables) {
20 | my @files;
21 | for my $table (@tables) {
22 | my $file = $self->dir->child($table);
23 | if (-f $file) {
24 | my $pid = $file->slurp;
25 | $self->log(info => "Another task ($pid) is using $table");
26 | $_->remove for @files;
27 | return;
28 | }
29 | $file->spew($$);
30 | push @files, $file;
31 | }
32 | $self->files(\@files);
33 | return 1;
34 | }
35 |
36 | sub DESTROY ($self) {
37 | my $files = $self->files or return;
38 | for my $file (@$files) {
39 | next unless -f $file;
40 | my $pid = $file->slurp;
41 | $file->remove if $pid eq $self->pid;
42 | }
43 | }
44 |
45 | 1;
46 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/TempDir.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::TempDir;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use File::Path::Tiny ();
6 | use Path::Tiny ();
7 | use File::Temp ();
8 | use Syntax::Keyword::Try;
9 | use overload
10 | '""' => \&_stringify,
11 | '0+' => \&_stringify,
12 | 'bool' => \&_stringify,
13 | 'cmp' => \&_stringify;
14 |
15 | our $AUTOLOAD;
16 |
17 | has 'root' => \&_root_is_required;
18 | has 'path' => \&_build_path;
19 | has 'pid' => sub ($self) { $$ };
20 |
21 | with qw/WWW::CPANTS::Role::Logger/;
22 |
23 | sub _root_is_required ($self) {
24 | Carp::confess "root is required";
25 | }
26 |
27 | sub _build_path ($self) {
28 | my $root = $self->root;
29 | $root->mkpath unless -d $root;
30 | my $path = Path::Tiny::path(File::Temp->newdir(
31 | DIR => $root->path,
32 | TEMPLATE => join('.', time, $$, "XXXXXXXX"),
33 | ));
34 | $path->mkpath unless -d $path;
35 | $path;
36 | }
37 |
38 | sub path_str ($self) { $self->path->stringify }
39 |
40 | sub _stringify ($self, @args) { $self->path . "" }
41 |
42 | sub AUTOLOAD ($self, @args) {
43 | return if $AUTOLOAD =~ /::DESTROY$/;
44 | $AUTOLOAD =~ s/.*:://;
45 | $self->path->$AUTOLOAD(@args);
46 | }
47 |
48 | sub DESTROY ($self) {
49 | return unless $self->pid eq $$;
50 | my $path = $self->path;
51 | return unless $path && -e $path;
52 | try { File::Path::Tiny::rm($path) }
53 | catch {
54 | my $error = $@;
55 | warn "[ALERT] Failed to remove tmpdir: $error [$$] $@\n";
56 | }
57 | if (-e $path) {
58 | warn "[ALERT] Failed to remove tmpdir $! [$$]\n";
59 | }
60 | }
61 |
62 | 1;
63 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Model/Timer.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Model::Timer;
2 |
3 | use Role::Tiny::With;
4 | use Mojo::Base -base, -signatures;
5 | use Time::Duration qw/duration/;
6 | use Time::HiRes qw/time/;
7 | use WWW::CPANTS::Util::Datetime qw/strftime/;
8 |
9 | with qw/WWW::CPANTS::Role::Logger/;
10 |
11 | has 'name' => 'Timer';
12 | has 'total';
13 | has 'started_at';
14 | has 'pid';
15 |
16 | sub start ($self) {
17 | my $name = $self->name;
18 | return if $name =~ /^Maint::/;
19 |
20 | $self->started_at(time);
21 | $self->pid($$);
22 | $self->log(notice => "[$name] started [$$]");
23 | }
24 |
25 | sub show_progress ($self, $done) {
26 | return unless $done;
27 | return unless $self->started_at;
28 |
29 | my $name = $self->name;
30 | my $elapsed = time - $self->started_at;
31 | if (my $total = $self->total) {
32 | my $estimate = sprintf "(%0.2f/s; estimated end time: %s) ",
33 | ($elapsed ? $done / $elapsed : '-'),
34 | strftime("%Y-%m-%d %H:%M", $elapsed * $total / $done + $self->started_at);
35 | $self->log(info => "[$name] $done/$total $estimate [$$]");
36 | } else {
37 | $self->log(info => "[$name] $done [$$]");
38 | }
39 | }
40 |
41 | sub DESTROY ($self) {
42 | return unless $self->started_at and $self->pid eq $$;
43 | local $Time::Duration::MILLISECOND = 1;
44 | my $duration = duration(time - $self->started_at);
45 | my $name = $self->name;
46 | $self->log(notice => "[$name] ended (elapsed $duration) [$$]");
47 | }
48 |
49 | 1;
50 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/API/Model/V5/Author/Status.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::API::Model::V5::Author::Status;
2 |
3 | use Mojo::Base -role, -signatures;
4 |
5 | sub check_whois_status ($self, $pause_id) {
6 | my $db = $self->db;
7 | my $whois = $db->table('Whois')->find($pause_id) or return $self->bad_request("'$pause_id' not found");
8 |
9 | # Do not expose deleted user's information
10 | if ($whois->{deleted}) {
11 | return unless wantarray;
12 | return (undef, { pause_id => $pause_id, deleted => 1 });
13 | }
14 |
15 | # Hide spammers too
16 | if ($whois->{nologin} and !$whois->{system}) {
17 | return unless wantarray;
18 | return (undef, { pause_id => $pause_id, banned => 1 });
19 | }
20 |
21 | return 1 unless wantarray;
22 | return (1, $whois);
23 | }
24 |
25 | 1;
26 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/CPAN/Indices.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::CPAN::Indices;
2 |
3 | use Mojo::Base -role, -signatures;
4 | use WWW::CPANTS::Model::CPAN::Packages;
5 | use WWW::CPANTS::Model::CPAN::Perms;
6 | use WWW::CPANTS::Model::CPAN::Whois;
7 | use WWW::CPANTS::Model::CPAN::Recent;
8 |
9 | requires 'root';
10 |
11 | has 'packages' => \&_build_packages;
12 | has 'perms' => \&_build_perms;
13 | has 'whois' => \&_build_whois;
14 | has 'recent' => \&_build_recent;
15 |
16 | sub indices ($self) {
17 | my @indices = map { $self->$_ } qw/packages perms whois recent/;
18 | \@indices;
19 | }
20 |
21 | sub _build_packages ($self) {
22 | WWW::CPANTS::Model::CPAN::Packages->new(root => $self->root);
23 | }
24 |
25 | sub _build_perms ($self) {
26 | WWW::CPANTS::Model::CPAN::Perms->new(root => $self->root);
27 | }
28 |
29 | sub _build_whois ($self) {
30 | WWW::CPANTS::Model::CPAN::Whois->new(root => $self->root);
31 | }
32 |
33 | sub _build_recent ($self) {
34 | WWW::CPANTS::Model::CPAN::Recent->new(root => $self->root);
35 | }
36 |
37 | 1;
38 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/CPAN/Path.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::CPAN::Path;
2 |
3 | use Mojo::Base -role, -signatures;
4 |
5 | sub child ($self, $path) { $self->root->child($path) }
6 |
7 | sub distribution ($self, $path) {
8 | $self->child("authors/id/$path");
9 | }
10 |
11 | sub author_dir ($self, $pause_id) {
12 | my $path = join "/", substr($pause_id, 0, 1), substr($pause_id, 0, 2), $pause_id;
13 | my $dir = $self->child("authors/id/$path");
14 | return unless -d $dir;
15 | $dir;
16 | }
17 |
18 | sub author_dir_iterator ($self, $pause_id) {
19 | my $dir = $self->author_dir($pause_id) or return;
20 | $dir->iterator({ recurse => 1, follow_symlinks => 0 });
21 | }
22 |
23 | 1;
24 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/Logger.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::Logger;
2 |
3 | use Mojo::Base -role, -signatures;
4 |
5 | sub log ($self, $level, $message) {
6 | WWW::CPANTS->instance->logger->log($level => $message);
7 | }
8 |
9 | 1;
10 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/Options.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::Options;
2 |
3 | use Mojo::Base -role, -signatures;
4 |
5 | my @SeenOptions;
6 |
7 | sub options ($self) {
8 | my $class = ref $self || $self;
9 | no strict 'refs';
10 | @{"$class\::OPTIONS"};
11 | }
12 |
13 | around 'new' => sub ($orig, $class, @args) {
14 | my @rules;
15 | for my $option ($class->options) {
16 | my ($rule, @attr_args) = ref $option ? @$option : ($option);
17 | my ($name) = $rule =~ /^(\w+)/;
18 | Mojo::Base::attr($class, $name, @attr_args);
19 | push @rules, $rule;
20 | }
21 |
22 | Mojo::Util::getopt \@ARGV, [qw/bundling pass_through/],
23 | \my %opts => @rules;
24 |
25 | push @SeenOptions, @rules;
26 |
27 | if (%opts) {
28 | if (@args == 1 and ref $args[0] eq 'HASH') {
29 | $args[0]{$_} = $opts{$_} for keys %opts;
30 | } else {
31 | push @args, %opts;
32 | }
33 | }
34 | $orig->($class, @args);
35 | };
36 |
37 | sub show_help ($self) {
38 | say "options:";
39 | for my $option (@SeenOptions) {
40 | my $name = ref $option ? $option->[0] : $option;
41 | $name =~ s/[\|=].+$//;
42 | say " $name";
43 | }
44 | }
45 |
46 | 1;
47 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Role/Task/FixAnalysis.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Role::Task::FixAnalysis;
2 |
3 | use Mojo::Base -role, -signatures;
4 | use WWW::CPANTS::Util::JSON;
5 | use WWW::CPANTS::Util::HideInternal;
6 | use WWW::CPANTS::Util::PathUid;
7 | use Parse::Distname qw/parse_distname/;
8 |
9 | sub run ($self, @args) {
10 | if (@args) {
11 | $self->fix_with_args(\@args);
12 | } else {
13 | $self->fix_all;
14 | }
15 | }
16 |
17 | sub fix_all ($self) {
18 | my $table = $self->db->table('Analysis');
19 | my $iterator = $table->iterate;
20 | while (my $row = $iterator->next) {
21 | my ($uid, $path, $json) = @$row{qw/uid path json/};
22 | next unless $json;
23 | my $stash = decode_json($json);
24 | $self->update($uid, $stash) or next;
25 | $self->log(info => "fixed $path");
26 |
27 | if ($self->dump) {
28 | say hide_internal(encode_pretty_json($stash));
29 | }
30 | }
31 | }
32 |
33 | sub fix_with_args ($self, $args) {
34 | my $table = $self->db->table('Analysis');
35 | for my $path (@$args) {
36 | next unless defined $path and $path ne '';
37 | my $info = parse_distname($path);
38 | if (!$info) {
39 | $self->log(error => "$path seems not a CPAN distribution");
40 | next;
41 | }
42 |
43 | $path = $info->{cpan_path};
44 | my $uid = path_uid($path);
45 | my $json = $table->select_json_by_uid($uid) or next;
46 | my $stash = decode_json($json);
47 | $self->update($uid, $stash) or next;
48 | $self->log(info => "fixed $path");
49 |
50 | if ($self->dump) {
51 | say hide_internal(encode_pretty_json($stash));
52 | }
53 | }
54 | }
55 |
56 | 1;
57 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/CoreList.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::CoreList;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use Module::CoreList;
6 |
7 | our @EXPORT = qw/is_core core_since removed_core_since deprecated_core_since/;
8 | our $MinPerlVersion = 5.008001;
9 |
10 | sub is_core ($module, $perl_version = $MinPerlVersion) {
11 | if (!$perl_version or $perl_version < $MinPerlVersion) {
12 | $perl_version = $MinPerlVersion;
13 | }
14 | Module::CoreList::is_core($module, undef, $perl_version);
15 | }
16 |
17 | sub core_since ($module, $version = 0) {
18 | Module::CoreList::first_release($module, $version);
19 | }
20 |
21 | sub removed_core_since ($module) {
22 | Module::CoreList::removed_from($module);
23 | }
24 |
25 | sub deprecated_core_since ($module) {
26 | Module::CoreList::deprecated_in($module);
27 | }
28 |
29 | 1;
30 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Datetime.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Datetime;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use Time::Moment;
6 | use Time::Zone qw/tz_local_offset/;
7 |
8 | our @EXPORT = qw(
9 | strftime ymd ymdhms days_ago year datetime
10 | epoch_from_date
11 | );
12 |
13 | our $TZ_OFFSET = tz_local_offset() / 60;
14 |
15 | sub _from_epoch ($epoch = time) {
16 | Time::Moment->from_epoch($epoch // time)->with_offset_same_instant($TZ_OFFSET);
17 | }
18 |
19 | sub strftime ($format, $epoch = time) {
20 | _from_epoch($epoch // time)->strftime($format);
21 | }
22 |
23 | sub ymd ($epoch) { strftime('%F', $epoch) }
24 |
25 | sub ymdhms ($epoch) { strftime('%F %T', $epoch) }
26 |
27 | sub datetime ($epoch) { strftime('%FT%TZ', $epoch) }
28 |
29 | sub year ($epoch) { _from_epoch($epoch)->year }
30 |
31 | sub days_ago ($days) { _from_epoch()->minus_days($days) }
32 |
33 | sub epoch_from_date ($date) {
34 | my ($year, $month, $day) = split '-', $date;
35 | Time::Moment->new(
36 | year => $year,
37 | month => $month,
38 | day => $day,
39 | )->epoch;
40 | }
41 |
42 | 1;
43 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Diff.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Diff;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 |
6 | our @EXPORT = qw/diff/;
7 | our @EXPORT_OK = qw/kwalitee_diff/;
8 |
9 | if (eval { require Text::Diff::Unified::XS; 1 }) {
10 | *diff = \&_xs_diff;
11 | } else {
12 | require Text::Diff;
13 | *diff = \&_pp_diff;
14 | }
15 |
16 | sub _xs_diff ($old, $new) {
17 | Text::Diff::Unified::XS::diff($old, $new);
18 | }
19 |
20 | sub _pp_diff ($old, $new) {
21 | Text::Diff::diff($old, $new, { STYLE => 'Unified' });
22 | }
23 |
24 | sub kwalitee_diff ($old, $new) {
25 | my @diff;
26 | for my $key (sort keys $new->{kwalitee}->%*) {
27 | my $old_value = $old->{kwalitee}{$key} // '';
28 | my $new_value = $new->{kwalitee}{$key} // '';
29 | if ($old_value ne $new_value) {
30 | push @diff, "$key: $old_value=>$new_value";
31 | }
32 | }
33 | return join ', ', @diff;
34 | }
35 |
36 | 1;
37 |
38 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/HideInternal.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::HideInternal;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use WWW::CPANTS;
6 |
7 | our @EXPORT = qw/hide_internal/;
8 |
9 | sub hide_internal ($str) {
10 | my $root = WWW::CPANTS->instance->app_root;
11 | my ($home) = $root =~ m|^((?:[A-Z]:)?/home/[^/]+)|i;
12 | no warnings 'uninitialized';
13 | $str =~ s!$home/\.plenv/versions/[^/]+/lib/perl5/(site_perl/)?5\.\d+\.\d+/!$1lib/!g;
14 | $str =~ s!$home/((?:backpan|cpan)/)!$1!g;
15 | $str =~ s!$root/tmp/analyze/[^/]+/[^/]+/!!g;
16 | $str =~ s!$root/extlib/[^/]+/!!g;
17 | $str =~ s!$root/!!g;
18 | $str =~ s!$home/!!g;
19 | $str;
20 | }
21 |
22 | 1;
23 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Loader.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Loader;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Module::Find qw/findallmod/;
5 | use Module::Runtime ();
6 | use Exporter 5.57 qw/import/;
7 |
8 | our @EXPORT = qw/use_module submodules submodule_names/;
9 | our %CACHE;
10 |
11 | sub use_module ($name) {
12 | $CACHE{$name} //= Module::Runtime::use_module($name);
13 | }
14 |
15 | sub submodules ($namespace) {
16 | my %map;
17 |
18 | $Module::Find::followMode = 0;
19 | for my $package (findallmod $namespace) {
20 | my $name = $package =~ s/^${namespace}:://r;
21 | $map{$name} = $package;
22 | }
23 | \%map;
24 | }
25 |
26 | sub submodule_names ($namespace) {
27 | sort keys submodules($namespace)->%*;
28 | }
29 |
30 | 1;
31 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Parallel.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Parallel;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Parallel::Runner;
5 | use Exporter qw/import/;
6 | use Syntax::Keyword::Try;
7 | use WWW::CPANTS;
8 |
9 | our @EXPORT = qw/parallel/;
10 |
11 | sub parallel ($workers, $code) {
12 | $workers = 0 if $INC{'Devel/Cover.pm'} or $^O eq 'MSWin32';
13 |
14 | my $runner =
15 | ($workers)
16 | ? Parallel::Runner->new($workers)
17 | : WWW::CPANTS::Util::Parallel::Dummy->new($workers);
18 |
19 | $0 = "$^X $0 @ARGV (master)" unless $0 =~ /\((?:master|worker)\)/;
20 |
21 | my $runner_r = \$runner;
22 | local $SIG{TERM} = sub {
23 | warn "CAUGHT TERM";
24 | $$runner_r->killall('TERM');
25 | $$runner_r->finish;
26 | exit;
27 | };
28 | local $SIG{INT} = sub {
29 | warn "CAUGHT INT";
30 | $$runner_r->killall('INT');
31 | $$runner_r->finish;
32 | exit;
33 | };
34 |
35 | try { $code->($runner) }
36 | catch {
37 | my $error = $@;
38 | WWW::CPANTS->instance->logger->log(error => $error);
39 | }
40 |
41 | $runner->finish;
42 | }
43 |
44 | package WWW::CPANTS::Util::Parallel::Dummy;
45 |
46 | use Mojo::Base -strict, -signatures;
47 | use WWW::CPANTS;
48 |
49 | sub new ($class, $max_workers) { bless {}, $class }
50 |
51 | sub run ($self, $code) {
52 | try { $code->() }
53 | catch {
54 | my $error = $@;
55 | WWW::CPANTS->instance->logger->log(error => $error);
56 | }
57 | }
58 | sub pid ($self) { $$ }
59 | sub killall ($self, $sig) { kill $sig, $$ }
60 | sub finish ($self) { }
61 | sub max ($self) { 1 }
62 |
63 | 1;
64 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Path.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Path;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use WWW::CPANTS;
6 |
7 | our @EXPORT = qw/cpants_app_path cpants_path/;
8 |
9 | sub cpants_app_path ($path) {
10 | WWW::CPANTS->instance->app_root->child($path);
11 | }
12 |
13 | sub cpants_path ($path) {
14 | WWW::CPANTS->instance->root->child($path);
15 | }
16 |
17 | 1;
18 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/PathUid.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::PathUid;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use Digest::FNV::XS ();
6 |
7 | our @EXPORT = qw/path_uid/;
8 |
9 | sub path_uid ($path) {
10 | return unless defined $path;
11 | Digest::FNV::XS::fnv1a_64($path);
12 | }
13 |
14 | 1;
15 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Util/Version.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Util::Version;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Exporter qw/import/;
5 | use version;
6 | use Syntax::Keyword::Try;
7 |
8 | our @EXPORT = qw/numify_version/;
9 |
10 | sub numify_version ($version) {
11 | try {
12 | no warnings;
13 | my $num = version->parse($version)->numify;
14 | $num =~ s/_//g;
15 | return $num || 0;
16 | } catch {
17 | return 0;
18 | }
19 | }
20 |
21 | 1;
22 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Context.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Context;
2 |
3 | use Mojo::Base -base, -signatures;
4 | use Mojo::URL;
5 |
6 | has 'api_ctx' => \&_build_api_ctx;
7 | has 'base_url' => \&_build_base_url;
8 |
9 | sub _build_api_ctx ($self) {
10 | require WWW::CPANTS::API::Context;
11 | WWW::CPANTS::API::Context->new;
12 | }
13 |
14 | sub _build_base_url ($self) {
15 | Mojo::URL->new('https://cpants.cpanauthors.org');
16 | }
17 |
18 | sub api_url ($self, $path, $query = undef) {
19 | my $url = $self->api_ctx->api_base->clone;
20 | $path =~ s|^/||;
21 | $url->path($path);
22 | $url->query(%$query) if $query;
23 | $url;
24 | }
25 |
26 | 1;
27 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Home.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Home;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | no warnings qw/deprecated/;
6 |
7 | sub index ($c) {
8 | $c->render_with(
9 | sub ($c, $params, $format) {
10 | my $res = $c->get_api('Recent') or return;
11 |
12 | given ($format) {
13 | when ('') {
14 | return {
15 | render => 'home',
16 | stash => {
17 | releases => $res->{data},
18 | },
19 | };
20 | }
21 | }
22 | return;
23 | },
24 | );
25 | }
26 |
27 | 1;
28 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Kwalitee.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Kwalitee;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | no warnings qw/deprecated/;
6 |
7 | sub index ($c) {
8 | $c->render_with(
9 | sub ($c, $params, $format) {
10 | my $res = $c->get_api('Kwalitee') or return;
11 |
12 | given ($format) {
13 | when ('json') {
14 | return { json => $res };
15 | }
16 | when ('') {
17 | return {
18 | render => 'kwalitee',
19 | stash => $res,
20 | };
21 | }
22 | }
23 | return;
24 | },
25 | );
26 | }
27 |
28 | sub indicator ($c) {
29 | $c->render_with(
30 | sub ($c, $params, $format) {
31 | my $tab = $params->{tab} // 'Indicator';
32 | my $tab_class = $c->tab_class("Kwalitee", $tab);
33 | my $res = $c->get_api($tab_class, $params) or return;
34 |
35 | given ($format) {
36 | when ('json') {
37 | return { json => $res };
38 | }
39 | when ('') {
40 | return {
41 | render => $c->template_name($tab_class),
42 | stash => $res,
43 | };
44 | }
45 | }
46 | },
47 | );
48 | }
49 |
50 | 1;
51 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Ranking.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Ranking;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | use String::CamelCase qw/decamelize/;
6 | no warnings qw/deprecated/;
7 |
8 | sub index ($c) {
9 | $c->render_with(
10 | sub ($c, $params, $format) {
11 |
12 | my $tab = $params->{tab} // 'FiveOrMore';
13 | my $league = $params->{league} = decamelize($tab);
14 | my $data = $c->get_api('Ranking', $params) or return;
15 |
16 | given ($format) {
17 | when ('json') {
18 | return { json => $data };
19 | }
20 | when ('') {
21 | return {
22 | render => "ranking/$league",
23 | stash => {
24 | league => $league,
25 | ranking => $data,
26 | },
27 | };
28 | }
29 | }
30 | },
31 | );
32 | }
33 |
34 | 1;
35 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Recent.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Recent;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | no warnings qw/deprecated/;
6 |
7 | sub index ($c) {
8 | $c->render_with(
9 | sub ($c, $params, $format) {
10 | my $res = $c->get_api('Recent', $params) or return;
11 |
12 | given ($format) {
13 | when ('json') {
14 | return { json => $res };
15 | }
16 | when ('') {
17 | return {
18 | render => 'recent',
19 | stash => $res,
20 | };
21 | }
22 | }
23 | return;
24 | },
25 | );
26 | }
27 |
28 | 1;
29 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Root.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Root;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 |
5 | sub check_maintenance ($c) {
6 | my $status = $c->get_api('Status') // {};
7 |
8 | if ($status->{maintenance}) {
9 | $c->stash("cpants.has_notice" => 1);
10 | $c->stash("cpants.notice_maintenance" => 1);
11 | }
12 | $c->stash('last_analyzed', $status->{last_analyzed});
13 | $c->stash('tracking_id', WWW::CPANTS->instance->config->{tracking_id});
14 | return 1;
15 | }
16 |
17 | 1;
18 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Search.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Search;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | no warnings qw/deprecated/;
6 |
7 | sub search ($c) {
8 | $c->render_with(
9 | sub ($c, $params, $format) {
10 | my $name = $params->{name};
11 | my $res;
12 | if (defined $name and $name ne '') {
13 | $res = $c->get_api('Search', $params) or return;
14 | if (@{ $res->{authors} } == 1 && !@{ $res->{dists} }) {
15 | return { redirect_to => '/author/' . $res->{authors}[0] };
16 | }
17 | if (@{ $res->{dists} } == 1 && !@{ $res->{authors} }) {
18 | return { redirect_to => '/dist/' . $res->{dists}[0] };
19 | }
20 | }
21 |
22 | given ($format) {
23 | when ('') {
24 | return $res if $res->{redirect_to};
25 | return {
26 | render => 'search',
27 | stash => $res,
28 | };
29 | }
30 | }
31 | return;
32 | },
33 | );
34 | }
35 |
36 | 1;
37 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Controller/Stats.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Controller::Stats;
2 |
3 | use Mojo::Base 'WWW::CPANTS::Web::Controller', -signatures;
4 | use experimental qw/switch/;
5 | no warnings qw/deprecated/;
6 |
7 | sub index ($c) {
8 | $c->render_with(
9 | sub ($c, $params, $format) {
10 | my $tab = $params->{tab} // '';
11 | my $tab_class = $c->tab_class("Stats", $tab);
12 | my $res = $c->get_api($tab_class, $params) or return;
13 |
14 | given ($format) {
15 | when ('json') {
16 | return { json => $res };
17 | }
18 | when ('') {
19 | return {
20 | render => $c->template_name($tab_class),
21 | stash => $res,
22 | };
23 | }
24 | }
25 | },
26 | );
27 | }
28 |
29 | 1;
30 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Plugin/Hooks.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Plugin::Hooks;
2 |
3 | use Mojo::Base 'Mojolicious::Plugin', -signatures;
4 | use Mojo::Path;
5 | use IO::Compress::Gzip qw(gzip $GzipError);
6 |
7 | sub register ($self, $app, $conf) {
8 | $app->hook(before_dispatch => \&before_dispatch);
9 | $app->hook(after_render => \&after_render);
10 | }
11 |
12 | sub before_dispatch ($c) {
13 | return if $c->req->headers->header('X-Forwarded-For');
14 | return unless ($c->req->headers->accept_encoding || '') =~ /gzip/i;
15 | my $path = $c->stash->{path} || $c->req->url->path->clone->canonicalize;
16 | return unless my @parts = @{ Mojo::Path->new("$path")->parts };
17 | return if $parts[0] eq '..';
18 | my $rel = join('/', @parts) . '.gz';
19 | if (my $file = $c->app->static->file($rel)) {
20 | my $type = $rel =~ /\.(\w+)\.gz$/ ? $c->app->types->type($1) : undef;
21 | $c->res->headers->content_type($type || 'text/plain');
22 | $c->res->headers->content_encoding('gzip');
23 | $c->app->static->serve_asset($c, $file) or return;
24 | $c->stash->{'mojo.static'}++;
25 | return !!$c->rendered;
26 | }
27 | }
28 |
29 | sub after_render ($c, $output, $format) {
30 | return if $c->stash->{'mojo.static'};
31 | return if $c->req->headers->header('X-Forwarded-For');
32 | return if $c->req->is_xhr;
33 | return unless ($c->req->headers->accept_encoding || '') =~ /gzip/i;
34 | return if $c->res->content->is_multipart || $c->res->content->is_dynamic;
35 |
36 | $c->res->headers->content_encoding('gzip');
37 | gzip $output, \my $compressed or die $GzipError;
38 | $$output = $compressed;
39 | }
40 |
41 | 1;
42 |
--------------------------------------------------------------------------------
/lib/WWW/CPANTS/Web/Util/URL.pm:
--------------------------------------------------------------------------------
1 | package WWW::CPANTS::Web::Util::URL;
2 |
3 | use Mojo::Base -strict, -signatures;
4 | use Mojo::URL;
5 | use Gravatar::URL ();
6 | use HTML::Entities ();
7 | use Regexp::Common qw/URI/;
8 |
9 | sub metacpan_url ($dist) {
10 | Mojo::URL->new(sprintf 'https://metacpan.org/release/%s/%s', @$dist{qw/author name_version/});
11 | }
12 |
13 | sub bugtracker_url ($dist) {
14 | if (my $url = $dist->{bugtracker_url}) {
15 | Mojo::URL->new($url);
16 | } else {
17 | $url = Mojo::URL->new('https://rt.cpan.org/Public/Dist/Display.html');
18 | $url->query(Name => $dist->{name});
19 | $url;
20 | }
21 | }
22 |
23 | sub repository_url ($dist) {
24 | my $url = $dist->{repository_url} or return;
25 | $url =~ s!^git://github!https://github!;
26 | Mojo::URL->new($url);
27 | }
28 |
29 | sub gravatar_url ($pause_id) {
30 | Gravatar::URL::gravatar_url(
31 | email => ($pause_id // '__dummy__') . '@cpan.org',
32 | size => 130,
33 | default => 'identicon',
34 | https => 1,
35 | );
36 | }
37 |
38 | sub linkify ($text) {
39 | return '' unless defined $text;
40 | $text = HTML::Entities::encode_entities($text);
41 | $text =~ s!($RE{URI}{HTTP}{-scheme => 'https?'})!$1!gr;
42 | }
43 |
44 | 1;
45 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cpants",
3 | "version": "5.0.0",
4 | "description": "CPAN Testing Service",
5 | "author": "Kenichi Ishigaki",
6 | "license": "Artistic-1.0-Perl",
7 | "repository": "https://github.com/cpants/www-cpants",
8 | "dependencies": {
9 | "bootstrap": "^3.4.1",
10 | "datatables": "^1.10.18",
11 | "datatables-responsive": "^1.0.7",
12 | "font-awesome": "^4.7.0",
13 | "html5shiv": "^3.7.3",
14 | "jquery": "^3.6.0",
15 | "jquery-jsonview": "^1.2.3",
16 | "jquery-migrate": "^3.3.2",
17 | "respond.js": "^1.4.2",
18 | "sprintf-js": "^1.1.2",
19 | "strftime": "^0.10.0",
20 | "swagger-ui-dist": "^5.0.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/public/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/public/bootstrap/js/npm.js:
--------------------------------------------------------------------------------
1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.
2 | require('../../js/transition.js')
3 | require('../../js/alert.js')
4 | require('../../js/button.js')
5 | require('../../js/carousel.js')
6 | require('../../js/collapse.js')
7 | require('../../js/dropdown.js')
8 | require('../../js/modal.js')
9 | require('../../js/tooltip.js')
10 | require('../../js/popover.js')
11 | require('../../js/scrollspy.js')
12 | require('../../js/tab.js')
13 | require('../../js/affix.js')
--------------------------------------------------------------------------------
/public/cpants/css/cpants.css:
--------------------------------------------------------------------------------
1 | h1 {
2 | display: inline;
3 | }
4 | h2 {
5 | margin-top: 10px;
6 | margin-bottom: 30px;
7 | }
8 | h3 {
9 | margin-bottom: 30px;
10 | }
11 | section {
12 | padding-bottom: 50px;
13 | }
14 | img {
15 | max-width: 100%;
16 | }
17 | #footer {
18 | margin-top: 50px;
19 | }
20 | .sidebar ul.dropdown-menu li a {
21 | padding-left: 3px;
22 | }
23 | .list-flat-disc {
24 | padding-left: 0px;
25 | list-style: outside disc none;
26 | }
27 | .list-csv {
28 | list-style: none;
29 | padding-left: 0px;
30 | display: inline;
31 | }
32 | .list-csv li {
33 | display: inline;
34 | }
35 | .list-csv li:after {
36 | content: ", ";
37 | }
38 | .list-csv li:last-child:after {
39 | content: "";
40 | }
41 | .navbar-static-top {
42 | margin-bottom: 0px;
43 | }
44 | #main {
45 | padding-top: 20px;
46 | }
47 |
--------------------------------------------------------------------------------
/public/datatables-responsive/js/responsive.jqueryui.js:
--------------------------------------------------------------------------------
1 | /*! jQuery UI integration for DataTables' Responsive
2 | * ©2015 SpryMedia Ltd - datatables.net/license
3 | */
4 |
5 | (function( factory ){
6 | if ( typeof define === 'function' && define.amd ) {
7 | // AMD
8 | define( ['jquery', 'datatables.net-jqui', 'datatables.net-responsive'], function ( $ ) {
9 | return factory( $, window, document );
10 | } );
11 | }
12 | else if ( typeof exports === 'object' ) {
13 | // CommonJS
14 | module.exports = function (root, $) {
15 | if ( ! root ) {
16 | root = window;
17 | }
18 |
19 | if ( ! $ || ! $.fn.dataTable ) {
20 | $ = require('datatables.net-jqui')(root, $).$;
21 | }
22 |
23 | if ( ! $.fn.dataTable.Responsive ) {
24 | require('datatables.net-responsive')(root, $);
25 | }
26 |
27 | return factory( $, root, root.document );
28 | };
29 | }
30 | else {
31 | // Browser
32 | factory( jQuery, window, document );
33 | }
34 | }(function( $, window, document, undefined ) {
35 | 'use strict';
36 | var DataTable = $.fn.dataTable;
37 |
38 |
39 | var _display = DataTable.Responsive.display;
40 | var _original = _display.modal;
41 |
42 | _display.modal = function ( options ) {
43 | return function ( row, update, render ) {
44 | if ( ! $.fn.dialog ) {
45 | _original( row, update, render );
46 | }
47 | else {
48 | if ( ! update ) {
49 | $( '
' )
50 | .append( render() )
51 | .appendTo( 'body' )
52 | .dialog( $.extend( true, {
53 | title: options && options.header ? options.header( row ) : '',
54 | width: 500
55 | }, options.dialog ) );
56 | }
57 | }
58 | };
59 | };
60 |
61 |
62 | return DataTable.Responsive;
63 | }));
64 |
--------------------------------------------------------------------------------
/public/datatables/images/Sorting icons.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/Sorting icons.psd
--------------------------------------------------------------------------------
/public/datatables/images/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/favicon.ico
--------------------------------------------------------------------------------
/public/datatables/images/sort_asc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/sort_asc.png
--------------------------------------------------------------------------------
/public/datatables/images/sort_asc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/sort_asc_disabled.png
--------------------------------------------------------------------------------
/public/datatables/images/sort_both.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/sort_both.png
--------------------------------------------------------------------------------
/public/datatables/images/sort_desc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/sort_desc.png
--------------------------------------------------------------------------------
/public/datatables/images/sort_desc_disabled.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/datatables/images/sort_desc_disabled.png
--------------------------------------------------------------------------------
/public/font-awesome/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/font-awesome/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/public/font-awesome/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/font-awesome/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/public/font-awesome/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/font-awesome/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/public/font-awesome/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/font-awesome/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/public/font-awesome/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cpants/www-cpants/cf926b448b67039889c0877d618d76ff9abd4a51/public/font-awesome/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/public/highcharts/modules/no-data-to-display.js:
--------------------------------------------------------------------------------
1 | /*
2 | Highcharts JS v4.1.4 (2015-03-10)
3 | Plugin for displaying a message when there is no data visible in chart.
4 |
5 | (c) 2010-2014 Highsoft AS
6 | Author: Oystein Moseng
7 |
8 | License: www.highcharts.com/license
9 | */
10 | (function(c){function i(){return!!this.points.length}function e(){this.hasData()?this.hideNoData():this.showNoData()}var f=c.seriesTypes,d=c.Chart.prototype,g=c.getOptions(),h=c.extend,j=c.each;h(g.lang,{noData:"No data to display"});g.noData={position:{x:0,y:0,align:"center",verticalAlign:"middle"},attr:{},style:{fontWeight:"bold",fontSize:"12px",color:"#60606a"}};j(["pie","gauge","waterfall","bubble"],function(a){if(f[a])f[a].prototype.hasData=i});c.Series.prototype.hasData=function(){return this.visible&&
11 | this.dataMax!==void 0&&this.dataMin!==void 0};d.showNoData=function(a){var b=this.options,a=a||b.lang.noData,b=b.noData;if(!this.noDataLabel)this.noDataLabel=this.renderer.label(a,0,0,null,null,null,null,null,"no-data").attr(b.attr).css(b.style).add(),this.noDataLabel.align(h(this.noDataLabel.getBBox(),b.position),!1,"plotBox")};d.hideNoData=function(){if(this.noDataLabel)this.noDataLabel=this.noDataLabel.destroy()};d.hasData=function(){for(var a=this.series,b=a.length;b--;)if(a[b].hasData()&&!a[b].options.isInternal)return!0;
12 | return!1};d.callbacks.push(function(a){c.addEvent(a,"load",e);c.addEvent(a,"redraw",e)})})(Highcharts);
13 |
--------------------------------------------------------------------------------
/public/highcharts/themes/grid-light.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Grid-light theme for Highcharts JS
3 | * @author Torstein Honsi
4 | */
5 |
6 | // Load the fonts
7 | Highcharts.createElement('link', {
8 | href: '//fonts.googleapis.com/css?family=Dosis:400,600',
9 | rel: 'stylesheet',
10 | type: 'text/css'
11 | }, null, document.getElementsByTagName('head')[0]);
12 |
13 | Highcharts.theme = {
14 | colors: ["#7cb5ec", "#f7a35c", "#90ee7e", "#7798BF", "#aaeeee", "#ff0066", "#eeaaee",
15 | "#55BF3B", "#DF5353", "#7798BF", "#aaeeee"],
16 | chart: {
17 | backgroundColor: null,
18 | style: {
19 | fontFamily: "Dosis, sans-serif"
20 | }
21 | },
22 | title: {
23 | style: {
24 | fontSize: '16px',
25 | fontWeight: 'bold',
26 | textTransform: 'uppercase'
27 | }
28 | },
29 | tooltip: {
30 | borderWidth: 0,
31 | backgroundColor: 'rgba(219,219,216,0.8)',
32 | shadow: false
33 | },
34 | legend: {
35 | itemStyle: {
36 | fontWeight: 'bold',
37 | fontSize: '13px'
38 | }
39 | },
40 | xAxis: {
41 | gridLineWidth: 1,
42 | labels: {
43 | style: {
44 | fontSize: '12px'
45 | }
46 | }
47 | },
48 | yAxis: {
49 | minorTickInterval: 'auto',
50 | title: {
51 | style: {
52 | textTransform: 'uppercase'
53 | }
54 | },
55 | labels: {
56 | style: {
57 | fontSize: '12px'
58 | }
59 | }
60 | },
61 | plotOptions: {
62 | candlestick: {
63 | lineColor: '#404048'
64 | }
65 | },
66 |
67 |
68 | // General
69 | background2: '#F0F0EA'
70 |
71 | };
72 |
73 | // Apply the theme
74 | Highcharts.setOptions(Highcharts.theme);
75 |
--------------------------------------------------------------------------------
/public/jquery-jsonview/jquery.jsonview.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 | .jsonview {
3 | font-family: monospace;
4 | font-size: 1.1em;
5 | white-space: pre-wrap; }
6 | .jsonview .prop {
7 | font-weight: bold; }
8 | .jsonview .null {
9 | color: red; }
10 | .jsonview .bool {
11 | color: blue; }
12 | .jsonview .num {
13 | color: blue; }
14 | .jsonview .string {
15 | color: green;
16 | white-space: pre-wrap; }
17 | .jsonview .string.multiline {
18 | display: inline-block;
19 | vertical-align: text-top; }
20 | .jsonview .collapser {
21 | position: absolute;
22 | left: -1em;
23 | cursor: pointer; }
24 | .jsonview .collapsible {
25 | transition: height 1.2s;
26 | transition: width 1.2s; }
27 | .jsonview .collapsible.collapsed {
28 | height: .8em;
29 | width: 1em;
30 | display: inline-block;
31 | overflow: hidden;
32 | margin: 0; }
33 | .jsonview .collapsible.collapsed:before {
34 | content: "…";
35 | width: 1em;
36 | margin-left: .2em; }
37 | .jsonview .collapser.collapsed {
38 | transform: rotate(0deg); }
39 | .jsonview .q {
40 | display: inline-block;
41 | width: 0px;
42 | color: transparent; }
43 | .jsonview li {
44 | position: relative; }
45 | .jsonview ul {
46 | list-style: none;
47 | margin: 0 0 0 2em;
48 | padding: 0; }
49 | .jsonview h1 {
50 | font-size: 1.2em; }
51 |
52 | /*# sourceMappingURL=jquery.jsonview.css.map */
53 |
--------------------------------------------------------------------------------
/public/sprintf-js/angular-sprintf.min.js:
--------------------------------------------------------------------------------
1 | /*! sprintf-js v1.1.2 | Copyright (c) 2007-present, Alexandru Mărășteanu | BSD-3-Clause */
2 | !function(){"use strict";angular.module("sprintf",[]).filter("sprintf",function(){return function(){return sprintf.apply(null,arguments)}}).filter("fmt",["$filter",function(t){return t("sprintf")}]).filter("vsprintf",function(){return function(t,n){return vsprintf(t,n)}}).filter("vfmt",["$filter",function(t){return t("vsprintf")}])}();
3 | //# sourceMappingURL=angular-sprintf.min.js.map
4 |
--------------------------------------------------------------------------------
/public/sprintf-js/angular-sprintf.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["angular-sprintf.js"],"names":["angular","module","filter","sprintf","apply","arguments","$filter","format","argv","vsprintf"],"mappings":";CAEC,WACG,aAEAA,QACIC,OAAO,UAAW,IAClBC,OAAO,UAAW,WACd,OAAO,WACH,OAAOC,QAAQC,MAAM,KAAMC,cAGnCH,OAAO,MAAO,CAAC,UAAW,SAASI,GAC/B,OAAOA,EAAQ,cAEnBJ,OAAO,WAAY,WACf,OAAO,SAASK,EAAQC,GACpB,OAAOC,SAASF,EAAQC,MAGhCN,OAAO,OAAQ,CAAC,UAAW,SAASI,GAChC,OAAOA,EAAQ,eAnB1B","file":"angular-sprintf.min.js","sourcesContent":["/* global angular, sprintf, vsprintf */\n\n!function() {\n 'use strict'\n\n angular.\n module('sprintf', []).\n filter('sprintf', function() {\n return function() {\n return sprintf.apply(null, arguments)\n }\n }).\n filter('fmt', ['$filter', function($filter) {\n return $filter('sprintf')\n }]).\n filter('vsprintf', function() {\n return function(format, argv) {\n return vsprintf(format, argv)\n }\n }).\n filter('vfmt', ['$filter', function($filter) {\n return $filter('vsprintf')\n }])\n}(); // eslint-disable-line\n"]}
--------------------------------------------------------------------------------
/t/00_load.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use Test::More;
4 | use File::Find;
5 | use lib glob "$FindBin::Bin/../extlib/*/lib";
6 |
7 | my $root = "$FindBin::Bin/..";
8 |
9 | my $fail;
10 | find({
11 | wanted => sub {
12 | my $file = $File::Find::name;
13 | my ($package) = $file =~ m!lib/(.+)\.pm$!;
14 | return unless $package;
15 | $package =~ s|/|::|g;
16 | require_ok $package or $fail++;
17 | },
18 | no_chdir => 1,
19 | },
20 | "$root/lib"
21 | );
22 |
23 | require_ok "$root/api.psgi" or $fail++;
24 |
25 | BAIL_OUT if $fail;
26 |
27 | done_testing;
28 |
--------------------------------------------------------------------------------
/t/10_tasks.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use Test::More;
4 | use WWW::CPANTS::Test::TestPAN;
5 | use WWW::CPANTS::Bin::Runner;
6 | use Syntax::Keyword::Try;
7 |
8 | my $testpan = WWW::CPANTS::Test::TestPAN->new->setup;
9 |
10 | my $runner = WWW::CPANTS::Bin::Runner->new;
11 |
12 | for my $name ($runner->ctx->task_names->@*) {
13 | try {
14 | local @ARGV;
15 | ok $runner->run_tasks($name), $name;
16 | } catch {
17 | fail "$name: $@"
18 | }
19 | }
20 |
21 | done_testing;
22 |
--------------------------------------------------------------------------------
/t/analysis/required_perl.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use Test::More;
4 |
5 | test_analysis(
6 | '/required_perl',
7 | # test requires 5.006, runtime recommends 5.008, declared runtime requires 5.008008
8 | ['HUGMEIR/Params-Lazy-0.005.tar.gz', '5.008008'],
9 |
10 | # declared runtime requires 5.006, test requires v5.18.1, runtime requires v5.18.2
11 | ['HUGMEIR/MariaDB-NonBlocking-0.20.tar.gz', '5.018002'],
12 |
13 | # declared runtime requires 5.008005, runtime requires 5.010001
14 | ['NIKOLAS/Shout-2.1.4.tar.gz', '5.010001'],
15 |
16 | # declared runtime requires 5.008001
17 | ['PALIK/Gearman-2.004.015.tar.gz', '5.008001'],
18 |
19 | # runtime requires 5.026_000
20 | ['VBAR/Regexp-Compare-0.31.tar.gz', '5.026000'],
21 |
22 | # declared runtime requires 0
23 | ['SEBNOW/ResourcePool-Resource-Redis-1.tar.gz', undef],
24 | );
25 |
26 | done_testing;
27 |
--------------------------------------------------------------------------------
/t/api_model/acme/cpan_authors/stats/active_authors.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re any array_each subhashof);
6 |
7 | $ENV{TEST_ACME_MODULES} = 1;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/Acme-CPANAuthors-0.26.tar.gz',
12 | 'ISHIGAKI/Acme-CPANAuthors-Japanese-0.190426.tar.gz',
13 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
14 | );
15 | my $testpan = setup_testpan(@files);
16 | $testpan->cpan->update_indices;
17 |
18 | load_task('UpdateCPANIndices::Whois')->run;
19 | load_task('Traverse')->run(qw/ISHIGAKI/);
20 | load_task('Analyze')->run(@files);
21 | load_task('PostProcess::UpdateAuthorStats')->run;
22 | load_task('PostProcess::UpdateRanking')->run;
23 | load_task('Acme::UpdateModules')->run;
24 | load_task('Acme::UpdateStats')->run;
25 | };
26 |
27 | my $model = api_model('Acme::CPANAuthors::Stats::ActiveAuthors');
28 |
29 | subtest 'japanese' => sub {
30 | my $res = $model->load({ module_id => 'japanese' });
31 | cmp_deeply $res => {
32 | 'data' => array_each(subhashof({
33 | 'active_authors' => re('[0-9]+'),
34 | 'year' => re('20[0-9]+'),
35 | })),
36 | 'recordsTotal' => re('[0-9]+'),
37 | };
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/acme/cpan_authors/stats/new_authors.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re any array_each subhashof);
6 |
7 | $ENV{TEST_ACME_MODULES} = 1;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/Acme-CPANAuthors-0.26.tar.gz',
12 | 'ISHIGAKI/Acme-CPANAuthors-Japanese-0.190426.tar.gz',
13 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
14 | );
15 | my $testpan = setup_testpan(@files);
16 | $testpan->cpan->update_indices;
17 |
18 | load_task('UpdateCPANIndices::Whois')->run;
19 | load_task('Traverse')->run(qw/ISHIGAKI/);
20 | load_task('Analyze')->run(@files);
21 | load_task('PostProcess::UpdateAuthorStats')->run;
22 | load_task('PostProcess::UpdateRanking')->run;
23 | load_task('Acme::UpdateModules')->run;
24 | load_task('Acme::UpdateStats')->run;
25 | };
26 |
27 | my $model = api_model('Acme::CPANAuthors::Stats::NewAuthors');
28 |
29 | subtest 'japanese' => sub {
30 | my $res = $model->load({ module_id => 'japanese' });
31 | cmp_deeply $res => {
32 | 'data' => array_each(subhashof({
33 | 'new_authors' => re('[0-9]+'),
34 | 'year' => re('20[0-9]+'),
35 | })),
36 | 'recordsTotal' => re('[0-9]+'),
37 | };
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/acme/cpan_authors/stats/new_releases.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re any array_each subhashof);
6 |
7 | $ENV{TEST_ACME_MODULES} = 1;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/Acme-CPANAuthors-0.26.tar.gz',
12 | 'ISHIGAKI/Acme-CPANAuthors-Japanese-0.190426.tar.gz',
13 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
14 | );
15 | my $testpan = setup_testpan(@files);
16 | $testpan->cpan->update_indices;
17 |
18 | load_task('UpdateCPANIndices::Whois')->run;
19 | load_task('Traverse')->run(qw/ISHIGAKI/);
20 | load_task('Analyze')->run(@files);
21 | load_task('PostProcess::UpdateAuthorStats')->run;
22 | load_task('PostProcess::UpdateRanking')->run;
23 | load_task('Acme::UpdateModules')->run;
24 | load_task('Acme::UpdateStats')->run;
25 | };
26 |
27 | my $model = api_model('Acme::CPANAuthors::Stats::NewReleases');
28 |
29 | subtest 'japanese' => sub {
30 | my $res = $model->load({ module_id => 'japanese' });
31 | cmp_deeply $res => {
32 | 'data' => array_each(subhashof({
33 | 'new_releases' => re('[0-9]+'),
34 | 'year' => re('20[0-9]+'),
35 | })),
36 | 'recordsTotal' => re('[0-9]+'),
37 | };
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/acme/cpan_authors/stats/releases.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re any array_each subhashof);
6 |
7 | $ENV{TEST_ACME_MODULES} = 1;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/Acme-CPANAuthors-0.26.tar.gz',
12 | 'ISHIGAKI/Acme-CPANAuthors-Japanese-0.190426.tar.gz',
13 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
14 | );
15 | my $testpan = setup_testpan(@files);
16 | $testpan->cpan->update_indices;
17 |
18 | load_task('UpdateCPANIndices::Whois')->run;
19 | load_task('Traverse')->run(qw/ISHIGAKI/);
20 | load_task('Analyze')->run(@files);
21 | load_task('PostProcess::UpdateAuthorStats')->run;
22 | load_task('PostProcess::UpdateRanking')->run;
23 | load_task('Acme::UpdateModules')->run;
24 | load_task('Acme::UpdateStats')->run;
25 | };
26 |
27 | my $model = api_model('Acme::CPANAuthors::Stats::Releases');
28 |
29 | subtest 'japanese' => sub {
30 | my $res = $model->load({ module_id => 'japanese' });
31 | cmp_deeply $res => {
32 | 'data' => array_each(subhashof({
33 | 'releases' => re('[0-9]+'),
34 | 'year' => re('20[0-9]+'),
35 | })),
36 | 'recordsTotal' => re('[0-9]+'),
37 | };
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/v4/search.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my $testpan = setup_testpan(
9 | 'HEIKO/IsUTF8-0.2.tar.gz',
10 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
11 | 'ISAAC/DBIx-Objects-0.04.tar.gz',
12 | );
13 | load_task('Traverse')->run(qw/HEIKO ISHIGAKI ISAAC/);
14 | };
15 |
16 | my $model = api_model('V4::Search');
17 |
18 | subtest 'matches both' => sub {
19 | my $res = $model->load({ name => 'Is' });
20 | cmp_deeply $res => {
21 | 'authors' => [
22 | 'ISAAC',
23 | 'ISHIGAKI'
24 | ],
25 | 'dists' => ['IsUTF8'],
26 | };
27 | };
28 |
29 | done_testing;
30 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/cpan_distributions_by.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('Traverse')->run(qw/ISHIGAKI/);
14 | load_task('Analyze')->run(@files);
15 | };
16 |
17 | my $model = api_model('V4::Table::CPANDistributionsBy');
18 |
19 | subtest 'some of mine' => sub {
20 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
21 | cmp_deeply $res => {
22 | 'data' => [{
23 | 'date' => '2011-05-31',
24 | 'fails' => [],
25 | 'latest' => 1,
26 | 'name' => 'Path-Extended',
27 | 'score' => 100,
28 | 'version' => '0.19'
29 | },
30 | {
31 | 'date' => '2011-01-06',
32 | 'fails' => ['main_module_version_matches_dist_version'],
33 | 'latest' => 1,
34 | 'name' => 'Pod-Perldocs',
35 | 'score' => '96.88',
36 | 'version' => '0.17'
37 | },
38 | ],
39 | 'recordsTotal' => 2,
40 | };
41 | };
42 |
43 | done_testing;
44 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/fails_in.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | load_task('Traverse')->run(qw/ISHIGAKI/);
13 | load_task('Analyze')->run(@files);
14 | load_task('PostProcess::UpdateCaches')->run;
15 | };
16 |
17 | my $model = api_model('V4::Table::FailsIn');
18 |
19 | subtest 'some of mine' => sub {
20 | my $res = $model->load({ name => 'main_module_version_matches_dist_version' });
21 | cmp_deeply $res => {
22 | 'data' => [{
23 | 'author' => 'ISHIGAKI',
24 | 'availability' => 'Latest',
25 | 'date' => '2011-01-06',
26 | 'name_version' => 'Pod-Perldocs-0.17'
27 | }],
28 | 'indicator' => {
29 | 'defined_in' => 'Module::CPANTS::SiteKwalitee::Version',
30 | 'description' => "The version and/or name of the main module in this distribution doesn't match the distribution version and/or name.",
31 | 'level' => 'core',
32 | 'name' => 'main_module_version_matches_dist_version',
33 | 'remedy' => 'Make sure that the main module name and version are the same of the distribution.'
34 | },
35 | 'recordsTotal' => 1
36 | };
37 | };
38 |
39 | done_testing;
40 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/ranking.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run;
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess::UpdateAuthorStats')->run;
18 | load_task('PostProcess::UpdateRanking')->run;
19 | };
20 |
21 | my $model = api_model('V4::Table::Ranking');
22 |
23 | subtest 'some of mine' => sub {
24 | my $res = $model->load({ league => 'less_than_five' });
25 | cmp_deeply $res => {
26 | 'data' => [{
27 | 'average_core_kwalitee' => 100,
28 | 'average_kwalitee' => '153.12',
29 | 'cpan_dists' => 1,
30 | 'has_perl6' => 0,
31 | 'json' => undef,
32 | 'json_updated_at' => undef,
33 | 'last_new_release_at' => '1306782406',
34 | 'last_release_at' => '1306782406',
35 | 'last_analyzed_at' => re('^[0-9]+$'),
36 | 'pause_id' => 'ISHIGAKI',
37 | 'rank' => 1,
38 | 'recent_dists' => 0,
39 | }],
40 | 'recordsTotal' => 1,
41 | };
42 | };
43 |
44 | done_testing;
45 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/recent.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::Datetime;
5 | use Test::More;
6 | use Test::Deep qw(cmp_deeply);
7 | use Test::MockTime::HiRes;
8 |
9 | fixture {
10 | my @files = (
11 | 'BAYASHI/App-jl-0.12.tar.gz',
12 | 'ISHIGAKI/JSON-4.02.tar.gz',
13 | );
14 | my $testpan = setup_testpan(@files);
15 | load_task('Traverse')->run(qw/BAYASHI ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | };
18 |
19 | my $model = api_model('V4::Table::Recent');
20 |
21 | subtest 'some of mine' => sub {
22 | my $epoch = epoch_from_date('2019-04-30');
23 | mock_time {
24 | my $res = $model->load;
25 | cmp_deeply $res => {
26 | 'data' => [{
27 | 'date' => '2019-06-23',
28 | 'name' => 'App-jl',
29 | 'pause_id' => 'BAYASHI',
30 | 'score' => '100',
31 | 'version' => '0.12'
32 | }, {
33 | 'date' => '2019-02-23',
34 | 'name' => 'JSON',
35 | 'pause_id' => 'ISHIGAKI',
36 | 'score' => '100',
37 | 'version' => '4.02'
38 | },
39 | ],
40 | 'recordsTotal' => 2,
41 | };
42 | }
43 | $epoch;
44 | };
45 |
46 | done_testing;
47 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/recent_by.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::Datetime;
5 | use Test::More;
6 | use Test::Deep qw(cmp_deeply);
7 | use Test::MockTime::HiRes;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/JSON-4.02.tar.gz',
12 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
13 | );
14 | my $testpan = setup_testpan(@files);
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | };
18 |
19 | my $model = api_model('V4::Table::RecentBy');
20 |
21 | subtest 'some of mine' => sub {
22 | my $epoch = epoch_from_date('2019-04-30');
23 | mock_time {
24 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
25 | cmp_deeply $res => {
26 | 'data' => [{
27 | 'date' => '2019-02-23',
28 | 'fails' => [],
29 | 'name' => 'JSON',
30 | 'score' => '100',
31 | 'version' => '4.02'
32 | },
33 | ],
34 | 'recordsTotal' => 1,
35 | };
36 | }
37 | $epoch;
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/releases_of.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | 'MAKAMAKA/JSON-2.90.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('Traverse')->run(qw/ISHIGAKI MAKAMAKA/);
14 | load_task('Analyze')->run(@files);
15 | };
16 |
17 | my $model = api_model('V4::Table::ReleasesOf');
18 |
19 | subtest 'some of mine' => sub {
20 | my $res = $model->load({ name => 'JSON' });
21 | cmp_deeply $res => {
22 | 'data' => [{
23 | 'author' => 'ISHIGAKI',
24 | 'availability' => 'CPAN',
25 | 'date' => '2019-02-23',
26 | 'name' => 'JSON',
27 | 'score' => '100',
28 | 'version' => '4.02'
29 | },
30 | {
31 | 'author' => 'MAKAMAKA',
32 | 'availability' => 'CPAN',
33 | 'date' => '2013-10-31',
34 | 'name' => 'JSON',
35 | 'score' => '100',
36 | 'version' => '2.90'
37 | }
38 |
39 | ],
40 | 'recordsTotal' => 2,
41 | };
42 | };
43 |
44 | done_testing;
45 |
--------------------------------------------------------------------------------
/t/api_model/v4/table/reverse_dependencies_of.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Pod-PerldocJp-0.19.tar.gz',
10 | 'TOKUHIROM/LiBot-v0.0.3.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('Traverse')->run(qw/ISHIGAKI TOKUHIROM/);
14 | load_task('Analyze')->run(@files);
15 | load_task('PostProcess::UpdateReverseDependency')->run;
16 | };
17 |
18 | my $model = api_model('V4::Table::ReverseDependenciesOf');
19 |
20 | subtest 'some of mine' => sub {
21 | my $res = $model->load({ name => 'Pod-PerldocJp' });
22 | cmp_deeply $res => {
23 | 'data' => [{
24 | 'author' => 'TOKUHIROM',
25 | 'date' => '2013-10-12',
26 | 'name_version' => 'LiBot-v0.0.3',
27 | 'score' => '96.88'
28 | }],
29 | 'recordsTotal' => 1,
30 | };
31 | };
32 |
33 | done_testing;
34 |
--------------------------------------------------------------------------------
/t/api_model/v5/author.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply re);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess::UpdateAuthorStats')->run;
18 | load_task('PostProcess::UpdateRanking')->run;
19 | };
20 |
21 | my $model = api_model('V5::Author');
22 |
23 | subtest 'me' => sub {
24 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
25 | cmp_deeply $res => {
26 | 'ascii_name' => 'Kenichi Ishigaki',
27 | 'average_core_kwalitee' => '100',
28 | 'average_kwalitee' => '153.12',
29 | 'cpan_dists' => 1,
30 | 'deleted' => 0,
31 | 'email' => 'ishigaki@cpan.org',
32 | 'has_perl6' => 0,
33 | 'homepage' => 'http://d.hatena.ne.jp/charsbar',
34 | 'joined_on' => '2005-12-17',
35 | 'last_analyzed_at' => re('^[0-9]+$'),
36 | 'last_new_release_on' => '2011-05-31',
37 | 'last_release_on' => '2011-05-31',
38 | 'name' => 'Kenichi Ishigaki',
39 | 'nologin' => 0,
40 | 'pause_id' => 'ISHIGAKI',
41 | 'rank' => 1,
42 | 'recent_dists' => 0,
43 | 'system' => 0,
44 | 'year' => 2005,
45 | };
46 | };
47 |
48 | done_testing;
49 |
--------------------------------------------------------------------------------
/t/api_model/v5/author/cpan_distributions.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('UpdateCPANIndices::Whois')->run;
14 | load_task('Traverse')->run(qw/ISHIGAKI/);
15 | load_task('Analyze')->run(@files);
16 | };
17 |
18 | my $model = api_model('V5::Author::CPANDistributions');
19 |
20 | subtest 'mine' => sub {
21 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
22 | cmp_deeply $res => {
23 | 'data' => [{
24 | 'date' => '2011-05-31',
25 | 'fails' => [],
26 | 'latest' => 1,
27 | 'name' => 'Path-Extended',
28 | 'score' => '100',
29 | 'version' => '0.19'
30 | },
31 | {
32 | 'date' => '2011-01-06',
33 | 'fails' => ['main_module_version_matches_dist_version'],
34 | 'latest' => 1,
35 | 'name' => 'Pod-Perldocs',
36 | 'score' => '96.88',
37 | 'version' => '0.17'
38 | },
39 | ],
40 | 'recordsTotal' => 2,
41 | };
42 | };
43 |
44 | done_testing;
45 |
--------------------------------------------------------------------------------
/t/api_model/v5/author/feed.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('UpdateCPANIndices::Whois')->run;
14 | load_task('Traverse')->run(qw/ISHIGAKI/);
15 | load_task('Analyze')->run(@files);
16 | };
17 |
18 | my $model = api_model('V5::Author::Feed');
19 |
20 | subtest 'mine' => sub {
21 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
22 | cmp_deeply $res => {
23 | 'entries' => [{
24 | 'id' => 'Path-Extended-0.19',
25 | 'link' => '/release/ISHIGAKI/Path-Extended-0.19',
26 | 'summary' => 'Kwalitee: 100',
27 | 'title' => 'Path-Extended-0.19',
28 | 'updated' => '2011-05-31T04:06:46Z'
29 | },
30 | {
31 | 'id' => 'Pod-Perldocs-0.17',
32 | 'link' => '/release/ISHIGAKI/Pod-Perldocs-0.17',
33 | 'summary' => 'Kwalitee: 96.88; Core Fails: main_module_version_matches_dist_version',
34 | 'title' => 'Pod-Perldocs-0.17',
35 | 'updated' => '2011-01-06T16:12:00Z'
36 | },
37 | ],
38 | 'feed' => {
39 | 'author' => 'CPANTS',
40 | 'title' => 'CPANTS Feed for ISHIGAKI',
41 | 'updated' => '2011-05-31T04:06:46Z'
42 | },
43 | };
44 | };
45 |
46 | done_testing;
47 |
--------------------------------------------------------------------------------
/t/api_model/v5/author/recent_releases.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::Datetime;
5 | use Test::More;
6 | use Test::Deep qw(cmp_deeply);
7 | use Test::MockTime::HiRes;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/JSON-4.02.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | };
18 |
19 | my $model = api_model('V5::Author::RecentReleases');
20 |
21 | subtest 'matches both' => sub {
22 | my $epoch = epoch_from_date('2019-04-30');
23 | mock_time {
24 | my $res = $model->load({ pause_id => 'ISHIGAKI' });
25 | cmp_deeply $res => {
26 | 'data' => [{
27 | 'date' => '2019-02-23',
28 | 'fails' => [],
29 | 'name' => 'JSON',
30 | 'score' => '100',
31 | 'version' => '4.02'
32 | },
33 | ],
34 | 'recordsTotal' => 1
35 | };
36 | }
37 | $epoch;
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/api_model/v5/dist/errors.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | load_task('Traverse')->run(qw/ISHIGAKI/);
13 | load_task('Analyze')->run(@files);
14 | };
15 |
16 | my $model = api_model('V5::Dist::Errors');
17 |
18 | my @params = (
19 | [dist => { name => 'Pod-Perldocs' }],
20 | [release => { name => 'Pod-Perldocs-0.17', pause_id => 'ISHIGAKI' }],
21 | );
22 |
23 | for my $param (@params) {
24 | my ($name, $load_arg) = @$param;
25 | my $subtest = sub ($load_arg) {
26 | return sub {
27 | my $res = $model->load($load_arg);
28 | cmp_deeply $res => {
29 | 'data' => {
30 | 'errors' => [{
31 | 'category' => 'configure_prereq_matches_use',
32 | 'error' => ['Module::Build'],
33 | },
34 | ],
35 | },
36 | };
37 | };
38 | };
39 | subtest $name => $subtest->($load_arg);
40 | }
41 |
42 | done_testing;
43 |
--------------------------------------------------------------------------------
/t/api_model/v5/dist/metadata.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::JSON;
5 | use Test::More;
6 | use Test::Deep qw(cmp_deeply);
7 |
8 | fixture {
9 | my @files = (
10 | 'ISHIGAKI/Test-CPANfile-0.06.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('Traverse')->run(qw/ISHIGAKI/);
14 | load_task('Analyze')->run(@files);
15 | };
16 |
17 | my $model = api_model('V5::Dist::Metadata');
18 |
19 | my @params = (
20 | [dist => { name => 'Test-CPANfile' }],
21 | [release => { name => 'Test-CPANfile-0.06', pause_id => 'ISHIGAKI' }],
22 | );
23 |
24 | for my $param (@params) {
25 | my ($name, $load_arg) = @$param;
26 | my $subtest = sub ($load_arg) {
27 | return sub {
28 | my $res = $model->load($load_arg);
29 | # too big and fragile to compare
30 | ok my $metadata = $res->{data}{metadata};
31 | ok ref $metadata;
32 | note explain $metadata;
33 | };
34 | };
35 | subtest $name => $subtest->($load_arg);
36 | }
37 |
38 | done_testing;
39 |
--------------------------------------------------------------------------------
/t/api_model/v5/dist/releases.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Test-CPANfile-0.06.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | load_task('Traverse')->run(qw/ISHIGAKI/);
13 | load_task('Analyze')->run(@files);
14 | };
15 |
16 | my $model = api_model('V5::Dist::Releases');
17 |
18 | my @params = (
19 | [dist => { name => 'Test-CPANfile' }],
20 | [release => { name => 'Test-CPANfile-0.06', pause_id => 'ISHIGAKI' }],
21 | );
22 |
23 | for my $param (@params) {
24 | SKIP: {
25 | my ($name, $load_arg) = @$param;
26 | skip "$name is not supported yet", 1 if $name ne 'dist'; ## FIXME!
27 | my $subtest = sub ($load_arg) {
28 | return sub {
29 | my $res = $model->load($load_arg);
30 | note explain $res;
31 | cmp_deeply $res => {
32 | 'data' => [{
33 | 'author' => 'ISHIGAKI',
34 | 'availability' => 'CPAN',
35 | 'date' => '2019-01-16',
36 | 'name' => 'Test-CPANfile',
37 | 'score' => '100',
38 | 'version' => '0.06'
39 | }],
40 | 'recordsTotal' => 1
41 | };
42 | };
43 | };
44 | subtest $name => $subtest->($load_arg);
45 | }
46 | }
47 |
48 | done_testing;
49 |
--------------------------------------------------------------------------------
/t/api_model/v5/dist/used_by.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-PP-4.02.tar.gz',
10 | 'SKAJI/Perl-Build-1.29.tar.gz',
11 | );
12 | my $testpan = setup_testpan(@files);
13 | load_task('Traverse')->run(qw/ISHIGAKI SKAJI/);
14 | load_task('Analyze')->run(@files);
15 | load_task('PostProcess::UpdateReverseDependency')->run;
16 | };
17 |
18 | my $model = api_model('V5::Dist::UsedBy');
19 |
20 | my @params = (
21 | [dist => { name => 'JSON-PP' }],
22 | [release => { name => 'JSON-PP-4.02', pause_id => 'ISHIGAKI' }],
23 | );
24 |
25 | for my $param (@params) {
26 | my ($name, $load_arg) = @$param;
27 | my $subtest = sub ($load_arg) {
28 | return sub {
29 | my $res = $model->load($load_arg);
30 | cmp_deeply $res => {
31 | 'data' => [{
32 | 'author' => 'SKAJI',
33 | 'date' => '2018-12-21',
34 | 'name_version' => 'Perl-Build-1.29',
35 | 'score' => '100'
36 | }],
37 | 'recordsTotal' => 1
38 | };
39 | };
40 | };
41 | subtest $name => $subtest->($load_arg);
42 | }
43 |
44 | done_testing;
45 |
--------------------------------------------------------------------------------
/t/api_model/v5/kwalitee.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | load_task('Traverse')->run(qw/ISHIGAKI/);
13 | load_task('Analyze')->run(@files);
14 | };
15 |
16 | my $model = api_model('V5::Kwalitee');
17 |
18 | subtest 'mine' => sub {
19 | my $res = $model->load({});
20 |
21 | # too big and too fragile for cmp_deeply
22 | for my $type (qw/core extra experimental/) {
23 | ok exists $res->{data}{ $type . '_indicators' }, "data for $type indicator exists";
24 | }
25 | ok !grep { $_->{backpan_fails} } $res->{data}{core_indicators}->@*;
26 | ok grep { $_->{backpan_fails} } $res->{data}{extra_indicators}->@*;
27 | ok grep { $_->{backpan_fails} } $res->{data}{experimental_indicators}->@*;
28 | };
29 |
30 | done_testing;
31 |
--------------------------------------------------------------------------------
/t/api_model/v5/kwalitee/fail.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | load_task('Traverse')->run(qw/ISHIGAKI/);
13 | load_task('Analyze')->run(@files);
14 | load_task('PostProcess::UpdateCaches')->run;
15 | };
16 |
17 | my $model = api_model('V5::Kwalitee::Fail');
18 |
19 | subtest 'matches both' => sub {
20 | my $res = $model->load({ name => 'main_module_version_matches_dist_version' });
21 | note explain $res;
22 | cmp_deeply $res => {
23 | 'data' => [{
24 | 'author' => 'ISHIGAKI',
25 | 'availability' => 'Latest',
26 | 'date' => '2011-01-06',
27 | 'name_version' => 'Pod-Perldocs-0.17'
28 | },
29 | ],
30 | 'indicator' => {
31 | 'defined_in' => 'Module::CPANTS::SiteKwalitee::Version',
32 | 'description' => "The version and/or name of the main module in this distribution doesn't match the distribution version and/or name.",
33 | 'level' => 'core',
34 | 'name' => 'main_module_version_matches_dist_version',
35 | 'remedy' => 'Make sure that the main module name and version are the same of the distribution.'
36 | },
37 | 'recordsTotal' => 1
38 | };
39 | };
40 |
41 | done_testing;
42 |
--------------------------------------------------------------------------------
/t/api_model/v5/ranking.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess::UpdateAuthorStats')->run;
18 | load_task('PostProcess::UpdateRanking')->run;
19 | };
20 |
21 | my $model = api_model('V5::Ranking');
22 |
23 | subtest 'less_than_five' => sub {
24 | my $res = $model->load({ league => 'less_than_five' });
25 | cmp_deeply $res => {
26 | 'data' => [{
27 | 'average_core_kwalitee' => '100',
28 | 'cpan_dists' => 1,
29 | 'last_release_on' => '2011-05-31',
30 | 'pause_id' => 'ISHIGAKI',
31 | 'rank' => 1
32 | },
33 | ],
34 | 'recordsTotal' => 1
35 | };
36 | };
37 |
38 | done_testing;
39 |
--------------------------------------------------------------------------------
/t/api_model/v5/recent.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::Datetime;
5 | use Test::More;
6 | use Test::Deep qw(cmp_deeply);
7 | use Test::MockTime::HiRes;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/JSON-4.02.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | load_task('Traverse')->run(qw/ISHIGAKI/);
15 | load_task('Analyze')->run(@files);
16 | };
17 |
18 | my $model = api_model('V5::Recent');
19 |
20 | subtest 'recent' => sub {
21 | my $epoch = epoch_from_date('2019-04-30');
22 | mock_time {
23 | my $res = $model->load;
24 | cmp_deeply $res => {
25 | 'data' => [{
26 | 'date' => '2019-02-23',
27 | 'name' => 'JSON',
28 | 'pause_id' => 'ISHIGAKI',
29 | 'score' => '100',
30 | 'version' => '4.02'
31 | },
32 | ],
33 | 'recordsTotal' => 1
34 | };
35 | }
36 | $epoch;
37 | };
38 |
39 | done_testing;
40 |
--------------------------------------------------------------------------------
/t/api_model/v5/search.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | fixture {
8 | my @files = (
9 | 'HEIKO/IsUTF8-0.2.tar.gz',
10 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
11 | 'ISAAC/DBIx-Objects-0.04.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | load_task('Traverse')->run(qw/HEIKO ISHIGAKI ISAAC/);
15 | };
16 |
17 | my $model = api_model('V5::Search');
18 |
19 | subtest 'matches both' => sub {
20 | my $res = $model->load({ name => 'Is' });
21 | cmp_deeply $res => {
22 | 'authors' => [
23 | 'ISAAC',
24 | 'ISHIGAKI'
25 | ],
26 | 'dists' => ['IsUTF8'],
27 | };
28 | };
29 |
30 | done_testing;
31 |
--------------------------------------------------------------------------------
/t/api_model/v5/status.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Deep qw(cmp_deeply);
6 |
7 | my $model = api_model('V5::Status');
8 |
9 | subtest 'status' => sub {
10 | my $testpan = setup_testpan;
11 | load_task('AnalyzeAll')->run;
12 |
13 | my $res = $model->load({});
14 | ok $res->{last_analyzed};
15 | ok !$res->{maintenance};
16 | };
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/buildtool_not_executable.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | plan skip_all => "This test doesn't work well under Windows" if $^O eq 'MSWin32';
5 |
6 | test_kwalitee(
7 | 'buildtool_not_executable',
8 | ['COOLMEN/Test-More-Color-0.04.tar.gz', 0], # 2963
9 | ['CODECHILD/Thread-SharedTreeSet-0.01.tar.gz', 0], # 3191
10 | ['CODECHILD/Set-Definition-0.01.tar.gz', 0], # 4242
11 | ['JEEN/WebService-Aladdin-0.08.tar.gz', 0], # 4287
12 | ['DSYRTM/Guitar-Scale-0.06.tar.gz', 0], # 4469
13 | ['OVNTATAR/GitHub-Jobs-0.04.tar.gz', 0], # 5322
14 | ['EGILES/X11-Terminal-v1.0.0.tar.gz', 0], # 6205
15 | ['LTP/Game-Life-0.05.tar.gz', 0], # 6535
16 | ['LTP/IBM-SONAS-0.021.tar.gz', 0], # 7177
17 | ['DSYRTM/File-BetweenTree-1.02.tar.gz', 0], # 7590
18 | );
19 |
20 | done_testing;
21 |
--------------------------------------------------------------------------------
/t/kwalitee/configure_prereq_matches_use.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'configure_prereq_matches_use',
6 | ['SAPER/Shell-Parser-0.04.tar.gz', 0], # Module::Build
7 | ['INFOFLEX/DBD_SQLFLEX_8.2.tar.gz', 0], # DBI etc
8 | ['TRIZK/Mail-SpamAssassin-Contrib-Plugin-IPFilter-1.2.tar.gz', 0], # Devel::AssertOS
9 | ['FINNPERL/App-PerlXLock-0.08.tar.gz', 0], # Inline::Module
10 | );
11 |
12 | done_testing;
13 |
--------------------------------------------------------------------------------
/t/kwalitee/consistent_version.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'consistent_version',
6 | ['ANDK/CPAN-Test-Dummy-Perl5-Make-1.05.tar.gz', 0], # 2225
7 | ['CWEST/ACME-Error-0.03.tar.gz', 0], # 2233
8 |
9 | # no modules
10 | ['GAAS/Perl-API-0.01.tar.gz', 1], # 2003
11 | ['QJZHOU/killperl-1.01.tar.gz', 1], # 2069
12 | ['AWRIGLEY/prep-1.03.tar.gz', 1], # 2125
13 | ['LEOCHARRE/m4a2mp3-1.01.tar.gz', 1], # 2161
14 | ['NKUITSE/pathup-1.01.tar.gz', 1], # 2190
15 |
16 | # no version
17 | ['PINYAN/bitflags-0.10.tar.gz', 1], # 2053
18 | ['NI-S/Regexp-0.001.tar.gz', 1], # 2137
19 |
20 | # undef (invalid version) only
21 | ['TIMA/Bundle-Melody-Test-0.9.6a.tar.gz', 1], # 2042
22 | );
23 |
24 | done_testing;
25 |
--------------------------------------------------------------------------------
/t/kwalitee/distname_matches_name_in_meta.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'distname_matches_name_in_meta',
6 | # .pm
7 | ['LEEJO/CGI.pm-4.02.tar.gz', 0],
8 |
9 | # name with ::
10 | ['VLADO/CGI-AuthRegister-1.0.tar.gz', 0],
11 | ['BARBIE/CPAN-Testers-WWW-Reports-3.48.tar.gz', 0],
12 |
13 | # two dashes
14 | ['JESUS/Net--RabbitMQ-0.2.7.tar.gz', 0],
15 |
16 | # lacks name field in META
17 | ['DRSTEVE/stylehouse-20140427.tar.gz', 0],
18 |
19 | # name not to have been updated after rename (or copy)
20 | ['INA/Char/Latin1/Char-Latin1-0.96.tar.gz', 0],
21 | ['SADAMS/Mojolicious-Command-generate-upstart-0.02.tar.gz', 0],
22 |
23 | # distributed with the basename only
24 | ['JRUBIN/FT817COMM-0.9.9.tar.gz', 0],
25 |
26 | # with a prefix such as p5- and perl-
27 | ['SHMORIMO/p5-Text-Xslate-Syntax-Any-1.5015.tar.gz', 0],
28 | ['LOTTC/perl-OSDial-2.2.9.083.2.tar.gz', 0],
29 | );
30 |
31 | done_testing;
32 |
--------------------------------------------------------------------------------
/t/kwalitee/extractable.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'extractable',
6 | ['JHI/Statistics-DEA-0.04.tar.gz', -100], # 3468
7 | ['JACKS/CallerItem-1.0.tar.gz', -100], # 3479
8 | ['ACESTER/Math-GMatrix-0.2.tar.gz', -100], # 3775
9 | ['JACKS/AlarmCall-1.1.tar.gz', -100], # 3858
10 | ['JKAST/StatisticsDescriptive-1.1.tar.gz', -100], # 4098
11 | ['JHI/Statistics-Frequency-0.03.tar.gz', -100], # 4271
12 | ['MPOCOCK/GIFgraphExtensions-1.0.tar.gz', -100], # 4815
13 | ['SWORDSMAN/TSM_0.60.tar.gz', -100], # 5321
14 | ['STBEY/DFA-Kleene-1.0.tar.gz', -100], # 5353
15 |
16 | # invalid header blocks
17 | ['BLCKSMTH/String-RexxParse-1.10.tar.gz', -100],
18 | ['GROMMEL/Acme-Turing-0.02.tar.gz', -100],
19 |
20 | # link errors
21 | ['KAMIPO/autobox-String-Inflector-0.02.tar.gz', -100],
22 | ['KAZUHO/DBIx-Replicate-0.04.tar.gz', -100],
23 | );
24 |
25 | done_testing;
26 |
--------------------------------------------------------------------------------
/t/kwalitee/extracts_nicely.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'extracts_nicely',
6 | # one directory, without dist version
7 | ['STEFANOS/Finance-Currency-Convert-ECB-0.3.tar.gz', 1],
8 | ['XINZHENG/BIE-Data-HDF5-Data-0.01.tar.gz', 1],
9 | # more than one directories (with a directory with prepending _)
10 | ['SUNNYP/Captcha-reCAPTCHA-0.98.tar.gz', 0],
11 | # no top directory
12 | ['SMS/Net-SMS-CSNetworks-0.08.tar.gz', 0],
13 | ['KEUVGRVL/Log-Basic-1.2.1.tar.gz', 0],
14 | );
15 |
16 | done_testing;
17 |
--------------------------------------------------------------------------------
/t/kwalitee/has_abstract_in_pod.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_abstract_in_pod',
6 | ['HACHI/MogileFS-Plugin-MetaData-0.01.tar.gz', 0], # 2059
7 | ['URI/Acme-Madness-1.00.tar.gz', 0], # 2090
8 | ['PJF/Payroll-AU-PAYG-0.01.tar.gz', 0], # 2097
9 | ['LLAP/Plack-Middleware-OptionsOK-0.01.tar.gz', 0], # 2117
10 | ['GMCCAR/Acme-ManekiNeko-0.03.tar.gz', 0], # 2161
11 | ['KALEYCHEV/Math-Combination_out-0.21.tar.gz', 0], # 2163
12 | ['LEOCHARRE/Getopt-Std-Strict-1.01.tar.gz', 0], # 2165
13 | ['TJC/CGI-Untaint-telephone-0.03.tar.gz', 0], # 2178
14 | ['STRZELEC/CGI-SpeedUp-0.11.tar.gz', 0], # 2187
15 |
16 | # has a dash, though not in the same line as the package
17 | ['HODEL/Brasil-Checar-CGC-1.01a.tar.gz', 0], # 2018
18 |
19 | # abstract in non-.pm file
20 | ['LEONT/App-find2perl-1.003.tar.gz', 1],
21 |
22 | # invalid =encoding (utf-8;)
23 | ['INGY/IO-All-0.40.tar.gz', 0],
24 |
25 | # script name with a dash instead of package name
26 | ['SKIRMESS/App-ReportPrereqs-0.001.tar.gz', 1],
27 | );
28 |
29 | done_testing;
30 |
--------------------------------------------------------------------------------
/t/kwalitee/has_buildtool.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_buildtool',
6 | ['LAWSONK/Gtk2-Ex-MPlayerEmbed-0.03.tar.gz', 0], # 465
7 | ['HEDWIG/Template-Plugin-Duration-0.01.tar.gz', 0], # 474
8 | ['WEBY/Mojolicious-Plugin-Mobi-0.02.tar.gz', 0], # 474
9 | ['TIMA/Amazon-Bezos-0.001.tar.gz', 0], # 510
10 | ['LEOCHARRE/LEOCHARRE-X11-WMCtrl-0.01.tar.gz', 0], # 514
11 | ['WOLDRICH/Bundle-Woldrich-Term-0.02.tar.gz', 0], # 530
12 | ['WOLDRICH/App-epic-0.014.tar.gz', 0], # 812
13 | ['TAG/AnyEvent-Peer39-0.32.tar.gz', 0], # 824
14 | ['ABCABC/CFTP_01.tar.gz', 0], # 897
15 | ['DAMBAR/Catalyst-Plugin-Imager-0.01.tar.gz', 0], # 1026
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_changelog.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_changelog',
6 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
7 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
8 | ['BENNING/Math-BaseMulti-1.00.tar.gz', 0], # 2942
9 | ['HEYTRAV/Mojolicious-Plugin-Libravatar-1.08.tar.gz', 0], # 3415
10 | ['TXH/Template-Plugin-Filter-MinifyHTML-0.02.tar.gz', 0], # 3484
11 | ['MANIGREW/SEG7-1.0.1.tar.gz', 0], # 3847
12 | ['MUIR/modules/rinetd.pl-1.2.tar.gz', 0], # 4319
13 | ['GSB/WWW-Crab-Client-0.03.tar.gz', 0], # 4352
14 | ['RSHADOW/libmojolicious-plugin-human-perl_0.6.orig.tar.gz', 0], # 4504
15 | ['SRPATT/Finance-Bank-CooperativeUKPersonal-0.02.tar.gz', 0], # 4991
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_human_readable_license.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_human_readable_license',
6 | ['JJUDD/DBIx-Class-TimeStamp-HiRes-v1.0.0.tar.gz', 0], # 2596
7 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
8 | ['SCILLEY/POE/Component/IRC/Plugin/IRCDHelp-0.02.tar.gz', 0], # 3243
9 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
10 | ['JEEN/WebService-Aladdin-0.08.tar.gz', 0], # 4287
11 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
12 | ['IAMCAL/Flickr-API-1.06.tar.gz', 0], # 5172
13 | ['ANANSI/Anansi-ObjectManager-0.06.tar.gz', 0], # 5246
14 | );
15 |
16 | done_testing;
17 |
--------------------------------------------------------------------------------
/t/kwalitee/has_known_license_in_source_file.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_known_license_in_source_file',
6 | ['JJUDD/DBIx-Class-TimeStamp-HiRes-v1.0.0.tar.gz', 0], # 2596
7 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
8 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
9 | ['IAMCAL/Flickr-API-1.06.tar.gz', 0], # 5172
10 |
11 | # =head1 AUTHOR / COPYRIGHT / LICENSE
12 | ['BJOERN/AI-CRM114-0.01.tar.gz', 1],
13 |
14 | # has =head1 COPYRIGHT AND LICENSE without closing =cut
15 | ['DAMI/DBIx-DataModel-2.39.tar.gz', 1],
16 |
17 | # has =head1 LICENSE followed by =head1 COPYRIGHT
18 | ['YSASAKI/App-pfswatch-0.08.tar.gz', 1],
19 |
20 | # ignore inc/Devel/CheckLib
21 | ['DJERIUS/Lua-API-0.02.tar.gz', 1],
22 |
23 | # https://github.com/cpants/www-cpants/issues/44
24 | ['NEILB/Business-CCCheck-0.09.tar.gz', 1],
25 | );
26 |
27 | done_testing;
28 |
--------------------------------------------------------------------------------
/t/kwalitee/has_license_in_source_file.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_license_in_source_file',
6 | ['JJUDD/DBIx-Class-TimeStamp-HiRes-v1.0.0.tar.gz', 0], # 2596
7 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
8 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
9 | ['IAMCAL/Flickr-API-1.06.tar.gz', 0], # 5172
10 |
11 | # =head1 AUTHOR / COPYRIGHT / LICENSE
12 | ['BJOERN/AI-CRM114-0.01.tar.gz', 1],
13 |
14 | # has =head1 COPYRIGHT AND LICENSE without closing =cut
15 | ['DAMI/DBIx-DataModel-2.39.tar.gz', 1],
16 |
17 | # has =head1 LICENSE followed by =head1 COPYRIGHT
18 | ['YSASAKI/App-pfswatch-0.08.tar.gz', 1],
19 |
20 | # bin/ only
21 | ['JONALLEN/pod2pdf-0.42.tar.gz', 1],
22 |
23 | # script/ only
24 | ['GOEVERT/RePrec-0.032.tar.gz', 1],
25 |
26 | # scripts/ only
27 | ['NKH/App-Term-Jump-0.04.32.tar.gz', 1],
28 | );
29 |
30 | done_testing;
31 |
--------------------------------------------------------------------------------
/t/kwalitee/has_manifest.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_manifest',
6 | ['LAWSONK/Gtk2-Ex-MPlayerEmbed-0.03.tar.gz', 0], # 465
7 | ['WEBY/Mojolicious-Plugin-Mobi-0.02.tar.gz', 0], # 474
8 | ['WOLDRICH/Bundle-Woldrich-Term-0.02.tar.gz', 0], # 530
9 | ['WOLDRICH/App-epic-0.014.tar.gz', 0], # 812
10 | ['TAG/AnyEvent-Peer39-0.32.tar.gz', 0], # 824
11 | ['ABCABC/CFTP_01.tar.gz', 0], # 897
12 | ['SAULIUS/Date-Holidays-LT-0.01.tar.gz', 0], # 1532
13 | ['RUFF/DJabberd-Authen-Dovecot-0.1.tar.gz', 0], # 2105
14 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
15 | ['GVENKAT/JSON-HPack-0.0.3.tar.gz', 0], # 2475
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_meta_json.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_meta_json',
6 | ['AANOAA/WebService-Naver-TTS-v0.0.3.tar.gz', 0], # 3610
7 | ['DLIMA/Business-BR-CNJ-0.01.tar.gz', 0], # 2419
8 | ['INA/Fake/Our/Fake-Our-0.12.tar.gz', 0], # 7333
9 | ['JJNAPIORK/Catalyst-Plugin-DBIC-ConsoleQueryLog-0.002.tar.gz', 0], # 9867
10 | ['KORSANI/Log-Funlog-Lang-0.4.tar.gz', 0], # 3472
11 | ['MASAKYST/Acme-Kiyoshi-Array-0.01.tar.gz', 0], # 8628
12 | ['RRVCKU/Lingua-Stem-Uk-0.01.tar.gz', 0], # 4724
13 | ['RSN/IO-All-SFTP-0.01.tar.gz', 0], # 2720
14 | ['TTNDY/Sendmail-AbuseIPDB-0.10.tar.gz', 0], # 6078
15 | ['VVELOX/Log-Colorize-Helper-0.1.1.tar.gz', 0], # 5890
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_meta_yml.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_meta_yml',
6 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
7 | ['ANANSI/Anansi-Singleton-0.02.tar.gz', 0], # 2664
8 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
9 | ['DAVIEIRA/Data_Translate-0.3.tar.gz', 0], # 2970
10 | ['ANANSI/Anansi-Class-0.03.tar.gz', 0], # 3028
11 | ['ANANSI/Anansi-Actor-0.04.tar.gz', 0], # 3157
12 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
13 | ['MANIGREW/SEG7-1.0.1.tar.gz', 0], # 3847
14 | ['HITHIM/Socket-Mmsg-0.02.tar.gz', 0], # 3946
15 | ['STEFANOS/Net-SMTP_auth-SSL-0.2.tar.gz', 0], # 4058
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_proper_version.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_proper_version',
6 | ['WINTRU/Mica-1.a.0.tar.gz', 0], # 1196
7 | ['TSUNODA/Sledge-Plugin-SNMP-0.01a.tar.gz', 0], # 1767
8 | ['TIMA/Bundle-Melody-Test-0.9.6a.tar.gz', 0], # 2042
9 | ['CFABER/libuuid-perl_0.02.orig.tar.gz', 0], # 2091
10 | ['DANPEDER/MIME-Base32-1.02a.tar.gz', 0], # 3136
11 | ['MOBILEART/Net-OmaDrm-0.10a.tar.gz', 0], # 3208
12 | ['ASKADNA/CGI-Application-Plugin-Eparam-0.04f.tar.gz', 0], # 3228
13 | ['SPECTRUM/Math-BigSimple-1.1a.tar.gz', 0], # 3269
14 | ['TSKIRVIN/HTML-FormRemove-0.3a.tar.gz', 0], # 3625
15 | ['SHY/Wifi/Wifi-0.01a.tar.gz', 0], # 3767
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_readme.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_readme',
6 | ['SEVEAS/Term-Multiplexed-0.1.0.tar.gz', 0], # 1701
7 | ['SROMANOV/App-Nopaste-Service-Dpaste-0.01.tar.gz', 0], # 2448
8 | ['SROMANOV/Games-Chess-Position-Unicode-0.01.tar.gz', 0], # 2629
9 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
10 | ['BOOK/Bundle-MetaSyntactic-1.026.tar.gz', 0], # 3178
11 | ['BENMEYER/Finance-btce-0.02.tar.gz', 0], # 3575
12 | ['SYSADM/Mojolicious-Plugin-DeCSRF-0.94.tar.gz', 0], # 3654
13 | ['BKB/Lingua-EN-PluralToSingular-0.06.tar.gz', 0], # 3747
14 | ['MANIGREW/SEG7-1.0.1.tar.gz', 0], # 3847
15 | ['BKB/Lingua-JA-Gairaigo-Fuzzy-0.02.tar.gz', 0], # 4159
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_separate_license_file.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_separate_license_file',
6 | ['TOBYINK/Platform-Windows-0.002.tar.gz', 0], # 2206
7 | ['TOBYINK/Platform-Unix-0.002.tar.gz', 0], # 2264
8 | ['BOOK/Acme-MetaSyntactic-errno-1.003.tar.gz', 0], # 2889
9 | ['COOLMEN/Test-More-Color-0.04.tar.gz', 0], # 2963
10 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
11 | ['HITHIM/Socket-Mmsg-0.02.tar.gz', 0], # 3946
12 | ['COOLMEN/Test-Mojo-More-0.04.tar.gz', 0], # 4301
13 | ['MUGENKEN/Bundle-Unicheck-0.02.tar.gz', 0], # 4596
14 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
15 | ['CHENRYN/Nagios-Plugin-ByGmond-0.01.tar.gz', 0], # 5159
16 |
17 | # LICENSE.txt (RT #114274)
18 | ['IKEGAMI/Syntax-Feature-Void-v1.0.0.tar.gz', 1], #
19 | );
20 |
21 | done_testing;
22 |
--------------------------------------------------------------------------------
/t/kwalitee/has_tests.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_tests',
6 | ['MSTROUT/pumpkin-pragma-placeholder-0.001.tar.gz', 0], # 961
7 | ['SEVEAS/Term-Multiplexed-0.1.0.tar.gz', 0], # 1701
8 | ['PBLAIR/Copy-From-Git-0.000302.tar.gz', 0], # 2235
9 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
10 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
11 | ['NKH/Devel-Depend-Cl-0.06.tar.gz', 0], # 3540
12 | ['SEMUELF/WWW-Github-Files-0.02.tar.gz', 0], # 3634
13 | ['MUIR/modules/rinetd.pl-1.2.tar.gz', 0], # 4319
14 | ['SRPATT/Finance-Bank-CooperativeUKPersonal-0.02.tar.gz', 0], # 4991
15 | ['NANARDON/RT-Interface-Email-Filter-CheckMessageId-0.1.tar.gz', 0], # 5398
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_tests_in_t_dir.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_tests_in_t_dir',
6 | ['SEVEAS/Term-Multiplexed-0.1.0.tar.gz', 0], # 1701
7 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
8 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
9 | ['MUIR/modules/rinetd.pl-1.2.tar.gz', 0], # 4319
10 | ['SRPATT/Finance-Bank-CooperativeUKPersonal-0.02.tar.gz', 0], # 4991
11 | ['IAMCAL/Flickr-API-1.06.tar.gz', 0], # 5172
12 | ['PJB/Speech-Speakup-1.04.tar.gz', 0], # 7410
13 | ['FIBO/Task-Viral-20130508.tar.gz', 0], # 8128
14 | ['ADAMBA/Algorithm-MOS-0.001.tar.gz', 0], # 8570
15 | ['FIBO/Task-BeLike-FIBO-20130508.tar.gz', 0], # 8922
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/has_version.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'has_version',
6 | ['IDIVISION/nginx.pm.tar.gz', 0], # 1059
7 | ['PIROLIX/MIME_Base32_Readable.zip', 0], # 1461
8 | ['WILSONPM/OutlineNumber.tar.gz', 0], # 1798
9 | ['STEFANOS/Text-Phonetic-Caverphone.tar.gz', 0], # 1912
10 | ['JACKS/SelfStubber.tar.gz', 0], # 1934
11 | ['DAHILLMA/Geo-GoogleEarth-Document-modules.tar.gz', 0], # 1965
12 | ['SMAN/rpn.tar.gz', 0], # 1966
13 |
14 | # version 0, though problematic
15 | ['DAMOG/WWW-Tumblr-0.tar.gz', 1],
16 | ['TOKUHIROM/Perl-Build-0.tar.gz', 1],
17 | ['STEFANOS/Text-Phonetic-MatchRatingCodex-1-0.tar.gz', 1],
18 |
19 | # Parse::Distname allows the following
20 | ['ANDK/Memo-bindist-any-bin-2-archname-compiler.tar.gz', 1], # 1076
21 | );
22 |
23 | done_testing;
24 |
--------------------------------------------------------------------------------
/t/kwalitee/main_module_version_matches_dist_version.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'main_module_version_matches_dist_version',
6 | ['JEROMEMCK/Net-ICQ-On-1.7.tar.gz', 0], # 1005
7 | ['ARCANEZ/WWW-Mailchimp-0.006_02.tar.gz', 0], # 1007
8 | ['IDIVISION/nginx.pm.tar.gz', 0], # 1059
9 | ['MALUKU/sofu-config/sofu-config-0.2.tar.gz', 0], # 1059
10 | ['ZLIPTON/Bundle-Bonsai-0.02.tar.gz', 0], # 1075
11 | ['ANDK/Memo-bindist-any-bin-2-archname-compiler.tar.gz', 0], # 1076
12 | ['IDIVISION/nginx-0.0.1.tar.gz', 0], # 1082
13 | ['MTHURN/Devel-Fail-Make-1.005.tar.gz', 0], # 1088
14 | ['ILYAZ/os2/tk/binary/update-03.zip', 0], # 1125
15 |
16 | # illegal provides
17 | ['DJERIUS/Lua-API-0.02.tar.gz', 0],
18 |
19 | # v-version without v in the dist version
20 | ['SHLOMIF/HTML-TabbedExamples-Generate-0.0.5.tar.gz', 1],
21 | ['JANDREW/Data-Walk-Extracted-0.024.004.tar.gz', 1],
22 | ['DOMM/Module-ExtractUse-0.33.tar.gz', 1],
23 |
24 | # no main module
25 | ['MEWILCOX/apache.authznetldap.02.tar.gz', 0], # 1051
26 |
27 | # four dots (may lose info if parsed as a number)
28 | ['MISHIN/POD2-RU-5.18.0.1.33.tar.gz', 1],
29 |
30 | # provides without version(s)
31 | ['BURNERSK/Encoding-HandleUtf8-0.002.tar.gz', 0],
32 | );
33 |
34 | done_testing;
35 |
--------------------------------------------------------------------------------
/t/kwalitee/manifest_matches_dist.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'manifest_matches_dist',
6 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 0], # 2409
7 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907
8 | ['COOLMEN/Test-More-Color-0.04.tar.gz', 0], # 2963
9 | ['APIOLI/YAMC-0.2.tar.gz', 0], # 3245
10 | ['BENMEYER/Finance-btce-0.02.tar.gz', 0], # 3575
11 | ['SJQUINNEY/MooseX-Types-EmailAddress-1.1.2.tar.gz', 0], # 4257
12 | ['RSHADOW/libmojolicious-plugin-human-perl_0.6.orig.tar.gz', 0], # 4504
13 | ['LEPREVOST/Math-SparseMatrix-Operations-0.06.tar.gz', 0], # 4593
14 | ['SRPATT/Finance-Bank-CooperativeUKPersonal-0.02.tar.gz', 0], # 4991
15 | ['SULLR/Net-PcapWriter-0.71.tar.gz', 0], # 5337
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_json_conforms_to_known_spec.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_json_conforms_to_known_spec',
6 | # scalar license
7 | ['DMAKI/XML-RSS-Liberal-0.04.tar.gz', 0],
8 |
9 | # no abstract
10 | ['MLEHMANN/AnyEvent-DBus-0.31.tar.gz', 0],
11 |
12 | # resource -> license
13 | ['BARBIE/Template-Plugin-Lingua-EN-NameCase-0.01.tar.gz', 0],
14 |
15 | # invalid license
16 | ['TOMO/src/Net-SMTPS-0.03.tar.gz', 0], # perl
17 | ['RSAVAGE/DBIx-Admin-CreateTable-2.08.tgz', 0], # artistic_2_0
18 |
19 | # 'origin' for 'repository' does not have a URL scheme
20 | ['RJBS/Sub-Import-0.092800.tar.gz', 0],
21 | ['MARCEL/Permute-Named-1.100980.tar.gz', 0],
22 |
23 | # '' for 'repository' is not a valid URL.
24 | ['KEEDI/Pod-Weaver-Section-Encoding-0.100830.tar.gz', 0],
25 |
26 | # git@github.com:... does not have a URL authority
27 | ['TIMB/PostgreSQL-PLPerl-Trace-1.001.tar.gz', 0],
28 |
29 | # Custom key must begin with 'x_' or 'X_'.
30 | ['AVAR/Bot-Twatterhose-0.04.tar.gz', 0],
31 |
32 | # value is an undefined string
33 | ['TOBYINK/Return-Type-0.004.tar.gz', 0],
34 | );
35 |
36 | done_testing;
37 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_json_is_parsable.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_json_is_parsable',
6 | # invalid control characters in abstract
7 | ['JOHND/Data-Properties-YAML-0.04.tar.gz', 0], # \r
8 | ['WINFINIT/Catalyst-Plugin-ModCluster-0.02.tar.gz', 0], # \t
9 | ['LIKHATSKI/Ubic-Watchdog-Notice-0.31.tar.gz', 0], # \@
10 |
11 | # '"' expected
12 | ['SHURD/DMTF-CIM-WSMan-v0.09.tar.gz', 0],
13 | ['RFREIMUTH/RandomJungle-0.05.tar.gz', 0],
14 |
15 | # VMS
16 | ['PFAUT/VMS-Time-0_1.zip', 0],
17 |
18 | # malformed JSON string
19 | ['MAXS/Palm-MaTirelire-1.12.tar.gz', 0], # \x{fffd}
20 |
21 | # illegal backslash escape sequence
22 | ['JHTHORSEN/Convos-0.6.tar.gz', 0],
23 |
24 | # missing trailing comma (seemingly edited by hand)
25 | ['PLOCKABY/TAP-Formatter-BambooExtended-1.01.tar.gz', 0],
26 | );
27 |
28 | done_testing;
29 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_conforms_to_known_spec.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_conforms_to_known_spec',
6 | # '' for 'Carp' is not a valid version.
7 | ['MATIU/WWW-AfinimaKi-0.1.tar.gz', 0], # 518
8 |
9 | # 'HASH(0xb8924f0)' for 'repository' does not have a URL scheme
10 | ['JBERGER/Alien-GSL-0.01.tar.gz', 0], # 556
11 |
12 | # License 'Public domain' is invalid (license)
13 | ['SEVEAS/Term-Multiplexed-0.2.2.tar.gz', 0], # 1683
14 |
15 | # Missing mandatory field, 'author' (author)
16 | ['ANDK/CPAN-Test-Dummy-Perl5-Make-CircDepeOne-1.00.tar.gz', 0], # 1893
17 |
18 | # 'ExtUtils::MakeMaker version 6.17' for 'generated_by' is not a valid version. (requires -> generated_by)
19 | ['MITTI/PDF-Report-Table-1.00.tar.gz', 0], # 2300
20 |
21 | # Expected a map structure from string or file. (requires)
22 | ['ANDK/CPAN-Test-Dummy-Perl5-Make-Expect-1.00.tar.gz', 0], # 2323
23 |
24 | # 'DateTime::Event::Easter' for 'Time::Piece' is not a valid version.
25 | ['CLIFFORDJ/Date-Holidays-EnglandWales-0.01.tar.gz', 0], # 3085
26 |
27 | # Key 'Acme::Unic<ö>de' is not a legal module name.
28 | ['SCHWIGON/acme-unicode/Acme-Uenicoede-0.0501.tar.gz', 0], # 3651
29 |
30 | # 'meta-spec' => '1.1' is kind of broken, but it's not regarded
31 | # as a fatal error as of CPAN::Meta 2.132830.
32 | ['JOSEPHW/XML-Writer-0.545.tar.gz', 1],
33 | );
34 |
35 | done_testing;
36 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_declares_perl_version.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_declares_perl_version',
6 | ['TOBYINK/Platform-Windows-0.002.tar.gz', 0], # 2206
7 | ['TOBYINK/Platform-Unix-0.002.tar.gz', 0], # 2264
8 | ['COOLMEN/Test-More-Color-0.04.tar.gz', 0], # 2963
9 | ['TXH/Template-Plugin-Filter-MinifyHTML-0.02.tar.gz', 0], # 3484
10 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
11 | ['SMUELLER/Math-Symbolic-Custom-CCompiler-1.03.tar.gz', 0], # 5244
12 | ['LTP/Game-Life-0.05.tar.gz', 0], # 6535
13 | ['KPEE/Carp-Growl-0.0.3.tar.gz', 0], # 6682
14 | );
15 |
16 | done_testing;
17 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_has_license.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_has_license',
6 | ['CHENGANG/Log-Lite-0.05.tar.gz', 0], # 2739
7 | ['SCILLEY/POE/Component/IRC/Plugin/IRCDHelp-0.02.tar.gz', 0], # 3243
8 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
9 | ['HITHIM/Socket-Mmsg-0.02.tar.gz', 0], # 3946
10 | ['FAYLAND/Acme-CPANAuthors-Chinese-0.26.tar.gz', 0], # 4474
11 | ['BENNIE/ACME-KeyboardMarathon-1.15.tar.gz', 0], # 4479
12 | ['ALEXP/Catalyst-Model-DBI-0.32.tar.gz', 0], # 4686
13 | ['YTURTLE/Nephia-Plugin-Response-YAML-0.01.tar.gz', 0], # 4948
14 | ['CHENRYN/Nagios-Plugin-ByGmond-0.01.tar.gz', 0], # 5159
15 | ['IAMCAL/Flickr-API-1.06.tar.gz', 0], # 5172
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_has_provides.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_has_provides',
6 | ['TOBYINK/Platform-Windows-0.002.tar.gz', 0], # 2206
7 | ['TOBYINK/Platform-Unix-0.002.tar.gz', 0], # 2264
8 | ['COOLMEN/Test-More-Color-0.04.tar.gz', 0], # 2963
9 | ['COOLMEN/Test-Mojo-More-0.04.tar.gz', 0], # 4301
10 | ['SMUELLER/Math-SymbolicX-Complex-1.01.tar.gz', 0], # 4719
11 | ['CHENRYN/Nagios-Plugin-ByGmond-0.01.tar.gz', 0], # 5159
12 | ['SMUELLER/Math-Symbolic-Custom-CCompiler-1.03.tar.gz', 0], # 5244
13 | ['LTP/Game-Life-0.05.tar.gz', 0], # 6535
14 | );
15 |
16 | done_testing;
17 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_has_repository_resource.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_has_repository_resource',
6 | ['ISHIGAKI/Acme-CPANAuthors-Japanese-0.131002.tar.gz', 0],
7 | ['ISHIGAKI/Acme-CPANAuthors-0.23.tar.gz', 1],
8 | );
9 | done_testing;
10 |
--------------------------------------------------------------------------------
/t/kwalitee/meta_yml_is_parsable.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'meta_yml_is_parsable',
6 | # No META.yml
7 | ['UNBIT/Net-uwsgi-1.1.tar.gz', 1], # 2409
8 |
9 | # Stream does not end with newline character
10 | ['SCILLEY/POE/Component/IRC/Plugin/IRCDHelp-0.02.tar.gz', 0], # 3243
11 |
12 | # Error reading from file: utf8 "\xB0" does not map to Unicode
13 | ['WINTRU/Mica-1.a.0.tar.gz', 0], # 1196
14 |
15 | # CPAN::Meta::YAML does not support a feature in line
16 | ['STRO/Task-CPANAuthors-STRO-PPMCreator-2009.1018.tar.gz', 0], # 1555
17 |
18 | # CPAN::Meta::YAML failed to classify line ' --- #YAML:1.0'
19 | ['XPANEL/XPanel-0.0.7.tar.gz', 0], # 2207
20 |
21 | # CPAN::Meta::YAML found bad indenting
22 | ['NUFFIN/Devel-STDERR-Indent-0.01.tar.gz', 0], # 2594
23 |
24 | # CPAN::Meta::YAML found illegal characters
25 | ['SOCK/WWW-Search-Feedster-0.02.tar.gz', 0], # 3220
26 | );
27 |
28 | done_testing;
29 |
--------------------------------------------------------------------------------
/t/kwalitee/no_abstract_stub_in_pod.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_abstract_stub_in_pod',
6 | # h2xs
7 | ['ERICW/Roll-1.0.tar.gz', 0], # 2188
8 | ['IEFREMOV/Statistics-CountAverage-0.02.tar.gz', 0], # 2839
9 | ['HSLEE/Search-Equidistance-0.01.tar.gz', 0], # 3142
10 | ['OPITZ/URL-Grab-1.4.tar.gz', 0], # 3210
11 |
12 | # Module::Starter etc
13 | ['IKEBE/WebService-Livedoor-Auth-0.01.tar.gz', 0],
14 |
15 | # Minilla
16 | ['NNUTTER/Git-Repository-Plugin-Gerrit-0.03.tar.gz', 0],
17 | );
18 |
19 | done_testing;
20 |
--------------------------------------------------------------------------------
/t/kwalitee/no_broken_auto_install.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_broken_auto_install',
6 | ['GUGOD/WWW-Shorten-0rz-0.07.tar.gz', 0], # 14671
7 | ['CLKAO/IPC-Run-SafeHandles-0.04.tar.gz', 0], # 19431
8 | ['TBR/WKHTMLTOPDF-0.02.tar.gz', 0], # 19819
9 | ['MONS/Test-More-UTF8-0.04.tar.gz', 0], # 19952
10 | ['SKAUFMAN/Template-Plugin-Devel-StackTrace-0.02.tar.gz', 0], # 19960
11 | ['LUKEC/Test-Mock-LWP-0.06.tar.gz', 0], # 20054
12 | ['GAOU/Ubigraph-0.05.tar.gz', 0], # 20092
13 | ['YVESAGO/Jifty-Plugin-Userpic-0.9.tar.gz', 0], # 20161
14 | ['YVESAGO/Jifty-Plugin-SiteNews-0.9.tar.gz', 0], # 20249
15 | ['BOLAV/DateTime-Format-Duration-DurationString-0.03.tar.gz', 0], # 20527
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_broken_module_install.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_broken_module_install',
6 | ['GUGOD/Kwiki-Session-0.01.tar.gz', 0], # 7880
7 | ['GUGOD/Kwiki-Widgets-Links-0.01.tar.gz', 0], # 8214
8 | ['CLSUNG/Lingua-ZH-Segment-0.02.tar.gz', 0], # 8236
9 | ['RHUNDT/Catalyst-Model-Oryx-0.01.tar.gz', 0], # 8255
10 | ['XERN/Template-Plugin-IO-All-0.01.tar.gz', 0], # 8462
11 | ['IJLIAO/WWW-Scraper-ISBN-TWSrbook_Driver-0.01.tar.gz', 0], # 9139
12 | ['IJLIAO/WWW-Scraper-ISBN-TWYlib_Driver-0.01.tar.gz', 0], # 9199
13 | ['IJLIAO/WWW-Scraper-ISBN-TWTenlong_Driver-0.01.tar.gz', 0], # 9210
14 | ['IJLIAO/WWW-Scraper-ISBN-TWPchome_Driver-0.01.tar.gz', 0], # 9308
15 | ['IJLIAO/WWW-Scraper-ISBN-TWSoidea_Driver-0.01.tar.gz', 0], # 9348
16 |
17 | # M::I 1.04
18 | ['KARMAN/Dezi-UI-0.001000.tar.gz', 0],
19 | );
20 |
21 | done_testing;
22 |
--------------------------------------------------------------------------------
/t/kwalitee/no_dot_dirs.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_dot_dirs',
6 | # .svn
7 | ['WAZZUTEKE/Net-Twitter-RandomUpdate-1.2.tar.gz', 0], # 4710
8 | ['HOVENKO/HTML-ScriptLoader-1.00.tar.gz', 0], # 5160
9 |
10 | # .git
11 | ['EXODIST/local-lib-deps-0.02.tar.gz', 0], # 11662
12 | ['AQUILINA/Acme-Warn-LOLCAT-0.01.tar.gz', 0], # 12638
13 |
14 | # .hg
15 | ['LICHTKIND/Tie-Wx-Widget-0.5.tar.gz', 0], # 15259
16 |
17 | # .bzr
18 | ['GOOZBACH/asterisk-store/Asterisk-Store-Queue-Member-0.1.tar.gz', 0], # 19811
19 |
20 | # others
21 | ['BRENDAN/Test-Mimic-0.009007.tar.gz', 0], # 11809
22 | ['MUENALAN/Workflow-Aline-0.03.tar.gz', 0], # 24529
23 | ['NACHBAUR/Test-Story-0.07.tar.gz', 0], # 41977
24 | ['RKIES/ec-1.6.tar.gz', 0], # 77537
25 | ['VVELOX/Toader-1.1.0.tar.gz', 0], # 99633
26 | ['BBYRD/DBIx-Class-0.08204_01.tar.gz', 0], # 697468
27 | );
28 |
29 | done_testing;
30 |
--------------------------------------------------------------------------------
/t/kwalitee/no_dot_underscore_files.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_dot_underscore_files',
6 | ['LEPT/String-Iota-0.85.tar.gz', 0], # 2441
7 | ['DAMOG/Data-Format-HTML-0.5.1.tar.gz', 0], # 2737
8 | ['BRENTDAX/Template-Plugin-Lingua-Conjunction-0.02.tar.gz', 0], # 2875
9 | ['SOCK/WWW-Search-UrbanDictionary-0.4.tar.gz', 0], # 3176
10 | ['CLADI/SmarTalk_v10.tar.gz', 0], # 3289
11 | ['KAOSAGNT/CGI-Session-Serialize-php-1.1.tar.gz', 0], # 3336
12 | ['EBRAGIN/Cache-Memcached-Tags-0.02.tar.gz', 0], # 3399
13 | ['AHICOX/XML-Parser-YahooRESTGeocode-0.2.tar.gz', 0], # 3503
14 | ['RECSKY/Bot-BasicBot-Pluggable-Module-Pastebin-0.01.tar.gz', 0], # 3663
15 | ['SOCK/WWW-Yahoo-KeywordExtractor-0.5.2.tar.gz', 0], # 3806
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_files_to_be_skipped.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_files_to_be_skipped',
6 | ['ABELTJE/Test-DBIC-SQLite-0.01.tar.gz', 0], # 4821
7 | ['GTERMARS/Data-FormValidator-EmailValid-0.05.tar.gz', 0], # 5597
8 | ['LARION/Lingua-Translator-Microsoft-1.1.1.tar.gz', 0], # 6794
9 | ['MANWAR/Devel-Timer-0.11.tar.gz', 0], # 8150
10 | ['MIKEH/Bundle-Interchange-1.08.tar.gz', 0], # 2564
11 | ['MOOCOW/Alien-Moot-0.003.tar.gz', 0], # 3384
12 | ['RSN/ORDB-DebianModules-Generator-0.02.tar.gz', 0], # 3854
13 | ['SREZIC/Geo-Coder-Googlev3-0.17.tar.gz', 0], # 8640
14 | ['TOBYINK/Alien-LibXML-0.004.tar.gz', 0], # 9119
15 | ['TOMK/lib-archive-0.3.tar.gz', 0], # 8196
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_generated_files.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_generated_files',
6 | ['SANTEX/Finance-Quant-Quotes-0.01.tar.gz', 0], # 3159
7 | ['SANTEX/Finance-Quant-TA-0.01.tar.gz', 0], # 3269
8 | ['TAKERU/Catalyst-Model-Estraier-v0.0.6.tar.gz', 0], # 6175
9 | ['BDFOY/Unicode-Support-0.001.tar.gz', 0], # 6633
10 | ['RPETTETT/Module-PortablePath-0.17.tar.gz', 0], # 6951
11 | ['ROBN/Class-Constant-0.06.tar.gz', 0], # 7557
12 | ['TUSHAR/Log-SelfHistory_0.1.tar.gz', 0], # 8412
13 | ['CCCP/Plugins-Factory-0.01.tar.gz', 0], # 8876
14 | ['JAMHED/Dancer-Plugin-Scoped-0.02fix.tar.gz', 0], # 8885
15 | ['JKRAMER/SQL-Beautify-0.04.tar.gz', 0], # 8972
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_invalid_versions.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_invalid_versions',
6 | # string in version
7 | ['TIMA/Bundle-Melody-Test-0.9.6a.tar.gz', 0], # 2042
8 | ['JHARDING/Text-Typoifier-0.04a.tar.gz', 0], # 2334
9 | ['SPECTRUM/Math-BigSimple-1.1a.tar.gz', 0], # 3269
10 | ['TSKIRVIN/HTML-FormRemove-0.3a.tar.gz', 0], # 3625
11 | ['TBONE/Chess-Elo-1.0a.tar.gz', 0], # 3979
12 |
13 | # exponential
14 | ['ALTREUS/Catalyst-Authentication-Store-MongoDB-1e-4.tar.gz', 0], # 3788 (1e-4)
15 |
16 | # version from other module
17 | ['ROBAU/Data-ACL-0.02.tar.gz', 0], # 2844
18 |
19 | # scalar ref
20 | ['MAUKE/Defaults-Mauke-0.09.tar.gz', 1], # 3136
21 |
22 | # my empty string
23 | ['INGY/YAML-MLDBM-0.10.tar.gz', 0], # 3420
24 |
25 | # others
26 | ['ARTO/CGI-Application-Plugin-Config-IniFiles-0.03.tar.gz', 0], # 3004
27 | );
28 |
29 | done_testing;
30 |
--------------------------------------------------------------------------------
/t/kwalitee/no_local_dirs.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_local_dirs',
6 | # local, but no .pm files
7 | ['ERMEYERS/Bundle-Modules-2006.0606.tar.gz', 1], # 29296
8 |
9 | # perl5 (with non-portable files)
10 | # ['PERLER/MooseX-Attribute-Deflator-2.1.3.tar.gz', 0], # 109876
11 |
12 | # fatlib
13 | ['GETTY/Installer-0.005.tar.gz', 0], # 295629
14 | );
15 |
16 | done_testing;
17 |
--------------------------------------------------------------------------------
/t/kwalitee/no_maniskip_error.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_maniskip_error',
6 | # Unrecognized escape \i passed through in regex
7 | ['GARU/Net-TinyERP-0.05.tar.gz', 0], # 9814
8 | ['GARU/Data-Printer-0.40.tar.gz', 0], # 58847
9 |
10 | # Unrecognized escape \M passed through in regex
11 | ['FRANCKC/AnyEvent-Riak-0.01.tar.gz', 0], # 3878
12 |
13 | # Quantifier follows nothing in regex
14 | ['DVWRIGHT/WWW-TasteKid-0.1.4.tar.gz', 0], # 38770
15 | ['JANE/Data-TDMA-0.2.tar.gz', 0], # 17907
16 |
17 | # ^* matches null string many times in regex
18 | ['MANWAR/Term-ProgressBar-2.22.tar.gz', 0], # 19506
19 | ['SCHWIGON/class-methodmaker/Class-MethodMaker-2.24.tar.gz', 0], # 77242
20 | );
21 |
22 | done_testing;
23 |
--------------------------------------------------------------------------------
/t/kwalitee/no_missing_files_in_provides.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_missing_files_in_provides',
6 | ['MIYAGAWA/CPAN-Test-Dummy-Perl5-DifferentProvides-0.01.tar.gz', 0], # 9453
7 |
8 | ['LNATION/Moonshine-Bootstrap-0.01.tar.gz', 0], # 4621
9 | ['MANWAR/WWW-Google-APIDiscovery-0.23.tar.gz', 0], # 9876
10 |
11 | # trailing comma
12 | ['DFARRELL/stasis-0.07.tar.gz', 0], # 4705
13 |
14 | # inconsistent case
15 | ['PINE/SemVer-V2-Strict-0.13.tar.gz', 0], # 8597
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_mymeta_files.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_mymeta_files',
6 | ['SZABGAB/File-Open-OOP-0.01.tar.gz', 0], # 2431
7 | ['JHTHORSEN/The-synthesizer-0.01.tar.gz', 0], # 2514
8 | ['TENGU/Catalyst-Authentication-Credential-MultiFactor-1.0.tar.gz', 0], # 2577
9 | ['BDFOY/Psychic-Ninja-0.10_01.tar.gz', 0], # 2689
10 | ['JHTHORSEN/The-synthesizer-0.02.tar.gz', 0], # 2708
11 | ['ZZZ/Here-Template-0.1.tar.gz', 0], # 2793
12 | ['KIMOTO/Mojolicious-Plugin-AutoRoute-0.04.tar.gz', 0], # 2811
13 | ['BDFOY/Net-SSH-Perl-WithSocks-0.02.tar.gz', 0], # 2894
14 | ['ZZZ/Here-Template-0.2.tar.gz', 0], # 2902
15 | ['KIMOTO/Mojolicious-Plugin-AutoRoute-0.02.tar.gz', 0], # 2902
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_pax_headers.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_pax_headers',
6 | ['LAWALSH/mem-0.3.0.tar.gz', 0], # 1596
7 | ['ZAR/Mojolicious-Plugin-Captcha-0.01.tar.gz', 0], # 3591
8 | ['ATRICKETT/Config-Trivial-0.50.tar.gz', 0], # 10028
9 | ['LAWALSH/mem-0.3.1.tar.gz', 0], # 11201
10 | ['LAWALSH/P-1.0.19.tar.gz', 0], # 17520
11 | ['LAWALSH/P-1.0.20.tar.gz', 0], # 17760
12 | ['MARKOV/XML-Compile-SOAP12-2.03.tar.gz', 0], # 19182
13 | ['MARKOV/Net-OAuth2-0.53.tar.gz', 0], # 20529
14 | ['MARKOV/XML-LibXML-Simple-0.93.tar.gz', 0], # 22821
15 | ['TBENK/App-nrun-v1.0.0_1.tar.gz', 0], # 27074
16 | ['MSCHILLI/libwww-perl-6.06.tar.gz', 0],
17 | );
18 |
19 | done_testing;
20 |
--------------------------------------------------------------------------------
/t/kwalitee/no_pod_errors.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_pod_errors',
6 | ['ALEXP/Catalyst-Model-Proxy-0.04.tar.gz', 0], # 3671
7 | ['LEEYM/Geo-Coder-Cache-0.06.tar.gz', 0], # 3907
8 | ['SULLR/Net-PcapWriter-0.71.tar.gz', 0], # 5337
9 | ['LTP/Game-Life-0.05.tar.gz', 0], # 6535
10 | ['JHALLOCK/StormX-Query-DeleteWhere-0.10.tar.gz', 0], # 6869
11 | ['DSYRTM/File-BetweenTree-1.02.tar.gz', 0], # 7590
12 | ['JHALLOCK/GappX-Dialogs-0.005.tar.gz', 0], # 7766
13 | ['FIBO/Task-Viral-20130508.tar.gz', 0], # 8128
14 | ['JSOBRIER/WebService-Browshot-1.11.0.tar.gz', 0], # 9434
15 | ['SALVA/Class-StateMachine-0.23.tar.gz', 0], # 9859
16 |
17 | # broken pod for testing should not be counted
18 | ['RJBS/Pod-Elemental-0.103000.tar.gz', 1],
19 |
20 | # has binary data
21 | ['DCANTRELL/Number-Phone-3.3000.tar.gz', 1],
22 |
23 | # no actual data
24 | ['APLA/DBI-Easy-0.18.tar.gz', 1],
25 | );
26 |
27 | done_testing;
28 |
--------------------------------------------------------------------------------
/t/kwalitee/no_stdin_for_prompting.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_stdin_for_prompting',
6 | ['GMCCAR/Jabber-SimpleSend-0.03.tar.gz', 0], # 3455
7 | ['SPEEVES/Apache-AuthenNIS-0.13.tar.gz', 0], # 4517
8 | ['SPEEVES/Apache2-AuthenSmb-0.01.tar.gz', 0], # 5219
9 | ['KROW/DBIx-Password-1.9.tar.gz', 0], # 5478
10 | ['GEOTIGER/Data-Fax-0.02.tar.gz', 0], # 5944
11 | ['GEOTIGER/CGI-Getopt-0.13.tar.gz', 0], # 6014
12 | ['SPEEVES/Apache2-AuthNetLDAP-0.01.tar.gz', 0], # 6855
13 | ['SPEEVES/Apache-AuthNetLDAP-0.29.tar.gz', 0], # 6952
14 | ['AMALTSEV/XAO-MySQL-1.02.tar.gz', 0], # 7242
15 | ['BHODGES/Mail-IMAPFolderSearch-0.03.tar.gz', 0], # 7326
16 | );
17 |
18 | done_testing;
19 |
--------------------------------------------------------------------------------
/t/kwalitee/no_symlinks.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | plan skip_all => "This test doesn't work well under Windows" if $^O eq 'MSWin32';
5 |
6 | test_kwalitee(
7 | 'no_symlinks',
8 | ['CMORRIS/Parse-Extract-Net-MAC48-0.01.tar.gz', 0], # 3094
9 | ['BRUMLEVE/autobless-1.0.1.tar.gz', 0], # 3318
10 | ['BRUMLEVE/wildproto-1.0.1.tar.gz', 0], # 3617
11 | ['BRUMLEVE/vm-1.0.1.tar.gz', 0], # 4236
12 | ['CRUSOE/Template-Plugin-Filter-ANSIColor-0.0.3.tar.gz', 0], # 4963
13 | ['GSLONDON/Devel-AutoProfiler-1.200.tar.gz', 0], # 6139
14 | ['PHAM/Business-Stripe-0.04.tar.gz', 0], # 6412
15 | ['GAVINC/Config-Directory-0.05.tar.gz', 0], # 8774
16 | ['NETVARUN/Net-Semantics3-0.10.tar.gz', 0], # 8930
17 | ['GAVINC/File-DirCompare-0.7.tar.gz', 0], # 9018
18 | );
19 |
20 | done_testing;
21 |
--------------------------------------------------------------------------------
/t/kwalitee/no_unauthorized_packages.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'no_unauthorized_packages',
6 | ['ECARROLL/URI-http-Dump-0.03.tar.gz', 0], # URI::http
7 | ['PMAKHOLM/Devel-RemoteTrace-0.3.tar.gz', 0], # DB
8 | ['SIMKIN/Apache-LoggedAuthDBI-0.12.tar.gz', 0], # DBI etc
9 | ['WILLERT/Catalyst-Model-EmailStore-0.03.tar.gz', 0], # Email::Store::DBI
10 | ['KNM/Ambrosia-0.010.tar.gz', 0], # deferred
11 | ['HACHI/MogileFS-Plugin-MetaData-0.01.tar.gz', 0], # MogileFS::Store
12 | );
13 |
14 | done_testing;
15 |
--------------------------------------------------------------------------------
/t/kwalitee/portable_filenames.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | my @tests = (
5 | ['DGL/Acme-mA-1337.1.tar.gz', 0], # 3411
6 | ['SCHWIGON/acme-unicode/Acme-Uenicoede-0.0501.tar.gz', 0], # 3651
7 | ['DGL/Acme-3mxA-1337.37.tar.gz', 0], # 4093
8 | ['KOORCHIK/Mojolicious-Plugin-RenderFile-0.06.tar.gz', 0], # 4114
9 | );
10 |
11 | # The followings are only valid for non-Win32 env
12 | # (because invalid files will not be extracted on Win32).
13 | push @tests, (
14 | ['PERFSONAR/perfSONAR_PS-Status-Common-0.09.tar.gz', 0], # 5439
15 | ['PERFSONAR/perfSONAR_PS-Client-Echo-0.09.tar.gz', 0], # 6654
16 | ['FRASE/Test-Builder-Clutch-0.05.tar.gz', 0], # 6764
17 | ['PERFSONAR/perfSONAR_PS-DB-File-0.09.tar.gz', 0], # 7704
18 | ['PERFSONAR/perfSONAR_PS-Client-LS-Remote-0.09.tar.gz', 0], # 8232
19 | ) unless $^O eq 'MSWin32';
20 |
21 | test_kwalitee('portable_filenames', @tests);
22 |
23 | done_testing;
24 |
--------------------------------------------------------------------------------
/t/kwalitee/prereq_matches_use.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | #plan skip_all => 'TODO';
5 |
6 | test_kwalitee(
7 | 'prereq_matches_use',
8 | ['KAWABATA/Text-HikiDoc-1.023.tar.gz', 0], # Text::Highlight etc
9 | ['PVIGIER/XML-Compile-SOAP-Daemon-Dancer2-0.07.tar.gz', 0], # Log::Report etc
10 | ['MICKEY/PONAPI-Server-0.003002.tar.gz', 0], # URI::Escape
11 | ['TEAM/Net-Async-Pusher-0.004.tar.gz', 0], # Syntax::Keyword::Try
12 | ['DORNER/UI-Various-0.39.tar.gz', 1], # Curses::UI and Tk are declared as recommends
13 | );
14 |
15 | done_testing;
16 |
--------------------------------------------------------------------------------
/t/kwalitee/proper_libs.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'proper_libs',
6 | # pm files not in the lib/base dir
7 | ['NIELSD/Speech-Google-0.5.tar.gz', 0], # 2907 (Google/TTS.pm)
8 |
9 | # didn't extract nicely (dot underscore files)
10 | ['AQUILINA/WWW-LaQuinta-Returns-0.02.tar.gz', 0], # 4055
11 |
12 | # multiple pm files in the basedir
13 | ['DGRAHAM/simpleXMLParse/simplexmlparse_v1.4.tar.gz', 0], # 4336
14 |
15 | # no modules
16 | ['LAWSONK/Gtk2-Ex-MPlayerEmbed-0.03.tar.gz', 1], # 465
17 | ['WOLDRICH/App-epic-0.014.tar.gz', 1], # 812
18 | ['TAG/AnyEvent-Peer39-0.32.tar.gz', 1], # 824
19 | ['CASIANO/Git-Export-0.04.tar.gz', 1], # 2593
20 | ['MILOVIDOV/APP-Yatranslate-0.02.tar.gz', 1], # 3773
21 | ['DBR/pdoc-0.900.tar.gz', 1], # 3876
22 | ['MUIR/modules/rinetd.pl-1.2.tar.gz', 1], # 4319
23 | );
24 |
25 | done_testing;
26 |
--------------------------------------------------------------------------------
/t/kwalitee/test_prereq_matches_use.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'test_prereq_matches_use',
6 | ['STEVAN/decorators-0.01.tar.gz', 0], # Test::Fatal
7 | ['MANOWAR/WWW-KeenIO-0.02.tar.gz', 0], # Test::Mouse
8 | ['JLMARTIN/CloudDeploy-1.05.tar.gz', 0], # File::Slurp etc
9 | ['YANICK/Bread-Board-0.36.tar.gz', 0], # Moo
10 | );
11 |
12 | done_testing;
13 |
--------------------------------------------------------------------------------
/t/kwalitee/use_strict.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'use_strict',
6 | ['TOBYINK/Platform-Windows-0.002.tar.gz', 0], # 2206
7 | ['TOBYINK/Platform-Unix-0.002.tar.gz', 0], # 2264
8 | ['SCILLEY/POE/Component/IRC/Plugin/IRCDHelp-0.02.tar.gz', 0], # 3243
9 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
10 | ['TXH/Template-Plugin-Filter-MinifyHTML-0.02.tar.gz', 0], # 3484
11 | ['ANANSI/Anansi-ObjectManager-0.06.tar.gz', 0], # 5246
12 | ['MARCEL/Web-Library-0.01.tar.gz', 0], # 7345
13 | ['PJB/Speech-Speakup-1.04.tar.gz', 0], # 7410
14 | ['TEMPIRE/Eponymous-Hash-0.01.tar.gz', 0], # 8503
15 | ['SULLR/Net-SSLGlue-1.03.tar.gz', 0], # 8720
16 |
17 | # use 5.012 and higher
18 | ['ZDM/Pharaoh-BootStrap-3.00.tar.gz', 1], # use 5.12.0
19 | ['MALLEN/Acme-Github-Test-0.03.tar.gz', 1], # use 5.014
20 |
21 | # no .pm files
22 | ['RCLAMP/cvn-0.02.tar.gz', 1],
23 |
24 | # .pod without package declaration
25 | ['ETHER/Moose-2.1209.tar.gz', 1],
26 |
27 | # v6 module inside a Perl 5 distribution
28 | ['NINE/Inline-Perl6-0.07.tar.gz', 1],
29 | );
30 |
31 | done_testing;
32 |
--------------------------------------------------------------------------------
/t/kwalitee/use_warnings.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 |
4 | test_kwalitee(
5 | 'use_warnings',
6 | ['TOBYINK/Platform-Windows-0.002.tar.gz', 0], # 2206
7 | ['TOBYINK/Platform-Unix-0.002.tar.gz', 0], # 2264
8 | ['BOOK/Acme-MetaSyntactic-errno-1.003.tar.gz', 0], # 2889
9 | ['ANANSI/Anansi-Library-0.02.tar.gz', 0], # 3365
10 | ['TXH/Template-Plugin-Filter-MinifyHTML-0.02.tar.gz', 0], # 3484
11 | ['LTP/Game-Life-0.05.tar.gz', 0], # 6535
12 | ['PJB/Speech-Speakup-1.04.tar.gz', 0], # 7410
13 | ['JBAZIK/Archive-Ar-1.15.tar.gz', 0], # 7983
14 | ['SULLR/Net-SSLGlue-1.03.tar.gz', 0], # 8720
15 | ['SHARYANTO/Term-ProgressBar-Color-0.00.tar.gz', 0], # 9746
16 |
17 | # no .pm files
18 | ['RCLAMP/cvn-0.02.tar.gz', 1],
19 |
20 | # .pod without package declaration
21 | ['ETHER/Moose-2.1209.tar.gz', 1],
22 |
23 | # v6 module inside a Perl 5 distribution
24 | ['NINE/Inline-Perl6-0.07.tar.gz', 1],
25 |
26 | # v5.36.0
27 | ['GUGOD/PerlX-ScopeFunction-0.03.tar.gz', 1],
28 | );
29 |
30 | done_testing;
31 |
--------------------------------------------------------------------------------
/t/kwalitee/valid_signature.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use Test::RequiresInternet ('pool.sks-keyservers.net' => 80);
4 |
5 | test_kwalitee(
6 | 'valid_signature',
7 | ['HUGUEI/Net-Stomp-Receipt-0.36.tar.gz', 0], # 3686
8 | ['JJORE/perl-lint-mode-0.02.tar.gz', 0], # 4391
9 | ['SIMON/Lingua-EN-Keywords-2.0.tar.gz', 0], # 4639
10 | ['RPAGITSCH/Win32-Process-User-0.02.tar.gz', 0], # 5063
11 | ['HUGUEI/Finance-Currency-Convert-BChile-0.04.tar.gz', 0], # 5108
12 | [
13 | 'RKOBES/File-HomeDir-Win32-0.04.tar.gz',
14 | 0,
15 | sub {
16 | my $stash = shift;
17 | ok $stash->{error}{valid_signature};
18 | }
19 | ],
20 | [
21 | 'RIVY/Win32-CommandLine-0.938.tar.gz',
22 | 1,
23 | sub {
24 | my $stash = shift;
25 | like $stash->{error}{valid_signature} => qr/Old SIGNATURE detected/;
26 | }
27 | ],
28 |
29 | # No public key: May take a long time sometimes
30 | ['CRAIHA/Geo-Coordinates-Parser-0.01.tar.gz', 0], # 4009
31 | ['DMAKI/Class-Validating-0.02.tar.gz', 0], # 4624
32 | ['PELAGIC/List-Rotation-Cycle-1.009.tar.gz', 0], # 4648
33 | ['JMEHNLE/net-address-ipv4-local/Net-Address-IPv4-Local-0.12.tar.gz', 0], # 4848
34 | );
35 |
36 | done_testing;
37 |
--------------------------------------------------------------------------------
/t/root.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use Test::More;
4 | use WWW::CPANTS;
5 | use Path::Tiny;
6 |
7 | my $root = path("$FindBin::Bin/..")->realpath;
8 |
9 | my $api_app_root = WWW::CPANTS->instance->app_root;
10 | is $api_app_root => $root, "api app root";
11 |
12 | done_testing;
13 |
--------------------------------------------------------------------------------
/t/web/author.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | 'ISHIGAKI/Path-Extended-0.19.tar.gz',
11 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | $testpan->cpan->update_indices;
15 |
16 | load_task('UpdateCPANIndices')->run;
17 | load_task('Traverse')->run(qw/ISHIGAKI/);
18 | load_task('Analyze')->run(@files);
19 | load_task('PostProcess')->run;
20 | };
21 |
22 | subtest 'get' => sub {
23 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
24 | $t->get_ok('/author/ishigaki')->status_is(200);
25 | };
26 |
27 | subtest 'get json' => sub {
28 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
29 | $t->get_ok('/author/ishigaki.json')->status_is(200);
30 | };
31 |
32 | subtest 'get png' => sub {
33 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
34 | $t->get_ok('/author/ishigaki.png')->status_is(200);
35 | };
36 |
37 | subtest 'get svg' => sub {
38 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
39 | $t->get_ok('/author/ishigaki.svg')->status_is(200);
40 | };
41 |
42 | subtest 'feed' => sub {
43 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
44 | $t->get_ok('/author/ishigaki/feed')->status_is(200);
45 | };
46 |
47 | done_testing;
48 |
--------------------------------------------------------------------------------
/t/web/author/ignore_backpan_latest.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | 'ISHIGAKI/JSON-4.00.tar.gz',
11 | 'ISHIGAKI/Parse-PMFile-0.42.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | $testpan->cpan->update_indices;
15 |
16 | $testpan->backpan->add_files(
17 | 'ISHIGAKI/Pod-Perldocs-0.17.tar.gz', ## backpan latest with kwalitee issues
18 | );
19 |
20 | load_task('UpdateCPANIndices')->run;
21 | load_task('Traverse')->run(qw/ISHIGAKI/);
22 | load_task('Analyze')->run(@files);
23 | load_task('PostProcess')->run;
24 | };
25 |
26 | subtest 'get json' => sub {
27 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
28 | $t->get_ok('/author/ishigaki.json')->status_is(200);
29 | my $json = $t->tx->res->json;
30 | is $json->{author}{average_core_kwalitee} => 100, "Pod::Perldocs is ignored";
31 | };
32 |
33 | done_testing;
34 |
--------------------------------------------------------------------------------
/t/web/dist/pod_escapes.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'RRA/podlators-4.14.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices')->run;
15 | load_task('Traverse')->run(qw/RRA/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess')->run;
18 | };
19 |
20 | my @paths = qw(
21 | /dist/podlators
22 | /release/RRA/podlators-4.14
23 | );
24 |
25 | subtest 'no E<>' => sub {
26 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
27 |
28 | for my $path (@paths) {
29 | $t->get_ok($path)->status_is(200);
30 | $t->text_is('h2' => 'podlators 4.14');
31 | my $tbody = $t->tx->res->dom->at('#modules tbody');
32 | for my $tr ($tbody->find('tr')->each) {
33 | for my $td ($tr->find('td')->each) {
34 | unlike $td->text => qr/E<[^>]+>/, $td->text;
35 | }
36 | }
37 | }
38 | };
39 |
40 | done_testing;
41 |
--------------------------------------------------------------------------------
/t/web/home.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess')->run;
18 | };
19 |
20 | subtest 'get' => sub {
21 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
22 | $t->get_ok('/')->status_is(200);
23 | };
24 |
25 | done_testing;
26 |
--------------------------------------------------------------------------------
/t/web/kwalitee.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess')->run;
18 | };
19 |
20 | subtest 'get' => sub {
21 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
22 | $t->get_ok('/kwalitee')->status_is(200);
23 | };
24 |
25 | subtest 'get indicator' => sub {
26 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
27 | $t->get_ok('/kwalitee/use_strict')->status_is(200);
28 | };
29 |
30 | done_testing;
31 |
--------------------------------------------------------------------------------
/t/web/ranking.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess')->run;
18 | };
19 |
20 | subtest 'get' => sub {
21 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
22 | $t->get_ok('/ranking')->status_is(200);
23 | };
24 |
25 | subtest 'get five_or_more' => sub {
26 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
27 | $t->get_ok('/ranking/five_or_more')->status_is(200);
28 | };
29 |
30 | subtest 'get less_than_five' => sub {
31 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
32 | $t->get_ok('/ranking/less_than_five')->status_is(200);
33 | };
34 |
35 | done_testing;
36 |
--------------------------------------------------------------------------------
/t/web/recent.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use WWW::CPANTS::Util::Datetime;
5 | use Test::More;
6 | use Test::Mojo;
7 | use Test::MockTime::HiRes;
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/JSON-4.02.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | load_task('Traverse')->run(qw/ISHIGAKI/);
15 | load_task('Analyze')->run(@files);
16 | };
17 |
18 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
19 |
20 | subtest 'get' => sub {
21 | my $epoch = epoch_from_date('2019-12-31');
22 | mock_time {
23 | $t->get_ok('/recent')->status_is(200);
24 | }
25 | $epoch;
26 | };
27 |
28 | done_testing;
29 |
--------------------------------------------------------------------------------
/t/web/search.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | fixture {
8 | my @files = (
9 | 'ISHIGAKI/JSON-4.02.tar.gz',
10 | );
11 | my $testpan = setup_testpan(@files);
12 | $testpan->cpan->update_indices;
13 |
14 | load_task('UpdateCPANIndices::Whois')->run;
15 | load_task('Traverse')->run(qw/ISHIGAKI/);
16 | load_task('Analyze')->run(@files);
17 | load_task('PostProcess')->run;
18 | };
19 |
20 | subtest 'get' => sub {
21 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
22 | $t->get_ok('/search')->status_is(200);
23 | };
24 |
25 | done_testing;
26 |
--------------------------------------------------------------------------------
/t/web/stats.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::Test::Fixture;
4 | use Test::More;
5 | use Test::Mojo;
6 |
7 | plan skip_all => "not implemented yet";
8 |
9 | fixture {
10 | my @files = (
11 | 'ISHIGAKI/JSON-4.02.tar.gz',
12 | );
13 | my $testpan = setup_testpan(@files);
14 | $testpan->cpan->update_indices;
15 |
16 | load_task('UpdateCPANIndices::Whois')->run;
17 | load_task('Traverse')->run(qw/ISHIGAKI/);
18 | load_task('Analyze')->run(@files);
19 | load_task('PostProcess')->run;
20 | };
21 |
22 | subtest 'get' => sub {
23 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
24 | $t->get_ok('/stats')->status_is(200);
25 | };
26 |
27 | subtest 'get module_install' => sub {
28 | my $t = Test::Mojo->new('WWW::CPANTS::Web');
29 | $t->get_ok('/stats/module_install')->status_is(200);
30 | };
31 |
32 | done_testing;
33 |
--------------------------------------------------------------------------------
/templates/_notice.html.ep:
--------------------------------------------------------------------------------
1 | % if (stash('cpants.notice_maintenance')) {
2 |
3 |
4 |
Under maintenance.
5 |
Most probably CPANTS databases are being regenerated from scratch due to major changes in Kwalitee metrics or updates of relevant modules/perl. Usually this maintenance takes about a day or two, and some of the information may be old or missing tentatively. Sorry for the inconvenience.
6 |
7 |
8 | % } elsif (stash('cpants.notice_analyzing')) {
9 |
10 |
11 |
Analyzing.
12 |
Scheduled analysis is going on. Some of the information may be inconsistent. Sorry for the inconvenience.
13 |
14 |
15 | % }
16 |
--------------------------------------------------------------------------------
/templates/dist/_tab.html.ep:
--------------------------------------------------------------------------------
1 | % my @tabs = (['' => 'Overview'], [prereq => 'Prerequisites'], [used_by => 'Users'], [errors => 'Errors'], [releases => 'Releases'], [files => 'Files'], [metadata => 'Metadata']);
2 |
3 | % my $stash = stash('cpants');
4 | % my $data = $stash->{data};
5 | % my $dist = $stash->{distribution} // $data->{distribution} // {};
6 | % my $tab = stash('tab') // '';
7 | % my $controller = stash('controller') // 'dist';
8 |
9 | <%= $dist->{name} %> <%= $dist->{version} %>
10 |
11 | % if (!$dist->{cpan}) {
12 | Deleted
13 | % } elsif ($dist->{latest_stable_uid} and $dist->{latest_stable_uid} eq $dist->{uid}) {
14 | Latest
15 | % } elsif ($dist->{latest_dev_uid} and $dist->{latest_dev_uid} eq $dist->{uid}) {
16 | Latest Dev
17 | % }
18 |
19 |
20 | % # latest? avaliability? score? badge? feeds?
21 |
22 |
27 |
--------------------------------------------------------------------------------
/templates/dist/errors.html.ep:
--------------------------------------------------------------------------------
1 | % my $stash = stash('cpants');
2 | % my $data = $stash->{data};
3 | % my $distribution = $stash->{distribution};
4 | % my $errors = $data->{errors} // [];
5 |
6 | % layout 'layout';
7 |
8 | %= include 'dist/_sidebar';
9 |
10 |
11 |
12 | %= include 'dist/_tab';
13 |
14 |
15 |
16 |
Errors
17 |
18 | % if (!@$errors) {
19 |
No error details.
20 | % } else {
21 |
22 | % for my $error (@$errors) {
23 | - <%= $error->{category} %>
24 | -
25 | % my $reftype = ref $error->{error};
26 | % if (!$reftype) {
27 | % if ($error->{error} =~ /\n/s) {
28 |
<%= $error->{error} %>
29 | % } else {
30 | <%= $error->{error} %>
31 | % }
32 | % } elsif ($reftype eq 'ARRAY') {
33 |
34 | % for (@{$error->{error}}) {
35 | % if (/\n/s) {
36 | <%= $_ %>
37 | % } else {
38 | - <%= $_ %>
39 | % }
40 | % }
41 |
42 | % } elsif ($reftype eq 'HASH') {
43 |
52 | % }
53 |
54 | % }
55 |
56 | % }
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/templates/dist/files.html.ep:
--------------------------------------------------------------------------------
1 | % my $stash = stash('cpants');
2 | % my $files = $stash->{data}{files};
3 | % my $distribution = $stash->{distribution};
4 | % layout 'layout';
5 |
6 | %= include 'dist/_sidebar';
7 |
8 |
9 |
10 | %= include 'dist/_tab';
11 |
12 |
13 |
14 |
Files
15 |
16 | % for my $file (sort keys %$files) {
17 | -
18 | <%= $file %>
19 | % if ($files->{$file}{unreadable}) {
20 | (unreadable)
21 | % }
22 | % if ($files->{$file}{symlink}) {
23 | (symlink)
24 | % }
25 | % if ($files->{$file}{maniskip}) {
26 | (manifest skip)
27 | % }
28 |
29 | % }
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/templates/dist/metadata.html.ep:
--------------------------------------------------------------------------------
1 | % my $stash = stash('cpants');
2 | % my $data = $stash->{data};
3 | % my $distribution = $stash->{distribution};
4 | % layout 'layout', 'cpants.requires_jsonview' => 1;
5 |
6 | %= include 'dist/_sidebar';
7 |
8 |
9 |
10 | %= include 'dist/_tab';
11 |
12 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/templates/exception.html.ep:
--------------------------------------------------------------------------------
1 | % layout 'layout';
2 | Server Error
3 |
--------------------------------------------------------------------------------
/templates/not_found.html.ep:
--------------------------------------------------------------------------------
1 | % layout 'layout';
2 | Not found
3 |
--------------------------------------------------------------------------------
/templates/ranking/_sidebar.html.ep:
--------------------------------------------------------------------------------
1 | % my $stash = stash('cpants');
2 | % my $data = $stash->{data};
3 |
4 |
12 |
--------------------------------------------------------------------------------
/templates/stats.html.ep:
--------------------------------------------------------------------------------
1 | % layout 'layout';
2 |
3 |
4 |
5 |
6 |
7 |
CPANTS Statistics
8 |
9 |
10 |
11 |
12 |
13 | %= include 'stats/_sidebar';
14 |
--------------------------------------------------------------------------------
/templates/stats/_sidebar.html.ep:
--------------------------------------------------------------------------------
1 | % my $stash = stash('cpants');
2 | % my $data = $stash->{data};
3 |
4 |
12 |
--------------------------------------------------------------------------------
/web.psgi:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use FindBin;
3 | use lib "$FindBin::Bin/lib";
4 | use lib glob "extlib/*/lib";
5 | use WWW::CPANTS::Web;
6 | use Plack::Builder;
7 |
8 | my $web = WWW::CPANTS::Web->new;
9 |
10 | builder {
11 | enable 'ReverseProxy';
12 | $web->start('psgi');
13 | };
14 |
--------------------------------------------------------------------------------
/xt/api_model.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use Test::More;
3 | use WWW::CPANTS::Util::Loader;
4 | use WWW::CPANTS::Util::Path;
5 | use String::CamelCase qw/decamelize/;
6 |
7 | my @names = submodule_names("WWW::CPANTS::API::Model");
8 | my $t_dir = cpants_app_path("t/api_model");
9 | my %seen;
10 |
11 | subtest 'all the tests exists' => sub {
12 | for my $name (@names) {
13 | next unless $name =~ /::/;
14 | next if $name =~ /V5::Release::/;
15 | my $path = $name =~ s|::|/|gr;
16 | $path = decamelize($path) . ".t";
17 | $seen{$path} = 1;
18 | my $file = $t_dir->child($path);
19 | ok -f $file, "test for $name exists" or next;
20 | my $body = $file->slurp;
21 | ok $body =~ /api_model\('$name'\);/, "contains $name";
22 | }
23 | };
24 |
25 | subtest 'all the tests for removed api are removed' => sub {
26 | my $iter = $t_dir->iterator({ recurse => 1 });
27 | while (my $file = $iter->()) {
28 | next if -d $file;
29 | my $path = $file->relative($t_dir);
30 | ok exists $seen{$path}, "api for $path exists";
31 | }
32 | };
33 |
34 | done_testing;
35 |
--------------------------------------------------------------------------------
/xt/cpanfile.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict;
2 | use FindBin;
3 | use Test::More;
4 | use Test::CPANfile 0.07;
5 |
6 | my @extlibs = glob "$FindBin::Bin/../extlib/*/lib";
7 | require lib; lib->import(@extlibs);
8 |
9 | cpanfile_has_all_used_modules(
10 | parsers => [':bundled'],
11 | scan_also => \@extlibs,
12 | use_index => 'Mirror',
13 | recommends => 1,
14 | suggests => 1,
15 | develop => 1,
16 | exclude_core => 1,
17 | perl_version => '5.028',
18 | private_re => qr/Module::CPANTS::(Site)?Kwalitee/,
19 | features => {
20 | extlib => {
21 | paths => \@extlibs,
22 | },
23 | },
24 | );
25 |
26 | done_testing;
27 |
--------------------------------------------------------------------------------
/xt/kwalitee.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use Test::More;
3 | use WWW::CPANTS::Model::Kwalitee;
4 | use WWW::CPANTS::Util::Path;
5 |
6 | my $model = WWW::CPANTS::Model::Kwalitee->new;
7 | my $t_dir = cpants_app_path("t/kwalitee");
8 | my %seen;
9 |
10 | subtest 'all tests exist' => sub {
11 | for my $name (sort $model->names->@*) {
12 | my $path = "$name.t";
13 | $seen{$path}++;
14 | my $file = $t_dir->child($path);
15 | ok -f $file, "$path exists";
16 | }
17 | };
18 |
19 | subtest 'all removed tests are removed' => sub {
20 | for my $name (sort map { $_->basename } $t_dir->children) {
21 | ok exists $seen{$name}, "$name exists";
22 | }
23 | };
24 |
25 | done_testing;
26 |
--------------------------------------------------------------------------------
/xt/perlcritic.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use Test::More;
3 | use Test::Perl::Critic;
4 |
5 | all_critic_ok(qw/author bin lib t xt/, glob "*.psgi");
6 |
--------------------------------------------------------------------------------
/xt/util/validate.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Test;
3 | use WWW::CPANTS::API::Util::Validate;
4 | use WWW::CPANTS::Model::CPAN::Packages;
5 | use Test::More;
6 | use Parse::Distname qw/parse_distname/;
7 |
8 | my %fails;
9 | my $packages = WWW::CPANTS::Model::CPAN::Packages->new(root => WWW::CPANTS->instance->root);
10 | for my $package ($packages->list->@*) {
11 | my $path = $package->{path};
12 | my $info = parse_distname($path);
13 | my ($name, $version) = @$info{qw/name version/};
14 | ok defined(is_path($path)), "$path is path" or $fails{$path}{$path} = 1;
15 | ok defined(is_alphanum($name)), "$path name ($name) is alphanum" or $fails{$name}{$path} = 1;
16 | if (defined $version) {
17 | ok defined(is_alphanum($version)), "$path version ($version) is alphanum" or $fails{$version}{$path} = 1;
18 | }
19 | }
20 | note explain [map { $_ => [sort keys $fails{$_}->%*] } sort keys %fails];
21 |
22 | done_testing;
23 |
--------------------------------------------------------------------------------
/xt/web.t:
--------------------------------------------------------------------------------
1 | use Mojo::Base -strict, -signatures;
2 | use WWW::CPANTS::Util::Loader;
3 | use WWW::CPANTS::Util::Path;
4 | use Test::More;
5 | use String::CamelCase qw/decamelize/;
6 |
7 | my @names = submodule_names("WWW::CPANTS::Web::Controller");
8 | my $t_dir = cpants_app_path("t/web");
9 | my %seen;
10 |
11 | subtest 'all the tests exists' => sub {
12 | for my $name (@names) {
13 | next if $name =~ /Root|Release/;
14 | my $path = $name =~ s|::|/|gr;
15 | $path = decamelize($path) . ".t";
16 | $seen{$path} = 1;
17 | my $file = $t_dir->child($path);
18 | ok -f $file, "test for $name exists" or next;
19 | my $body = $file->slurp;
20 | ok $body =~ /Test::Mojo->new\('WWW::CPANTS::Web'\);/, "creates a tester for $name";
21 | }
22 | };
23 |
24 | subtest 'all the tests for removed controller are removed' => sub {
25 | my $iter = $t_dir->iterator({ recurse => 1 });
26 | while (my $file = $iter->()) {
27 | next if -d $file;
28 | my $path = $file->relative($t_dir);
29 | my $parent_path = $file->parent->relative($t_dir) . ".t";
30 | ok exists $seen{$path} || exists $seen{$parent_path}, "test for $path exists";
31 | }
32 | };
33 |
34 | done_testing;
35 |
--------------------------------------------------------------------------------