Tools for modeling links between files / URLs / etc.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

297 lines
5.3 KiB

5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
  1. =pod
  2. =head1 NAME
  3. App::Pieces - some utility functions for bookmarking and interlinking things
  4. =head1 SYNOPSIS
  5. tk tk tk
  6. =head1 INSTALLING
  7. $ perl Build.PL
  8. $ ./Build
  9. $ ./Build install
  10. =head1 DESCRIPTION
  11. tk tk tk
  12. =cut
  13. package App::Pieces;
  14. use strict;
  15. use warnings;
  16. use 5.10.0;
  17. use base qw(Exporter);
  18. our @EXPORT_OK = qw();
  19. our ($VERSION) = '0.0.1';
  20. use Carp;
  21. use DBI;
  22. use File::Basename;
  23. use File::HomeDir;
  24. use File::Spec;
  25. use URI;
  26. my %default = (
  27. dbfile => File::Spec->catfile(File::HomeDir->my_home, 'pieces.db')
  28. );
  29. =head1 SUBROUTINES
  30. =over
  31. =item new(%params)
  32. Get a new Pieces object with the specified parameters set.
  33. =cut
  34. sub new {
  35. my $class = shift;
  36. my %params = @_;
  37. my %copy_of_default = %default;
  38. my $self = \%copy_of_default;
  39. bless $self, $class;
  40. # Configure from passed-in values, overwriting defaults:
  41. for my $p (keys %params) {
  42. $self->{$p} = $params{$p};
  43. }
  44. $self->{dbh} = $self->get_dbh();
  45. return $self;
  46. }
  47. =item get_dbh()
  48. Get database handle for default pieces database, stored in F<~/pieces.db>.
  49. Creates a new database with the correct schema if one doesn't already exist.
  50. =cut
  51. sub get_dbh {
  52. my ($self) = shift;
  53. my $dbfile = $self->{dbfile};
  54. my $init_new = 0;
  55. $init_new = 1 unless -f $dbfile;
  56. my $dbh = DBI->connect("dbi:SQLite:dbname=$dbfile", "", "");
  57. $self->create_pieces_db($dbh) if $init_new;
  58. return $dbh;
  59. }
  60. =item create_pieces_db($dbh)
  61. Create new pieces tables.
  62. =cut
  63. sub create_pieces_db {
  64. my ($self) = shift;
  65. my ($dbh) = @_;
  66. # We'll stash what version of pieces created the schema, and when:
  67. $dbh->do(qq{
  68. CREATE TABLE schema_version (
  69. id integer primary key,
  70. version text DEFAULT '$VERSION',
  71. created text DEFAULT CURRENT_TIMESTAMP
  72. );
  73. });
  74. $dbh->do(q{ INSERT INTO schema_version DEFAULT VALUES; });
  75. $dbh->do(q{
  76. CREATE TABLE pieces (
  77. id integer primary key,
  78. address text,
  79. created text,
  80. modified text
  81. );
  82. });
  83. $dbh->do(q{
  84. CREATE TABLE links (
  85. id integer primary key,
  86. from_piece integer,
  87. to_piece integer,
  88. description text,
  89. created text
  90. );
  91. });
  92. $dbh->do(q{
  93. CREATE TABLE groups (
  94. id integer primary key,
  95. name text,
  96. description text,
  97. created text,
  98. modifed text
  99. );
  100. });
  101. }
  102. =item add(@addresses)
  103. Add one or more addresses to the pieces db.
  104. =cut
  105. sub add {
  106. my ($self) = shift;
  107. my (@addresses) = @_;
  108. # Filter out any paths that have already been marked:
  109. # XXX: This is gonna get pretty silly with thousands of entries to read every
  110. # time.
  111. my %addressmap = map { $_ => 1 } @addresses;
  112. $self->each_address(sub {
  113. my ($existing_addy) = @_;
  114. if ($addressmap{ $existing_addy }) {
  115. delete $addressmap{ $existing_addy };
  116. }
  117. });
  118. my $sth = $self->{dbh}->prepare(q{
  119. INSERT INTO pieces (address, created) VALUES (?, datetime('now'))
  120. });
  121. my @ids;
  122. foreach my $addy (keys %addressmap) {
  123. $sth->execute($addy);
  124. # The empty strings passed to last_insert_id() should just leave it
  125. # getting the last insert for the dbh:
  126. push @ids, $self->{dbh}->last_insert_id('', '', '', '');
  127. }
  128. return @ids;
  129. }
  130. sub get {
  131. my ($self) = shift;
  132. my ($addy) = @_;
  133. my $find_sth = $self->{dbh}->prepare(q{
  134. SELECT id FROM pieces WHERE address = ?;
  135. });
  136. my ($id) = $self->{dbh}->selectrow_array($find_sth, {}, $addy);
  137. return $id;
  138. }
  139. sub get_or_add {
  140. my ($self) = shift;
  141. my ($addy) = @_;
  142. my $id = $self->get($addy);
  143. # If we didn't find an existing piece, add it and get its id
  144. if (! defined $id) {
  145. ($id) = $self->add($addy);
  146. }
  147. return $id;
  148. }
  149. sub add_link {
  150. my ($self) = shift;
  151. my ($from, $to) = @_;
  152. my $from_id = $self->get_or_add($from);
  153. my $to_id = $self->get_or_add($to);
  154. my $sth = $self->{dbh}->prepare(q{
  155. INSERT INTO links (from_piece, to_piece, created) VALUES (?, ?, datetime('now'))
  156. });
  157. $sth->execute($from_id, $to_id);
  158. }
  159. =item remove(@paths)
  160. Remove all given paths from the mark list.
  161. =cut
  162. sub remove {
  163. my ($self) = shift;
  164. my (@addresses) = @_;
  165. my $sth = $self->{dbh}->prepare(q{
  166. DELETE FROM pieces WHERE address = ?;
  167. });
  168. foreach my $addy (@addresses) {
  169. $sth->execute($addy);
  170. }
  171. }
  172. =item each_address($func)
  173. Run an anonymous function against each item in the pieces list.
  174. Expects a sub which takes a path string.
  175. =cut
  176. sub each_address {
  177. my ($self) = shift;
  178. my ($func) = @_;
  179. my $sth = $self->{dbh}->prepare(q{
  180. SELECT DISTINCT(address) as address FROM pieces ORDER BY created;
  181. });
  182. $sth->execute();
  183. while (my $data = $sth->fetchrow_hashref()) {
  184. $func->($data->{address});
  185. }
  186. }
  187. sub foreach_row {
  188. my ($self) = shift;
  189. my ($query, $func) = @_;
  190. my $sth = $self->{dbh}->prepare($query);
  191. $sth->execute();
  192. while (my $data = $sth->fetchrow_hashref()) {
  193. $func->($data);
  194. }
  195. }
  196. =back
  197. =head1 AUTHOR
  198. Copyright 2018 Brennen Bearnes
  199. pieces is free software; you can redistribute it and/or modify
  200. it under the terms of the GNU General Public License as published by
  201. the Free Software Foundation; either version 2 of the License, or
  202. (at your option) any later version.
  203. This program is distributed in the hope that it will be useful,
  204. but WITHOUT ANY WARRANTY; without even the implied warranty of
  205. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  206. GNU General Public License for more details.
  207. =cut
  208. 1;