AtomBus-1.0405 000755 001750 001750 0 11743324625 13053 5 ustar 00naveed naveed 000000 000000 README 100644 001750 001750 15330 11743324625 14036 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 NAME
AtomBus - An AtomPub server for messaging.
VERSION
version 1.0405
SYNOPSIS
use Dancer;
use AtomBus;
dance;
DESCRIPTION
AtomBus is an AtomPub server that can be used for messaging. The idea is
that atom feeds can correspond to conceptual queues or buses. AtomBus is
built on top of the Dancer framework. It is also pubsubhubbub friendly.
These examples assume that you have configured your web server to point
HTTP requests starting with /atombus to your AtomBus server (see
"DEPLOYMENT"). To publish an entry, make a HTTP POST request:
$ curl -d ' allo
an important message
' http://localhost/atombus/feeds/widgets
That adds a new entry to a feed titled widgets. If that feed didn't
exist before, it will be created for you. To retrieve the widgets feed,
make a HTTP GET request:
$ curl http://localhost/atombus/feeds/widgets
Clients can request only entries that came after the last entry they
processed. They can do this by providing the id of the last message as
the start_after parameter:
$ curl http://localhost/atombus/feeds/widgets?start_after=42
Alternatively, you can provide a start_at param. This will retrieve
entries starting with the given id:
$ curl http://localhost/atombus/feeds/widgets?start_at=42
HTTP ETags are also supported. The server responds with an ETag header
for each request. The client can provide that ETag as the If-None-Match
header. The following example will work the same as if the client
provided a start_after parameter. Except that it will return an empty
body and a 304 status if there are no new entries. This is the behavior
that pubsubhubbub recommends
.
$ curl -H 'If-None-Match: "42"' http://localhost/atombus/feeds/widgets
Note that the most messages you will get per request is determined by
the page_size setting. If you do not specify a page_size setting, it
defaults to 1000. This default may change in the future, so don't count
on it.
AtomBus is mostly a proper implementation of the AtomPub protocol and
will validate 100% against . One point
where it diverges from the AtomPub spec is that feed entries are
returned in fifo order. This is because a message consumer will most
likely want to consume messages in the order that they were published.
In the future, a config setting may be available to reverse the order.
CONFIGURATION
Configuration can be achieved via a config.yml file or via the set
keyword. To use the config.yml approach, you will need to install YAML.
See the Dancer documentation for more information. The only required
config setting is the dsn.
Example config.yml:
# Dancer specific config settings
logger: file
log: errors
atombus:
page_size: 100
db:
dsn: dbi:mysql:database=atombus
user: joe
pass: momma
You can alternatively configure the server via the 'set' keyword in the
source code. This approach does not require a config file.
use Dancer;
use AtomBus;
# Dancer specific config settings
set logger => 'file';
set log => 'debug';
set show_errors => 1;
set atombus => {
page_size => 100,
db => {
dsn => 'dbi:SQLite:dbname=/var/local/atombus/atombus.db',
}
};
dance;
DATABASE
AtomBus is backed by a database. The dsn in the config must point to a
database which you have write privileges to. The tables will be created
automagically for you if they don't already exist. Of course that
requires create table privileges. All databases supported by DBIx::Class
are supported, which are most major databases including postgresql,
sqlite, mysql and oracle.
DEPLOYMENT
Deployment is very flexible. It can be run on a web server via CGI or
FastCGI. It can also be run on any Plack web server. See
Dancer::Deployment for more details.
FastCGI
AtomBus can be run via FastCGI. This requires that you have the FCGI and
Plack modules installed. Here is an example FastCGI script. It assumes
your AtomBus server is in the file atombus.pl.
#!/usr/bin/env perl
use Dancer ':syntax';
use Plack::Handler::FCGI;
my $app = do "/path/to/atombus.pl";
my $server = Plack::Handler::FCGI->new(nproc => 5, detach => 1);
$server->run($app);
Here is an example lighttpd config. It assumes you named the above file
atombus.fcgi.
fastcgi.server += (
"/atombus" => ((
"socket" => "/tmp/fcgi.sock",
"check-local" => "disable",
"bin-path" => "/path/to/atombus.fcgi",
)),
)
Now AtomBus will be running via FastCGI under /atombus.
Plack
AtomBus can be run with any Plack web server. Just run:
plackup atombus.pl
You can change the Plack web server via the -s option to plackup.
MOTIVATION
I like messaging systems because they make it so easy to create scalable
applications. Existing message brokers are great for creating message
queues. But once a consumer reads a message off of a queue, it is not
available for other consumers. I needed a system to publish events such
that multiple heterogeneous services could subscribe to them. So I
really needed a message bus, not a message queue. I could for example
have used something called topics in ActiveMQ, but I have found ActiveMQ
to be broken in general. An instance I manage has to be restarted daily.
AtomBus on the other hand will be extremely stable, because it is so
simple. It is in essence just a simple interface to a database. As long
as your database and web server are up, AtomBus will be there for you.
And there are many ways to add redundancy to databases and web heads.
Another advantage of using AtomBus is that Atom is a well known
standard. Everyone already has a client for it, their browser. Aren't
standards great! By the way, if you just need message queues, try
POE::Component::MessageQueue. It rocks. If you need a message bus, give
AtomBus a shot.
AUTHOR
Naveed Massjouni
COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
Changes 100644 001750 001750 5462 11743324625 14436 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 Revision history for AtomBus
1.0405 2012-04-17
Fixed tests to set config options at compile time before AtomBus module
is loaded.
Removed unnecessary before hook. Initializing now happens when AtomBus
is loaded.
Fixed the structure of the config options in config.yml.
1.0404 2010-12-29
Making configuration more friendly. All AtomBus specific settings
should now be under the 'atombus' config setting.
1.0403 2010-12-29
Renaming AtomMQ to AtomBus.
1.0402 2010-12-29
The example config in the POD was incorrectly setting password instead
of pass.
1.0401 2010-12-28
Just some POD fixes.
1.0400 2010-12-28
Added support for ETags. Behaves as recommended by pubsubhubbub:
http://code.google.com/p/pubsubhubbub/wiki/PublisherEfficiency
No longer require user to set schema_class config value.
1.0301 2010-12-11
Fixed a bug that breaks app in FCGI environment if Capture::Tiny
is used inside the before filter to silence deploying the schema.
1.0300 2010-12-10
AtomMQ is now a Dancer app, hooray!
A bunch of junk went away now that we are using Dancer.
Added support for paging via the page_size setting.
1.0200 2010-12-03
Added support for max_msgs_per_request parameter and corresponding
tests and documentation.
1.0100 2010-11-25
Generted feed now validates against http://validator.w3.org/feed
Added atommq_feed table. Now there are 2 tables total.
Entry and feed id's are now urn's
1.0000 2010-11-20
One AtomMQ server can serve multiple feeds.
Posting to a feed that doesn't exist will automatically create the feed
for you.
The feed name is no longer a param of the constructor. It is passed
in through the uri, e.g., /atommq/feed=widgets
Added a FastCGI section to the docs.
Made the PSGI example simpler in the docs.
0.0301 2010-11-19
Set AutoCommit and RaiseError DBI options to true.
0.0300 2010-11-19
Replaced dsn/user/password constructor params with db_info.
Added exception handling test for missing db_info param.
Updated default schema to use TEXT columns instead of VARCHAR.
0.0200 2010-11-14
Added auto_create_db option.
Added example sql for creating db table.
Fixed PSGI example.
0.0102 2010-11-14
Added directions for running AtomMQ in a PSGI environment.
Catch exception if db table creation fails.
0.0101 2010-11-14
Added more documentation.
0.0100 2010-11-13
Now using dbic instead of dbi.
Auto creating of db table should work for all db's supported by dbic.
0.0001 2010-11-12
First version, released on an unsuspecting world.
LICENSE 100644 001750 001750 43667 11743324625 14201 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
Terms of the Perl programming language system itself
a) the GNU General Public License as published by the Free
Software Foundation; either version 1, or (at your option) any
later version, or
b) the "Artistic License"
--- The GNU General Public License, Version 1, February 1989 ---
This software is Copyright (c) 2010 by Naveed Massjouni.
This is free software, licensed under:
The GNU General Public License, Version 1, February 1989
GNU GENERAL PUBLIC LICENSE
Version 1, February 1989
Copyright (C) 1989 Free Software Foundation, Inc.
51 Franklin St, Suite 500, Boston, MA 02110-1335 USA
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The license agreements of most software companies try to keep users
at the mercy of those companies. By contrast, our General Public
License is intended to guarantee your freedom to share and change free
software--to make sure the software is free for all its users. The
General Public License applies to the Free Software Foundation's
software and to any other program whose authors commit to using it.
You can use it for your programs, too.
When we speak of free software, we are referring to freedom, not
price. Specifically, the General Public License is designed to make
sure that you have the freedom to give away or sell copies of free
software, that you receive source code or can get it if you want it,
that you can change the software or use pieces of it in new free
programs; and that you know you can do these things.
To protect your rights, we need to make restrictions that forbid
anyone to deny you these rights or to ask you to surrender the rights.
These restrictions translate to certain responsibilities for you if you
distribute copies of the software, or if you modify it.
For example, if you distribute copies of a such a program, whether
gratis or for a fee, you must give the recipients all the rights that
you have. You must make sure that they, too, receive or can get the
source code. And you must tell them their rights.
We protect your rights with two steps: (1) copyright the software, and
(2) offer you this license which gives you legal permission to copy,
distribute and/or modify the software.
Also, for each author's protection and ours, we want to make certain
that everyone understands that there is no warranty for this free
software. If the software is modified by someone else and passed on, we
want its recipients to know that what they have is not the original, so
that any problems introduced by others will not reflect on the original
authors' reputations.
The precise terms and conditions for copying, distribution and
modification follow.
GNU GENERAL PUBLIC LICENSE
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
0. This License Agreement applies to any program or other work which
contains a notice placed by the copyright holder saying it may be
distributed under the terms of this General Public License. The
"Program", below, refers to any such program or work, and a "work based
on the Program" means either the Program or any work containing the
Program or a portion of it, either verbatim or with modifications. Each
licensee is addressed as "you".
1. You may copy and distribute verbatim copies of the Program's source
code as you receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice and
disclaimer of warranty; keep intact all the notices that refer to this
General Public License and to the absence of any warranty; and give any
other recipients of the Program a copy of this General Public License
along with the Program. You may charge a fee for the physical act of
transferring a copy.
2. You may modify your copy or copies of the Program or any portion of
it, and copy and distribute such modifications under the terms of Paragraph
1 above, provided that you also do the following:
a) cause the modified files to carry prominent notices stating that
you changed the files and the date of any change; and
b) cause the whole of any work that you distribute or publish, that
in whole or in part contains the Program or any part thereof, either
with or without modifications, to be licensed at no charge to all
third parties under the terms of this General Public License (except
that you may choose to grant warranty protection to some or all
third parties, at your option).
c) If the modified program normally reads commands interactively when
run, you must cause it, when started running for such interactive use
in the simplest and most usual way, to print or display an
announcement including an appropriate copyright notice and a notice
that there is no warranty (or else, saying that you provide a
warranty) and that users may redistribute the program under these
conditions, and telling the user how to view a copy of this General
Public License.
d) You may charge a fee for the physical act of transferring a
copy, and you may at your option offer warranty protection in
exchange for a fee.
Mere aggregation of another independent work with the Program (or its
derivative) on a volume of a storage or distribution medium does not bring
the other work under the scope of these terms.
3. You may copy and distribute the Program (or a portion or derivative of
it, under Paragraph 2) in object code or executable form under the terms of
Paragraphs 1 and 2 above provided that you also do one of the following:
a) accompany it with the complete corresponding machine-readable
source code, which must be distributed under the terms of
Paragraphs 1 and 2 above; or,
b) accompany it with a written offer, valid for at least three
years, to give any third party free (except for a nominal charge
for the cost of distribution) a complete machine-readable copy of the
corresponding source code, to be distributed under the terms of
Paragraphs 1 and 2 above; or,
c) accompany it with the information you received as to where the
corresponding source code may be obtained. (This alternative is
allowed only for noncommercial distribution and only if you
received the program in object code or executable form alone.)
Source code for a work means the preferred form of the work for making
modifications to it. For an executable file, complete source code means
all the source code for all modules it contains; but, as a special
exception, it need not include source code for modules which are standard
libraries that accompany the operating system on which the executable
file runs, or for standard header files or definitions files that
accompany that operating system.
4. You may not copy, modify, sublicense, distribute or transfer the
Program except as expressly provided under this General Public License.
Any attempt otherwise to copy, modify, sublicense, distribute or transfer
the Program is void, and will automatically terminate your rights to use
the Program under this License. However, parties who have received
copies, or rights to use copies, from you under this General Public
License will not have their licenses terminated so long as such parties
remain in full compliance.
5. By copying, distributing or modifying the Program (or any work based
on the Program) you indicate your acceptance of this license to do so,
and all its terms and conditions.
6. Each time you redistribute the Program (or any work based on the
Program), the recipient automatically receives a license from the original
licensor to copy, distribute or modify the Program subject to these
terms and conditions. You may not impose any further restrictions on the
recipients' exercise of the rights granted herein.
7. The Free Software Foundation may publish revised and/or new versions
of the General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the Program
specifies a version number of the license which applies to it and "any
later version", you have the option of following the terms and conditions
either of that version or of any later version published by the Free
Software Foundation. If the Program does not specify a version number of
the license, you may choose any version ever published by the Free Software
Foundation.
8. If you wish to incorporate parts of the Program into other free
programs whose distribution conditions are different, write to the author
to ask for permission. For software which is copyrighted by the Free
Software Foundation, write to the Free Software Foundation; we sometimes
make exceptions for this. Our decision will be guided by the two goals
of preserving the free status of all derivatives of our free software and
of promoting the sharing and reuse of software generally.
NO WARRANTY
9. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
REPAIR OR CORRECTION.
10. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
POSSIBILITY OF SUCH DAMAGES.
END OF TERMS AND CONDITIONS
Appendix: How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to humanity, the best way to achieve this is to make it
free software which everyone can redistribute and change under these
terms.
To do so, attach the following notices to the program. It is safest to
attach them to the start of each source file to most effectively convey
the exclusion of warranty; and each file should have at least the
"copyright" line and a pointer to where the full notice is found.
Copyright (C) 19yy
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 1, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) 19xx name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the
appropriate parts of the General Public License. Of course, the
commands you use may be called something other than `show w' and `show
c'; they could even be mouse-clicks or menu items--whatever suits your
program.
You should also get your employer (if you work as a programmer) or your
school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the
program `Gnomovision' (a program to direct compilers to make passes
at assemblers) written by James Hacker.
, 1 April 1989
Ty Coon, President of Vice
That's all there is to it!
--- The Artistic License 1.0 ---
This software is Copyright (c) 2010 by Naveed Massjouni.
This is free software, licensed under:
The Artistic License 1.0
The Artistic License
Preamble
The intent of this document is to state the conditions under which a Package
may be copied, such that the Copyright Holder maintains some semblance of
artistic control over the development of the package, while giving the users of
the package the right to use and distribute the Package in a more-or-less
customary fashion, plus the right to make reasonable modifications.
Definitions:
- "Package" refers to the collection of files distributed by the Copyright
Holder, and derivatives of that collection of files created through
textual modification.
- "Standard Version" refers to such a Package if it has not been modified,
or has been modified in accordance with the wishes of the Copyright
Holder.
- "Copyright Holder" is whoever is named in the copyright or copyrights for
the package.
- "You" is you, if you're thinking about copying or distributing this Package.
- "Reasonable copying fee" is whatever you can justify on the basis of media
cost, duplication charges, time of people involved, and so on. (You will
not be required to justify it to the Copyright Holder, but only to the
computing community at large as a market that must bear the fee.)
- "Freely Available" means that no fee is charged for the item itself, though
there may be fees involved in handling the item. It also means that
recipients of the item may redistribute it under the same conditions they
received it.
1. You may make and give away verbatim copies of the source form of the
Standard Version of this Package without restriction, provided that you
duplicate all of the original copyright notices and associated disclaimers.
2. You may apply bug fixes, portability fixes and other modifications derived
from the Public Domain or from the Copyright Holder. A Package modified in such
a way shall still be considered the Standard Version.
3. You may otherwise modify your copy of this Package in any way, provided that
you insert a prominent notice in each changed file stating how and when you
changed that file, and provided that you do at least ONE of the following:
a) place your modifications in the Public Domain or otherwise make them
Freely Available, such as by posting said modifications to Usenet or an
equivalent medium, or placing the modifications on a major archive site
such as ftp.uu.net, or by allowing the Copyright Holder to include your
modifications in the Standard Version of the Package.
b) use the modified Package only within your corporation or organization.
c) rename any non-standard executables so the names do not conflict with
standard executables, which must also be provided, and provide a separate
manual page for each non-standard executable that clearly documents how it
differs from the Standard Version.
d) make other distribution arrangements with the Copyright Holder.
4. You may distribute the programs of this Package in object code or executable
form, provided that you do at least ONE of the following:
a) distribute a Standard Version of the executables and library files,
together with instructions (in the manual page or equivalent) on where to
get the Standard Version.
b) accompany the distribution with the machine-readable source of the Package
with your modifications.
c) accompany any non-standard executables with their corresponding Standard
Version executables, giving the non-standard executables non-standard
names, and clearly documenting the differences in manual pages (or
equivalent), together with instructions on where to get the Standard
Version.
d) make other distribution arrangements with the Copyright Holder.
5. You may charge a reasonable copying fee for any distribution of this
Package. You may charge any fee you choose for support of this Package. You
may not charge a fee for this Package itself. However, you may distribute this
Package in aggregate with other (possibly commercial) programs as part of a
larger (possibly commercial) software distribution provided that you do not
advertise this Package as a product of your own.
6. The scripts and library files supplied as input to or produced as output
from the programs of this Package do not automatically fall under the copyright
of this Package, but belong to whomever generated them, and may be sold
commercially, and may be aggregated with this Package.
7. C or perl subroutines supplied by you and linked into this Package shall not
be considered part of this Package.
8. The name of the Copyright Holder may not be used to endorse or promote
products derived from this software without specific prior written permission.
9. THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
The End
dist.ini 100644 001750 001750 1421 11743324625 14576 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 name = AtomBus
author = Naveed Massjouni
license = Perl_5
copyright_holder = Naveed Massjouni
copyright_year = 2010
version = 1.0405
[Prereqs]
Atompub = 0
Capture::Tiny = 0
Dancer = 1.2002
Dancer::Plugin::DBIC = 0.1504
DBIx::Class = 0.08115
DBD::SQLite = 0
SQL::Translator = 0.11006
UUID::Tiny = 0.02
XML::Atom = 0
XML::XPath = 0
[MetaResources]
bugtracker.web = http://github.com/ironcamel/AtomBus/issues
repository.url = git://github.com/ironcamel/AtomBus.git
repository.web = http://github.com/ironcamel/AtomBus
repository.type = git
[@Filter]
-bundle = @Basic
-remove = Readme
[PruneFiles]
filenames = bin/app.pl
[OurPkgVersion]
[PodWeaver]
[ReadmeFromPod]
META.yml 100644 001750 001750 1334 11743324625 14406 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 ---
abstract: 'An AtomPub server for messaging.'
author:
- 'Naveed Massjouni '
build_requires: {}
configure_requires:
ExtUtils::MakeMaker: 6.30
dynamic_config: 0
generated_by: 'Dist::Zilla version 4.300002, CPAN::Meta::Converter version 2.112150'
license: perl
meta-spec:
url: http://module-build.sourceforge.net/META-spec-v1.4.html
version: 1.4
name: AtomBus
requires:
Atompub: 0
Capture::Tiny: 0
DBD::SQLite: 0
DBIx::Class: 0.08115
Dancer: 1.2002
Dancer::Plugin::DBIC: 0.1504
SQL::Translator: 0.11006
UUID::Tiny: 0.02
XML::Atom: 0
XML::XPath: 0
resources:
bugtracker: http://github.com/ironcamel/AtomBus/issues
repository: git://github.com/ironcamel/AtomBus.git
version: 1.0405
MANIFEST 100644 001750 001750 511 11743324625 14242 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 Changes
LICENSE
MANIFEST
META.yml
Makefile.PL
README
README.pod
config.yml
dist.ini
environments/development.yml
environments/production.yml
lib/AtomBus.pm
lib/AtomBus/Schema.pm
lib/AtomBus/Schema/Result/AtomBusEntry.pm
lib/AtomBus/Schema/Result/AtomBusFeed.pm
public/dispatch.cgi
public/dispatch.fcgi
t/01-basic.t
t/02-paging.t
config.yml 100644 001750 001750 230 11743324625 15077 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 appname: "AtomBus"
charset: "UTF-8"
atombus:
page_size: 100
db:
dsn: "dbi:Pg:dbname=atombus"
user: "joe"
pass: "momma"
README.pod 100644 001750 001750 117 11743324625 14554 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 =head1 INSTALLATION
sudo cpan AtomMQ
=head1 DOCUMENTATION
see L
Makefile.PL 100644 001750 001750 2324 11743324625 15107 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405
use strict;
use warnings;
use ExtUtils::MakeMaker 6.30;
my %WriteMakefileArgs = (
"ABSTRACT" => "An AtomPub server for messaging.",
"AUTHOR" => "Naveed Massjouni ",
"BUILD_REQUIRES" => {},
"CONFIGURE_REQUIRES" => {
"ExtUtils::MakeMaker" => "6.30"
},
"DISTNAME" => "AtomBus",
"EXE_FILES" => [],
"LICENSE" => "perl",
"NAME" => "AtomBus",
"PREREQ_PM" => {
"Atompub" => 0,
"Capture::Tiny" => 0,
"DBD::SQLite" => 0,
"DBIx::Class" => "0.08115",
"Dancer" => "1.2002",
"Dancer::Plugin::DBIC" => "0.1504",
"SQL::Translator" => "0.11006",
"UUID::Tiny" => "0.02",
"XML::Atom" => 0,
"XML::XPath" => 0
},
"VERSION" => "1.0405",
"test" => {
"TESTS" => "t/*.t"
}
);
unless ( eval { ExtUtils::MakeMaker->VERSION(6.56) } ) {
my $br = delete $WriteMakefileArgs{BUILD_REQUIRES};
my $pp = $WriteMakefileArgs{PREREQ_PM};
for my $mod ( keys %$br ) {
if ( exists $pp->{$mod} ) {
$pp->{$mod} = $br->{$mod} if $br->{$mod} > $pp->{$mod};
}
else {
$pp->{$mod} = $br->{$mod};
}
}
}
delete $WriteMakefileArgs{CONFIGURE_REQUIRES}
unless eval { ExtUtils::MakeMaker->VERSION(6.52) };
WriteMakefile(%WriteMakefileArgs);
t 000755 001750 001750 0 11743324625 13237 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 01-basic.t 100644 001750 001750 3534 11743324625 15070 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/t use Test::More import => ['!pass'], tests => 12;
use Dancer qw(:syntax);
use Dancer::Test;
use Dancer::Plugin::DBIC qw(schema);
use Capture::Tiny qw(capture);
BEGIN {
set atombus => {
db => {
dsn => 'dbi:SQLite:dbname=:memory:',
}
};
}
use AtomBus;
my $xml1 = q{
title111
content111
};
(my $xml2 = $xml1) =~ s/111/222/g;
my $feed = 'foo';
# Confirm that feed doesn't exist yet.
capture { # Silence output from schema->deploy in before filter.
response_status_is [ GET => "/feeds/$feed" ], 404;
};
my $res = dancer_response POST => "/feeds/$feed", { body => $xml1 };
is $res->{status} => 201, 'Got 201 for posting entry1.';
is schema->resultset('AtomBusEntry')->count() => 1, '1 entries in db.';
is schema->resultset('AtomBusFeed')->count() => 1, '1 feed in db.';
my ($entry1) = schema->resultset('AtomBusEntry')->search(
{ title => 'title111', content => 'content111', feed_title => $feed });
ok $entry1, 'Found entry 1.';
$res = dancer_response POST => "/feeds/$feed", { body => $xml2 };
is $res->{status} => 201, 'Got 201 for posting entry2.';
is schema->resultset('AtomBusEntry')->count() => 2, '2 entries in db.';
is schema->resultset('AtomBusFeed')->count() => 1, '1 feed in db.';
response_content_like [ GET => "/feeds/$feed" ], qr/content111/,
"Response has first message.";
my ($entry2) = schema->resultset('AtomBusEntry')->search(
{ title => 'title222', content => 'content222', feed_title => $feed });
ok $entry2, 'Found entry 2.';
ok $entry2->order_id > $entry1->order_id, 'The order_id field got incremented.';
response_content_like [ GET => "/feeds/$feed" ], qr/content111.+content222/s,
"Response has both messages in order.";
done_testing;
02-paging.t 100644 001750 001750 10301 11743324625 15263 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/t use Test::More import => ['!pass'], tests => 21;
use Dancer qw(:syntax);
use Dancer::Test;
use URI;
use XML::XPath;
use Dancer::Plugin::DBIC qw(schema);
use Capture::Tiny qw(capture);
BEGIN {
set atombus => {
db => {
dsn => 'dbi:SQLite:dbname=:memory:',
}
};
}
use AtomBus;
my $xml = q{
title%s
content%s
};
# Confirm that feed doesn't exist yet.
capture { # Silence output from schema->deploy in before filter.
response_status_is [ GET => "/feeds/foo" ], 404;
};
# Create one entry and examine result.
my $res = dancer_response POST => "/feeds/foo", { body => sprintf($xml, 1, 1) };
is $res->{status}=> 201, 'Status was 201';
my $etag = new URI($res->{headers}->{etag}, 'urn');
my $location = new URI($res->{headers}->{location}, 'http');
my $id_nss = $location;
($id_nss) =~ s,^.*/,,;
is $location->path => "/feeds/foo/entries/$id_nss", 'Location header is well-structured.';
is $etag->as_string => "urn:uuid:$id_nss", 'ETag was contained in location header.';
$res = dancer_response GET => $location->path;
is $res->{status}=> 200, 'Entry was GETtable with status 200';
# Create additional entries.
foreach my $i (2 .. 10) {
dancer_response POST => "/feeds/foo", { body => sprintf($xml, $i, $i) };
}
$res = dancer_response GET => "/feeds/foo";
my $xp = XML::XPath->new(xml => $res->{content});
my @entries = $xp->findnodes('/feed/entry');
is $res->{status}=> 200, 'Status was 200';
is @entries => 10, 'There are 10 entries';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 1 .. 10 ],
"All 10 entries are in order.";
my $id = $entries[4]->find('./id'); # this is the 5th entry
$res = dancer_response GET => "/feeds/foo", { params => { start_at => $id } };
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 6, 'Got 6 entries when starting at the 5th one.';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 5 .. 10 ],
"All 6 entries are in order.";
$res = dancer_response GET => "/feeds/foo", { params => {start_after => $id} };
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 5, 'Got 5 entries when starting after the 5th one.';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 6 .. 10 ],
"All 5 entries are in order.";
$res = dancer_response GET => "/feeds/foo",
{ headers => [ 'If-None-Match' => $id ] };
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 5, 'Got 5 entries when If-None-Match is 5th element.';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 6 .. 10 ],
"All 5 entries are in order.";
$res = dancer_response GET => "/feeds/foo",
{ headers => [ 'If-None-Match' => 'foo' ] };
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 10, 'Got all entries when If-None-Match is an unkown id.';
$id = $entries[-1]->find('./id'); # this is the last entry
$res = dancer_response GET => "/feeds/foo",
{ headers => [ 'If-None-Match' => $id] };
is $res->{status}, 304, "Status is 304 when If-None-Match is the last id";
is $res->{content}, '', "Body is empty when If-None-Match is the last id";
config->{atombus}{page_size} = 7;
$res = dancer_response GET => "/feeds/foo";
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 7, 'There are 7 entries with page_size = 7.';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 1 .. 7 ],
"All 7 entries are in order.";
$id = $entries[-1]->find('./id');
$res = dancer_response GET => "/feeds/foo", { params => {start_after => $id} };
$xp = XML::XPath->new(xml => $res->{content});
@entries = $xp->findnodes('/feed/entry');
is @entries => 3, 'Got rest of entries on last (second) page.';
is_deeply
[ map $_->findvalue('./content/div'), @entries ],
[ map "content$_", 8 .. 10 ],
"All 3 entries are in order.";
done_testing;
lib 000755 001750 001750 0 11743324625 13542 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 AtomBus.pm 100644 001750 001750 27231 11743324625 15637 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib package AtomBus;
use Dancer qw(:syntax);
use Dancer::Plugin::DBIC qw(schema);
use Atompub::DateTime qw(datetime);
use UUID::Tiny;
use XML::Atom;
$XML::Atom::DefaultVersion = '1.0';
our $VERSION = '1.0405'; # VERSION
set content_type => 'application/xml';
config->{plugins}{DBIC}{atombus} = config->{atombus}{db};
config->{plugins}{DBIC}{atombus}{schema_class} = 'AtomBus::Schema';
eval { schema->deploy }; # Fails gracefully if tables already exist.
get '/feeds/:feed_title/entries/:entry_id' => sub {
my $entry_id = 'urn:uuid:' . params->{entry_id};
my $db_entry = schema->resultset('AtomBusEntry')->find({id => $entry_id});
return send_error("No such message exists", 404)
unless $db_entry;
my $if_none_match = request->header('If-None-Match');
#TODO: support revised entries (i.e. Atompub PUT)
# my $revision_id = $db_entry->revision_id || $db_entry->id;
my $revision_id = $db_entry->id;
# If ETag matches current revision id of entry
if (my $id = $if_none_match) {
$id =~ s/^"(.*)"$/$1/; # Remove surrounding quotes
if ($revision_id eq $id) {
status 304;
return '';
}
}
my $entry = _entry_from_db($db_entry);
_add_etag($revision_id);
header Vary => 'If-None-Match';
return $entry->as_xml;
};
get '/feeds/:feed_title' => sub {
my $feed_title = lc params->{feed_title};
my $start_after = params->{start_after};
my $start_at = params->{start_at};
my $if_none_match = request->header('If-None-Match');
my $order_id;
if (my $id = $if_none_match) {
$id =~ s/^"(.*)"$/$1/; # Remove surrounding quotes
my $entry = schema->resultset('AtomBusEntry')->find({id => $id});
$order_id = $entry->order_id if $entry;
}
if (my $id = $start_after || $start_at) {
my $entry = schema->resultset('AtomBusEntry')->find({id => $id});
return send_error("No such message exists with id $id", 400)
unless $entry;
$order_id = $entry->order_id;
}
my $db_feed = schema->resultset('AtomBusFeed')->find(
{ title => $feed_title });
return send_error("No such feed exists named $feed_title", 404)
unless $db_feed;
my $feed = XML::Atom::Feed->new;
$feed->title($feed_title);
$feed->id($db_feed->id);
my $person = XML::Atom::Person->new;
$person->name($db_feed->author_name);
$feed->author($person);
$feed->updated($db_feed->updated);
my $self_link = XML::Atom::Link->new;
$self_link->rel('self');
$self_link->type('application/atom+xml');
$self_link->href(request->uri_for(request->path));
$feed->add_link($self_link);
#my $hub_link = XML::Atom::Link->new;
#$hub_link->rel('hub');
#$hub_link->href('http://184.106.189.98:8080/publish');
#$feed->add_link($hub_link);
my %query = (feed_title => $feed_title);
if ($order_id) {
$query{order_id} = { '>' => $order_id } if $if_none_match;
$query{order_id} = { '>' => $order_id } if $start_after;
$query{order_id} = { '>=' => $order_id } if $start_at;
}
my $rset = schema->resultset('AtomBusEntry')->search(
\%query, { order_by => ['order_id'] });
my $count = config->{atombus}{page_size} || 1000;
my $last_id;
while ($count-- && (my $entry = $rset->next)) {
$feed->add_entry(_entry_from_db($entry));
$last_id = $entry->id;
}
# If ETag was provided and there are no new entries
if (not $last_id and $if_none_match) {
status 304;
return '';
}
_add_etag($last_id) if $last_id;
header Vary => 'If-None-Match';
return $feed->as_xml;
};
post '/feeds/:feed_title' => sub {
my $feed_title = lc params->{feed_title};
my $body = request->body;
return send_error("Request body is empty", 400)
unless $body;
my $entry = XML::Atom::Entry->new(\$body);
my $updated = datetime->w3cz;
my $db_feed = schema->resultset('AtomBusFeed')->find_or_create({
title => $feed_title,
id => _gen_id(),
author_name => 'AtomBus',
updated => $updated,
}, { key => 'title_unique' });
my $db_entry = schema->resultset('AtomBusEntry')->create({
feed_title => $feed_title,
id => _gen_id(),
title => $entry->title,
content => $entry->content->body,
updated => $updated,
});
$db_feed->update({updated => $updated});
$entry = _entry_from_db($db_entry);
_add_etag($entry->id);
header Location => $entry->link->href;
content_type 'application/atom+xml;type=entry';
status 'created';
return $entry->as_xml;
};
sub _gen_id { 'urn:uuid:' . create_UUID_as_string() }
sub _id_nss { $_ = shift; s/^urn:uuid://; return $_ }
sub _entry_from_db {
my $row = shift;
my $entry = XML::Atom::Entry->new;
$entry->title($row->title);
$entry->content($row->content);
$entry->id($row->id);
$entry->updated($row->updated);
my $self_link = XML::Atom::Link->new;
$self_link->rel('self');
$self_link->type('application/atom+xml');
$self_link->href( join( '/', uri_for('/feeds'), $row->feed_title->title, 'entries', _id_nss($row->id) ));
$entry->add_link($self_link);
return $entry;
}
sub _add_etag { header ETag => qq("$_[0]") }
# ABSTRACT: An AtomPub server for messaging.
1;
__END__
=pod
=head1 NAME
AtomBus - An AtomPub server for messaging.
=head1 VERSION
version 1.0405
=head1 SYNOPSIS
use Dancer;
use AtomBus;
dance;
=head1 DESCRIPTION
AtomBus is an AtomPub server that can be used for messaging.
The idea is that atom feeds can correspond to conceptual queues or buses.
AtomBus is built on top of the L framework.
It is also pubsubhubbub friendly.
These examples assume that you have configured your web server to point HTTP
requests starting with /atombus to your AtomBus server (see L).
To publish an entry, make a HTTP POST request:
$ curl -d ' allo
an important message
' http://localhost/atombus/feeds/widgets
That adds a new entry to a feed titled widgets.
If that feed didn't exist before, it will be created for you.
To retrieve the widgets feed, make a HTTP GET request:
$ curl http://localhost/atombus/feeds/widgets
Clients can request only entries that came after the last entry they processed.
They can do this by providing the id of the last message as the start_after
parameter:
$ curl http://localhost/atombus/feeds/widgets?start_after=42
Alternatively, you can provide a start_at param. This will retrieve entries
starting with the given id:
$ curl http://localhost/atombus/feeds/widgets?start_at=42
HTTP ETags are also supported.
The server responds with an ETag header for each request.
The client can provide that ETag as the If-None-Match header.
The following example will work the same as if the client provided a start_after
parameter.
Except that it will return an empty body and a 304 status if there are no new
entries.
This is the behavior that pubsubhubbub recommends
L.
$ curl -H 'If-None-Match: "42"' http://localhost/atombus/feeds/widgets
Note that the most messages you will get per request is determined by the
page_size setting. If you do not specify a page_size setting, it defaults to
1000. This default may change in the future, so don't count on it.
AtomBus is mostly a proper implementation of the AtomPub protocol and will
validate 100% against L.
One point where it diverges from the AtomPub spec is that feed entries are
returned in fifo order.
This is because a message consumer will most likely want to consume messages
in the order that they were published.
In the future, a config setting may be available to reverse the order.
=head1 CONFIGURATION
Configuration can be achieved via a config.yml file or via the set keyword.
To use the config.yml approach, you will need to install L.
See the L documentation for more information.
The only required config setting is the dsn.
Example config.yml:
# Dancer specific config settings
logger: file
log: errors
atombus:
page_size: 100
db:
dsn: dbi:mysql:database=atombus
user: joe
pass: momma
You can alternatively configure the server via the 'set' keyword in the source
code. This approach does not require a config file.
use Dancer;
use AtomBus;
# Dancer specific config settings
set logger => 'file';
set log => 'debug';
set show_errors => 1;
set atombus => {
page_size => 100,
db => {
dsn => 'dbi:SQLite:dbname=/var/local/atombus/atombus.db',
}
};
dance;
=head1 DATABASE
AtomBus is backed by a database.
The dsn in the config must point to a database which you have write privileges
to.
The tables will be created automagically for you if they don't already exist.
Of course that requires create table privileges.
All databases supported by L are supported,
which are most major databases including postgresql, sqlite, mysql and oracle.
=head1 DEPLOYMENT
Deployment is very flexible.
It can be run on a web server via CGI or FastCGI.
It can also be run on any L web server.
See L for more details.
=head2 FastCGI
AtomBus can be run via FastCGI.
This requires that you have the L and L modules installed.
Here is an example FastCGI script.
It assumes your AtomBus server is in the file atombus.pl.
#!/usr/bin/env perl
use Dancer ':syntax';
use Plack::Handler::FCGI;
my $app = do "/path/to/atombus.pl";
my $server = Plack::Handler::FCGI->new(nproc => 5, detach => 1);
$server->run($app);
Here is an example lighttpd config.
It assumes you named the above file atombus.fcgi.
fastcgi.server += (
"/atombus" => ((
"socket" => "/tmp/fcgi.sock",
"check-local" => "disable",
"bin-path" => "/path/to/atombus.fcgi",
)),
)
Now AtomBus will be running via FastCGI under /atombus.
=head2 Plack
AtomBus can be run with any L web server. Just run:
plackup atombus.pl
You can change the Plack web server via the -s option to plackup.
=head1 MOTIVATION
I like messaging systems because they make it so easy to create scalable
applications.
Existing message brokers are great for creating message queues.
But once a consumer reads a message off of a queue, it is not available for
other consumers.
I needed a system to publish events such that multiple heterogeneous services
could subscribe to them.
So I really needed a message bus, not a message queue.
I could for example have used something called topics in ActiveMQ,
but I have found ActiveMQ to be broken in general.
An instance I manage has to be restarted daily.
AtomBus on the other hand will be extremely stable, because it is so simple.
It is in essence just a simple interface to a database.
As long as your database and web server are up, AtomBus will be there for you.
And there are many ways to add redundancy to databases and web heads.
Another advantage of using AtomBus is that Atom is a well known standard.
Everyone already has a client for it, their browser.
Aren't standards great!
By the way, if you just need message queues, try
L.
It rocks. If you need a message bus, give AtomBus a shot.
=head1 AUTHOR
Naveed Massjouni
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
public 000755 001750 001750 0 11743324625 14252 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 dispatch.cgi 100755 001750 001750 323 11743324625 16656 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/public #!/usr/bin/env perl
use Dancer ':syntax';
use FindBin '$RealBin';
use Plack::Runner;
set apphandler => 'PSGI';
set environment => 'development';
my $psgi = "$RealBin/../bin/app.pl";
Plack::Runner->run($psgi);
dispatch.fcgi 100755 001750 001750 426 11743324625 17030 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/public #!/usr/bin/env perl
use Dancer ':syntax';
use FindBin '$RealBin';
use Plack::Handler::FCGI;
set apphandler => 'PSGI';
set environment => 'production';
my $app = do "$RealBin/../bin/app.pl";
my $server = Plack::Handler::FCGI->new(nproc => 5, detach => 1);
$server->run($app);
AtomBus 000755 001750 001750 0 11743324625 15114 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib Schema.pm 100644 001750 001750 737 11743324625 17001 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib/AtomBus package AtomBus::Schema;
use strict;
use warnings;
use base 'DBIx::Class::Schema';
__PACKAGE__->load_namespaces;
1;
__END__
=pod
=head1 NAME
AtomBus::Schema
=head1 VERSION
version 1.0405
=head1 AUTHOR
Naveed Massjouni
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
environments 000755 001750 001750 0 11743324625 15523 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405 production.yml 100644 001750 001750 451 11743324625 20554 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/environments # configuration file for production environment
# only log warning and error messsages
log: "warning"
# log message to a file in logs/
logger: "file"
# don't consider warnings critical
warnings: 0
# hide errors
show_errors: 0
# cache route resolution for maximum performance
route_cache: 1
development.yml 100644 001750 001750 522 11743324625 20707 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/environments # configuration file for development environment
logger: "file"
# core is the lowest, it shows Dancer's core log messages as well as yours
# (debug, warning and error)
#log: "core"
log: "debug"
# shoud Dancer consider warnings as critical errors?
warnings: 1
# should Dancer show a stacktrace when an error is caught?
show_errors: 1
Result 000755 001750 001750 0 11743324625 17572 5 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib/AtomBus/Schema AtomBusFeed.pm 100644 001750 001750 3331 11743324625 22426 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib/AtomBus/Schema/Result package AtomBus::Schema::Result::AtomBusFeed;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("atombus_feed");
__PACKAGE__->add_columns(
id => { data_type => "varchar", is_nullable => 0, size => 100 },
title => { data_type => "varchar", is_nullable => 0, size => 255 },
author_name => { data_type => "varchar", is_nullable => 1, size => 255 },
author_email => { data_type => "varchar", is_nullable => 1, size => 255 },
updated => { data_type => "varchar", is_nullable => 0, size => 100 },
);
__PACKAGE__->set_primary_key("id");
__PACKAGE__->add_unique_constraint("title_unique", ["title"]);
__PACKAGE__->has_many(
"atombus_entries",
"AtomBus::Schema::Result::AtomBusEntry",
{ "foreign.feed_title" => "self.title" },
{ cascade_copy => 0, cascade_delete => 0 },
);
1;
__END__
=pod
=head1 NAME
AtomBus::Schema::Result::AtomBusFeed
=head1 VERSION
version 1.0405
=head1 NAME
AtomBus::Schema::Result::AtomBusFeed
=head1 ACCESSORS
=head2 id
data_type: 'varchar'
is_nullable: 0
size: 100
=head2 title
data_type: 'varchar'
is_nullable: 0
size: 255
=head2 author_name
data_type: 'varchar'
is_nullable: 1
size: 255
=head2 author_email
data_type: 'varchar'
is_nullable: 1
size: 255
=head2 updated
data_type: 'varchar'
is_nullable: 0
size: 100
=head1 RELATIONS
=head2 atombus_entries
Type: has_many
Related object: L
=head1 AUTHOR
Naveed Massjouni
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut
AtomBusEntry.pm 100644 001750 001750 4313 11743324625 22665 0 ustar 00naveed naveed 000000 000000 AtomBus-1.0405/lib/AtomBus/Schema/Result package AtomBus::Schema::Result::AtomBusEntry;
use strict;
use warnings;
use base 'DBIx::Class::Core';
__PACKAGE__->table("atombus_entry");
__PACKAGE__->add_columns(
order_id => { data_type => "integer", is_nullable => 0,
is_auto_increment => 1 },
id => { data_type => "varchar", is_nullable => 0, size => 100 },
feed_title => { data_type => "varchar", is_nullable => 0, size => 255,
is_foreign_key => 1 },
title => { data_type => "text", is_nullable => 0 },
author_name => { data_type => "varchar", is_nullable => 1, size => 255 },
author_email => { data_type => "varchar", is_nullable => 1, size => 255 },
updated => { data_type => "varchar", is_nullable => 0, size => 100 },
content => { data_type => "text", is_nullable => 0 },
);
__PACKAGE__->set_primary_key("order_id");
__PACKAGE__->add_unique_constraint("id_unique", ["id"]);
__PACKAGE__->belongs_to(
"feed_title",
"AtomBus::Schema::Result::AtomBusFeed",
{ title => "feed_title" },
{ is_deferrable => 1, on_delete => "CASCADE", on_update => "CASCADE" },
);
1;
__END__
=pod
=head1 NAME
AtomBus::Schema::Result::AtomBusEntry
=head1 VERSION
version 1.0405
=head1 NAME
AtomBus::Schema::Result::AtomBusEntry
=head1 ACCESSORS
=head2 order_id
data_type: 'integer'
is_nullable: 0
is_auto_increment: 1
=head2 id
data_type: 'varchar'
is_nullable: 0
size: 100
=head2 feed_title
data_type: 'varchar'
is_foreign_key: 1
is_nullable: 0
size: 255
=head2 title
data_type: 'text'
is_nullable: 0
=head2 author_name
data_type: 'varchar'
is_nullable: 1
size: 255
=head2 author_email
data_type: 'varchar'
is_nullable: 1
size: 255
=head2 updated
data_type: 'varchar'
is_nullable: 0
size: 100
=head2 content
data_type: 'text'
is_nullable: 0
=head1 RELATIONS
=head2 feed_title
Type: belongs_to
Related object: L
=head1 AUTHOR
Naveed Massjouni
=head1 COPYRIGHT AND LICENSE
This software is copyright (c) 2010 by Naveed Massjouni.
This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.
=cut