bookit 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ *.DS_Store
6
+ lib/test.*
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in bookit.gemspec
4
+ gemspec
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ Gem::Specification.new do |s|
3
+ s.name = "bookit"
4
+ s.version = "0.0.1"
5
+ s.platform = Gem::Platform::RUBY
6
+
7
+ s.authors = ["Luke van der Hoeven"]
8
+ s.email = ["hungerandthirst@gmail.com"]
9
+
10
+ s.homepage = "http://github.com/plukevdh/bookit"
11
+ s.summary = %q{BOOKS WHAT}
12
+ s.description = %q{A quick way to format and output generic book information or articles in multiple formats. Mainly: PDF, EPUB and MOBI}
13
+
14
+ s.rubyforge_project = "bookit"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # output
22
+ s.add_dependency("prawn")
23
+ s.add_dependency("eeepub")
24
+
25
+ # parsing
26
+ s.add_dependency("nokogiri")
27
+
28
+ # storage
29
+ s.add_dependency("redis")
30
+ end
@@ -0,0 +1,19 @@
1
+ <div><div class="entry grid_8 prefix_2">
2
+ <p>I&#8217;m not sure how old I was &#8211; seven or eight, maybe &#8211; when my Grandmother caught my cousin and I prying the wood paneling off the wall in one of her house&#8217;s bedrooms using the plastic ketchup and mustard bottles from a grocery store playset we had. I can count on one hand the number of times I remember her actually becoming visibly angry with any of her grandkids, and that was one of them.</p>
3
+ <p>But we hadn&#8217;t intended to be destructive. We were searching for a secret passage. The house was pretty old &#8211; my Mom had grown up in it &#8211; and so, according to the logic of kids&#8217; fantasy and detective media, it had secret passages and we were going to find them, little ears to the walls, palming and knocking softly like I must have seen someone do in a movie. My cousin was too young to do much other than completely believe my logic, that the slightly loose piece of paneling we discovered in that particular bedroom had to conceal a door.</p>
4
+ <p>I&#8217;m not entirely sure what I hoped it&#8217;d be a door <em>to</em>. Crawlspaces to enable professional espionage against our family? Dark, quiet passages to underworld chasms, mysterious cities, foreign tombs laden with treasure or haunted by royal ghosts? Long-concealed empire drawing rooms somehow forgotten as part of the infrastructure of the house? It didn&#8217;t really matter, as any of these outcomes were implied in &#8216;secret passage.&#8217; We&#8217;d figure it out when we found it.</p>
5
+ <p>I was always doing things like that. Crawling, fingers outstretched, eyes shut, for the backs of closets, feeling deterred &#8211; but never defeated &#8211; should my fingertips brush cool plaster and not the foreign foliage of another realm. Searching inside of flowers for miniature cities, listening to trees in case one of them was a prince from another world, transformed by a curse. Whispering at the moon in the hopes of stumbling upon an incantation &#8211; all of it, a continuous (often inspiring, often lonesome) search for ways to go <em>elsewhere</em>.</p>
6
+ <p>Entertainment media aimed at children has always promised that escape. Fairy tales illuminate a world that exists beyond what the eye can see, a realm of magic available to one special person with the right sort of heart. Unicorns, the legends go, only appear for the pure. The hero chosen to save the kingdom is always one unlikely, often luck and love-starved child. Even modern blockbuster movies about kids getting lost in the big city, of babysitting nights gone awry, about young detectives, have helped contribute to an entire culture aimed at teaching kids to dream of something better than &#8216;all there is,&#8217; of powers undiscovered, a way out of the mundane.</p>
7
+ <p>Imagination is generally the realm of children. Young people have not yet learned the world is boring and hard, and so learning to create those psychic buffer zones, how to be their own companion, how to seek escape, is a skill foundation that must be laid early.</p>
8
+ <p>Even the grown-ups who, as the stereotype goes, tell kids to get their heads out of the clouds, to stop talking to people that aren&#8217;t there, are, intentionally or otherwise, strengthening the dreamer&#8217;s urge that will help them be great creators and great innovators as adults, in the face of a society that loves to quash the tall poppy with prescribed social norms, to fling up bureaucratic roadblocks, to declare: &#8216;Impossible.&#8217;</p>
9
+ <p>A resilient spine of imagination is what drives human beings to strive for impossible things &#8211; like dream of space travel, of cures for incurable disease, of world peace. Technology, interaction and even the development&#160; of architecture and vehicle design marches onward in no small part because imaginative kids grow up into ambitious adults who really, really want to actualize the shiny adventures of the science fiction books they love &#8211; flying cars, sentient robots, space needles and all.</p>
10
+ <p>Because &#8216;wanting out&#8217; is a quintessential part of the adult life. All people need to believe there is a way to escape normalcy and the status quo. If we all believed there was nothing to do but accept reality, life would be intolerable. We could not progress.</p>
11
+ <p>Most important about the things we dreamed as children is that there was never any real danger. A secret passage walled into an old house is far more likely to contain a corpse than a luminous parallel universe, but I never thought about that. Even if as a kid you <em>did</em> manage to build a functioning time machine so that you could visit the age of the dinosaurs, you never stopped to think that you might become the meal of some vicious primordial monster.</p>
12
+ <p>We sought portals to new lands without worrying that we might end up their permanent prisoner. Any potential threat &#8211; black knights, malfunctioning robots, dragons and ghosts &#8211; only served to promise us the chance to be heroic. Dreamer-children learned to consider possibilities with endless wellsprings of excitement and ambition, not fear.</p>
13
+ <p>The worst in humanity arises from the chasm between the real world and our dreams. We are liable to become inert, we become avoidant, we become addicts, or we self-sabotage because of the cruelty of the real, and the crushing lesson that there is only a certain extent to which we will ever subvert it.</p>
14
+ <p>But survival and great success are born from the refusal to accept boundaries, though it may be strange to conceive of our greatest creative endeavors as direct responses to the fact that life sucks. Or to think that the most impressive thing any individual will ever accomplish is born out of the friction between what we want to be and what we are.&#160; That&#8217;s what we were learning when playing imagination games as children &#8211; how to want a way out, always, from the world we were given.</p>
15
+ <p>And the worse that world was, the more we wanted that escape. That&#8217;s why the greatest strides in this world are often made by the people who&#8217;ve suffered most &#8211; and why you never meet anyone interesting who says they had a great time in high school. <span class="tc_mark"><img src="http://thoughtcatalog.com/wp-content/themes/thought_catalog/images/tc_mark.gif" alt="TC mark"></span></p>
16
+ <h3>You should follow Thought Catalog on Twitter <a href="http://www.twitter.com/thoughtcatalog" rel="nofollow">here</a>.</h3>
17
+ </div>
18
+ </div>
19
+
@@ -0,0 +1,68 @@
1
+ Ruby + Upskirt = Markdown that doesn't suck
2
+ ===========================================
3
+
4
+ Redcarpet is a Ruby wrapper for Upskirt. It is mostly based on Ryan
5
+ Tomayko's RDiscount wrapper, and inspired by Rick Astley wearing a kilt.
6
+
7
+ Redcarpet is powered by the Upskirt library, which can be found at
8
+
9
+ https://www.github.com/tanoku/upskirt
10
+
11
+ You might want to find out more about Upskirt to see what makes these Ruby
12
+ bindings so awesome.
13
+
14
+ Credits
15
+ -------
16
+
17
+ * Natacha Porté, lady of Markdown
18
+ * Vicent Martí, wannabe
19
+ * With special thanks to Ryan Tomayko
20
+
21
+ Install
22
+ -------
23
+
24
+ Redcarpet is readily available as a Ruby gem:
25
+
26
+ $ [sudo] gem install redcarpet
27
+
28
+ The Redcarpet source (including Upskirt as a submodule) is available at GitHub:
29
+
30
+ $ git clone git://github.com/tanoku/redcarpet.git
31
+
32
+ Usage
33
+ -----
34
+
35
+ Redcarpet implements the basic protocol popularized by RedCloth:
36
+
37
+ require 'redcarpet'
38
+ markdown = Redcarpet.new("Hello World!")
39
+ puts markdown.to_html
40
+
41
+ Additional processing options can be turned on when creating the
42
+ Redcarpet object:
43
+
44
+ markdown = Redcarpet.new("Hello World!", :smart, :filter_html)
45
+
46
+ Note that by default, Redcarpet parses standard Markdown (with no extensions)
47
+ and offers a sane subset of parse options which allow you to modify the rendering
48
+ output and to enable MD extensions on a per-case basis.
49
+
50
+ Redcarpet also offers a wrapper class, `RedcarpetCompat` with the same flags
51
+ and behavior as the RDiscount library, which acts as a drop-in replacement.
52
+
53
+ License
54
+ -------
55
+
56
+ Permission to use, copy, modify, and distribute this software for any
57
+ purpose with or without fee is hereby granted, provided that the above
58
+ copyright notice and this permission notice appear in all copies.
59
+
60
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
61
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
62
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
63
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
64
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
65
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
66
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
67
+
68
+
@@ -0,0 +1,77 @@
1
+ <div><div class="entry-content">
2
+ <p>I was a Java guy for 10 years and I&#8217;ve been a Rubyist for the last 5 years. Over the years, I&#8217;ve tried to develop expertise in a particular area of technology that will both pay the bills and make me happy as a programmer while also watching for upcoming changes in the tech world. I often find myself diving into a particular technology just to get my hands dirty and get a feel for its strengths and weaknesses. As my JavaScript skills have always been weak, I&#8217;ve decided to deep dive into <a href="http://nodejs.org" rel="nofollow">Node.js</a> to understand what it does well and improve my JavaScript skills at the same time.</p>
3
+ <p>For this post, I&#8217;m just going to cover the basics; I&#8217;ll follow up soon with deeper posts.<br></p>
4
+ <h2>Overview</h2>
5
+ <p>JavaScript has an interesting history &#8211; it hasn&#8217;t developed like most other languages; until recently, executing JavaScript meant embedding it in a web page for a browser to execute. A few things happened which radically hastened the rise in JavaScript as an reasonable server-side language:</p>
6
+ <ul><li>AJAX and the Browser Wars have resulted in dramatic improvements in Javascript runtime performance and high-quality developer tools.</li>
7
+ <li>Node.js built Process, File and Network I/O APIs on top of Google&#8217;s <a href="http://code.google.com/p/v8/" rel="nofollow">V8 JavaScript engine</a>, allowing command line programs and daemons to be built in JavaScript.</li>
8
+ </ul><p>Node.js adds a friendly command line face to V8 and APIs that are conceptually similar to Ruby&#8217;s EventMachine library: all I/O is asynchronous and threads are unavailable to user code. Additionally JavaScript is a prototype-based language, not object-oriented. This makes for a programming model that is radically different from what Ruby or Java developers are used to.</p>
9
+ <h2>Installation</h2>
10
+ <p>I&#8217;m going to assume OSX and I like to install things with <a href="http://mxcl.github.com/homebrew/" rel="nofollow">Homebrew</a>. We&#8217;ll install node and npm, node&#8217;s package manager, with these commands:</p>
11
+ <pre class="brush: bash;">
12
+ brew update # update Homebrew's formulas to the latest
13
+ brew install node # install node
14
+ curl http://npmjs.org/install.sh | sudo sh # install npm
15
+ </pre>
16
+ <p>Once installed, you should be able to run <code>node --help</code> and <code>npm --help</code>.</p>
17
+ <p>A minimal web server using Node.js:</p>
18
+ <pre class="brush: jscript;">
19
+ var http = require('http');
20
+ http.createServer(function (req, res) {
21
+ res.writeHead(200, {'Content-Type': 'text/plain'});
22
+ res.end('Hello World\n');
23
+ }).listen(8124, "127.0.0.1");
24
+ </pre>
25
+ <p>Copy that code into <code>hello.js</code> and run it:</p>
26
+ <pre>
27
+ node hello.js
28
+ </pre>
29
+ <p>Now let&#8217;s slam it with some requests:</p>
30
+ <pre>
31
+ ab -n 10000 -c 50 http://127.0.0.1:8124/
32
+ </pre>
33
+ <p>Results:</p>
34
+ <pre>
35
+ Server Hostname: 127.0.0.1
36
+ Server Port: 8124
37
+ Document Path: /
38
+ Document Length: 12 bytes
39
+ Concurrency Level: 50
40
+ Time taken for tests: 1.479 seconds
41
+ Complete requests: 10000
42
+ Failed requests: 0
43
+ Write errors: 0
44
+ Total transferred: 760000 bytes
45
+ HTML transferred: 120000 bytes
46
+ Requests per second: 6760.79 [#/sec] (mean)
47
+ Time per request: 7.396 [ms] (mean)
48
+ Time per request: 0.148 [ms] (mean, across all concurrent requests)
49
+ Transfer rate: 501.78 [Kbytes/sec] received
50
+
51
+ Connection Times (ms)
52
+ min mean[+/-sd] median max
53
+ Connect: 0 0 0.2 0 3
54
+ Processing: 1 7 3.6 7 20
55
+ Waiting: 1 7 3.6 7 20
56
+ Total: 1 7 3.6 7 22
57
+
58
+ Percentage of the requests served within a certain time (ms)
59
+ 50% 7
60
+ 66% 9
61
+ 75% 10
62
+ 80% 11
63
+ 90% 12
64
+ 95% 13
65
+ 98% 15
66
+ 99% 16
67
+ 100% 22 (longest request)
68
+ </pre>
69
+ <p>Not bad. Of course, this is using localhost and a trivial app but at least we know it&#8217;s up and running well. In my next post, we&#8217;ll explore the Node.js source code itself.
70
+ </p><div class="snap_nopreview sharing robots-nocontent">
71
+
72
+ </div>
73
+ </div>
74
+
75
+
76
+ </div>
77
+
@@ -0,0 +1,28 @@
1
+ <div>
2
+ <div>
3
+ <h1>TEST DOC</h2>
4
+ <p>
5
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris <a href="http://test.link.com">condimentum nibh</a>, ut fermentum massa justo sit amet risus. Nulla vitae elit libero, a pharetra augue. Maecenas faucibus mollis interdum. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
6
+ </p>
7
+
8
+
9
+ <a href="http://text.com">Nullam quis</a>
10
+ <p>
11
+ Nullam quis risus eget urna mollis ornare vel eu leo. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper. Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit sit amet non magna.
12
+
13
+ <img src="http://snarkerati.com/movie-news/files/2011/01/russell_brand.jpg" width="120" >
14
+ </p>
15
+
16
+ <ul>
17
+ <li>One</li>
18
+ <li>
19
+ <a href="http://text.com">Two</a>
20
+ </li>
21
+ <li>Three</li>
22
+ </ul>
23
+
24
+ <p>
25
+ Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Nulla vitae elit libero, a pharetra augue. Cras mattis consectetur purus sit amet fermentum. Maecenas sed diam eget risus varius blandit sit amet non magna. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.
26
+ </p>
27
+ </div>
28
+ </div>
@@ -0,0 +1,29 @@
1
+ HEADER "TEST DOC"
2
+ PARAGRAPH
3
+ TEXT: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris
4
+ LINK:
5
+ TEXT: "http://test.link.com":"condimentum nibh",
6
+ TEXT: ut fermentum massa justo sit amet risus. Nulla vitae elit libero, a pharetra augue. Maecenas faucibus mollis interdum. Maecenas sed diam eget risus varius blandit sit amet non magna. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.
7
+
8
+ LINK:
9
+ TEXT: "http://text.com":"Nullam quis"
10
+ PARAGRAPH:
11
+ TEXT: Nullam quis risus eget urna mollis ornare vel eu leo. Praesent commodo cursus magna, vel scelerisque nisl consectetur et. Vestibulum id ligula porta felis euismod semper. Donec id elit non mi porta gravida at eget metus. Maecenas sed diam eget risus varius blandit sit amet non magna.
12
+
13
+ IMAGE: "http://snarkerati.com/movie-news/files/2011/01/russell_brand.jpg"
14
+
15
+ LIST:
16
+ TEXT: "One"
17
+ LINK:
18
+ TEXT: "http://text.com":"Two"
19
+ TEXT: "Three"
20
+
21
+ PARAGRAPH:
22
+ TEXT: Maecenas sed diam eget risus varius blandit sit amet non magna. Maecenas faucibus mollis interdum. Nulla vitae elit libero, a pharetra augue. Cras mattis consectetur purus sit amet fermentum. Maecenas sed diam eget risus varius blandit sit amet non magna. Morbi leo risus, porta ac consectetur ac, vestibulum at eros. Aenean eu leo quam. Pellentesque ornare sem lacinia quam venenatis vestibulum.
23
+
24
+ #<Bookit::Content::Header:0x000001013c2c18>
25
+ #<Bookit::Content::Paragraph:0x000001013ac8f0>
26
+ #<Bookit::Content::Link:0x000001013abab8>
27
+ #<Bookit::Content::Paragraph:0x000001013ab2c0>
28
+ #<Bookit::Content::List:0x0000010139ce00>
29
+ #<Bookit::Content::Paragraph:0x0000010139c310>
@@ -0,0 +1,24 @@
1
+ module Bookit
2
+ require 'prawn'
3
+ require 'eeepub'
4
+ require 'redis'
5
+
6
+ autoload :PersistableObject, './bookit/persistable_object'
7
+ autoload :Article, './bookit/article'
8
+ autoload :Parser, './bookit/parser'
9
+ autoload :Content, './bookit/content'
10
+
11
+ class Content
12
+ autoload :Generic, './bookit/content/generic'
13
+ end
14
+
15
+ class Parser
16
+ autoload :Html, './bookit/parser/html'
17
+ end
18
+
19
+ class Emitter
20
+ autoload :Abstract, './bookit/emitter/abstract'
21
+ autoload :Pdf, './bookit/emitter/pdf'
22
+ end
23
+ end
24
+
@@ -0,0 +1,27 @@
1
+ require './bookit/parser/html'
2
+ require './bookit/emitter/pdf'
3
+
4
+ module Bookit
5
+ class Article
6
+ REQUIRED_ATTRS = [:author, :date_published, :url, :title, :content]
7
+ attr_accessor *REQUIRED_ATTRS, :attributes
8
+
9
+ def initialize(attrs, parser=Bookit::Parser::Html)
10
+ @attributes = attrs.dup
11
+
12
+ REQUIRED_ATTRS.each do |key|
13
+ raise "Required attribute #{key} not found." unless @attributes[key]
14
+ self.send "#{key}=", @attributes[key]
15
+ end
16
+
17
+ # parse raw content into array of generic content
18
+ @content = Content.new(@content, parser)
19
+ end
20
+
21
+ # Article's to print method outputs the given input content into the given
22
+ # emitter document type. We output PDF by default.
23
+ def to_print(emitter=Bookit::Emitter::Pdf)
24
+ emitter.new(self).generate
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,39 @@
1
+ # the Content class is an AST of sorts. it holds the generic structure
2
+ # of the article's content so that we can easily parse out important content
3
+ # attributes and then later output into other formats.
4
+
5
+ module Bookit
6
+ class Content
7
+ attr_reader :raw_content
8
+ attr_accessor :formatted_content
9
+
10
+ autoload :Generic, './bookit/content/generic'
11
+ autoload :Paragraph, './bookit/content/paragraph'
12
+ autoload :Text, './bookit/content/text'
13
+ autoload :Link, './bookit/content/link'
14
+ autoload :Image, './bookit/content/image'
15
+ autoload :Header, './bookit/content/header'
16
+ autoload :List, './bookit/content/list'
17
+
18
+ TYPES = {
19
+ paragraph: Content::Paragraph,
20
+ image: Content::Image,
21
+ header: Content::Header,
22
+ link: Content::Link,
23
+ list: Content::List
24
+ }
25
+
26
+ def initialize(raw_content, parser)
27
+ @raw_content = raw_content
28
+ @formatted_content = parser.new.parse(raw_content)
29
+ end
30
+
31
+ # #render for any Content object should always return a string
32
+ # it is also possible to pass a block to render actions so that
33
+ # you can fine-tune the content output from your Emitters
34
+ def render
35
+ @formatted_content.each &:render
36
+ end
37
+
38
+ end
39
+ end
@@ -0,0 +1,29 @@
1
+ module Bookit
2
+ class Content::Generic
3
+ attr_accessor :attributes
4
+
5
+ # Content can always take options. These can usually be used
6
+ # to add additional formatting information for the Emitters
7
+ def initialize(options={})
8
+ @attributes = options
9
+ end
10
+
11
+ # #render for any Content object should always return a string
12
+ # it is also possible to pass a block to render actions so that
13
+ # you can fine-tune the content output from your Emitters
14
+ def render
15
+ inspect
16
+ end
17
+
18
+ # gives us an easy to compare type symbol
19
+ def type
20
+ self.class.to_s.split('::').last.downcase.to_sym
21
+ end
22
+
23
+ # returns true if this symbol is tied to the class type
24
+ def is_type? sym
25
+ self.class == Content::TYPES[sym]
26
+ end
27
+ end
28
+ end
29
+
@@ -0,0 +1,19 @@
1
+ module Bookit
2
+ class Content
3
+ class Header < Generic
4
+ attr_accessor :text
5
+
6
+ # headers will only ever contain text, but usually contained separately
7
+ # for formatting purposes
8
+ def initialize(text, options={})
9
+ @text = text
10
+
11
+ super options
12
+ end
13
+
14
+ def render
15
+ text
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ require 'open-uri'
2
+
3
+ module Bookit
4
+ class Content
5
+ class Image < Generic
6
+ attr_accessor :source
7
+
8
+ # takes a source url
9
+ def initialize(source, options={})
10
+ @source = source
11
+
12
+ super options
13
+ end
14
+
15
+ # returns the url to the image
16
+ def render
17
+ [open(source), attributes]
18
+ end
19
+ end
20
+ end
21
+ end
22
+
@@ -0,0 +1,21 @@
1
+ module Bookit
2
+ class Content
3
+
4
+ # Content that will be clickable to web urls. Can be images or text.
5
+ class Link < Generic
6
+ attr_accessor :url, :text
7
+
8
+ def initialize(url, objects, options={})
9
+ @url = url
10
+ @objects = (objects.class == Array) ? objects : [objects]
11
+
12
+ super options
13
+ end
14
+
15
+ def render
16
+ [@url, @objects.map(&:render)]
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,20 @@
1
+ module Bookit
2
+ class Content
3
+ class List < Generic
4
+ attr_accessor :items
5
+
6
+ # a List could contain text or links.
7
+ def initialize(items, options={})
8
+ items.compact!
9
+ @items = (items.class == Array) ? items : [items]
10
+
11
+ super options
12
+ end
13
+
14
+ def render
15
+ @items
16
+ end
17
+ end
18
+ end
19
+ end
20
+
@@ -0,0 +1,22 @@
1
+ module Bookit
2
+ class Content
3
+ class Paragraph < Generic
4
+ attr_accessor :contents
5
+
6
+ # Paragraph can contain most other object types. Text, images or links.
7
+ def initialize(contents, options={})
8
+ # this could be an array of images, text, links or just a single element
9
+ contents.compact!
10
+ @contents = (contents.class == Array) ? contents : [contents]
11
+
12
+ super options
13
+ end
14
+
15
+ def render
16
+ @contents
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+
@@ -0,0 +1,19 @@
1
+ module Bookit
2
+ class Content
3
+ class Text < Generic
4
+ attr_accessor :text
5
+
6
+ def initialize(text, options={})
7
+ @text = text
8
+
9
+ super options
10
+ end
11
+
12
+ def render
13
+ text
14
+ end
15
+ end
16
+ end
17
+ end
18
+
19
+
@@ -0,0 +1,4 @@
1
+ module Bookit
2
+ class Emitter
3
+ end
4
+ end
@@ -0,0 +1,35 @@
1
+ module Bookit
2
+ class Emitter
3
+ class Abstract
4
+ attr_accessor :article, :options
5
+
6
+ DEFAULT_OPTIONS = {
7
+ font_size: 12,
8
+
9
+ header_size: 20,
10
+ header_style: :bold,
11
+
12
+ subtitle_size: 10,
13
+ subtitle_style: :italic
14
+ }
15
+
16
+ def initialize(article, options={})
17
+ @article = article
18
+ @options = DEFAULT_OPTIONS.merge(options)
19
+
20
+ # have all our type renderers raise an error unless defined in the subclass.
21
+ Bookit::Content::TYPES.each do |type|
22
+ self.class.send(:define_method, "render_#{type}".to_sym, -> { raise_abstract })
23
+ end
24
+
25
+ end
26
+
27
+ private
28
+ def raise_abstract
29
+ raise "Abstract class, please defined this to render for your filetype."
30
+ end
31
+
32
+ end
33
+ end
34
+ end
35
+
@@ -0,0 +1,37 @@
1
+ module Bookit
2
+ class Emitter
3
+ class Abstract
4
+ attr_accessor :article, :options
5
+
6
+ DEFAULT_OPTIONS = {
7
+ font_size: 12,
8
+
9
+ header_size: 20,
10
+ header_style: :bold,
11
+
12
+ subtitle_size: 10,
13
+ subtitle_style: :italic
14
+ }
15
+
16
+ def initialize(article, options={})
17
+ @article = article
18
+ @options = DEFAULT_OPTIONS.merge(options)
19
+
20
+ # have all our type renderers raise an error unless defined in the subclass.
21
+ Bookit::Content::TYPES.each do |type|
22
+ define_method "render_#{type}".to_sym do
23
+ raise_abstract
24
+ end
25
+ end
26
+
27
+ end
28
+
29
+ private
30
+ def raise_abstract
31
+ raise "Abstract class, please defined this to render for your filetype."
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+
@@ -0,0 +1,71 @@
1
+ require 'prawn'
2
+ require 'prawn/measurement_extensions'
3
+
4
+ module Bookit
5
+ class Emitter
6
+ class Pdf < Abstract
7
+ def generate
8
+ @pdf = Prawn::Document.new(info: {
9
+ :Title => @article.title,
10
+ :CreationDate => @article.date_published,
11
+ :Author => @article.author,
12
+ :Source => @article.url },
13
+ margin: 0.5.in, page_size: 'LEGAL')
14
+
15
+ @pdf.font_size = options[:font_size]
16
+
17
+ @pdf.text @article.title, size: options[:header_size], style: options[:header_style]
18
+ @pdf.text @article.author, style: :bold
19
+
20
+ @pdf.text @article.date_published, size: options[:subtitle_size], style: options[:subtitle_style]
21
+ @pdf.text @article.url, size: options[:subtitle_size], style: options[:subtitle_style]
22
+
23
+ @pdf.text "\n\n"
24
+
25
+ @article.content.formatted_content.each do |item|
26
+ output(render(item))
27
+ end
28
+
29
+ @pdf
30
+ end
31
+
32
+ private
33
+ def render(item)
34
+ send "render_#{item.type}", item.render
35
+ end
36
+
37
+ def output(group)
38
+ group[2] = {inline_format: true}
39
+ @pdf.send *group.flatten
40
+ @pdf.text("\n")
41
+ end
42
+
43
+ def render_paragraph(items)
44
+ ["text", items.map {|i| render(i)[1..-1]}.join("")]
45
+ end
46
+
47
+ def render_text(text)
48
+ ["text", text]
49
+ end
50
+
51
+ # render images on finding them. not embeddable within a paragraph for now.
52
+ def render_image(img)
53
+ @pdf.image *img
54
+ ["text"]
55
+ end
56
+
57
+ def render_header(text)
58
+ ["text", text, size: options[:header_size]]
59
+ end
60
+
61
+ def render_list(items)
62
+ ["text", items.map {|i| "- #{render(i)[1..-1].join(" ")}"}.join("\n")]
63
+ end
64
+
65
+ def render_link(link)
66
+ ["text", "<color rgb='#0000ff'><u><link href='#{link[0]}'>#{link[1].join("")}</link></u></color>"]
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,4 @@
1
+ module Bookit
2
+ class Parser
3
+ end
4
+ end
@@ -0,0 +1,47 @@
1
+ require 'nokogiri'
2
+
3
+ module Bookit
4
+ class Parser
5
+ class Html
6
+ def parse(content)
7
+ elements = []
8
+
9
+ doc = Nokogiri::HTML(content)
10
+ elements = walk(doc.root, [])
11
+
12
+ elements.compact
13
+ end
14
+
15
+ def walk(element, tree)
16
+ return tree if element.nil? || (element.content.strip.empty? if element.name != "img")
17
+
18
+ tree << case element.name
19
+ when "p"
20
+ Bookit::Content::Paragraph.new(walk_children(element, []))
21
+ when "text"
22
+ Bookit::Content::Text.new(element.content.strip)
23
+ when "h1", "h2", "h3", "h4"
24
+ Bookit::Content::Header.new(element.content.strip)
25
+ when "a"
26
+ Bookit::Content::Link.new(element.attributes["href"].value, walk_children(element, []))
27
+ when "img"
28
+ attrs = {}
29
+ ['width', 'height'].each {|a| attrs[a.to_sym] = element.attributes[a] ? element.attributes[a].value.to_i : nil}
30
+
31
+ Bookit::Content::Image.new(element.attributes["src"].value, attrs)
32
+ when "ul", "ol"
33
+ Bookit::Content::List.new(walk_children(element, []))
34
+ else
35
+ walk_children(element, tree)
36
+ nil
37
+ end
38
+
39
+ return tree
40
+ end
41
+
42
+ def walk_children(element, tree)
43
+ element.children.inject(tree) {|past, child| walk(child, past)}
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,77 @@
1
+ # super-meta object persistence class to Redis
2
+ require 'redis'
3
+ require 'active_model'
4
+
5
+ module Bookit
6
+ class PersistableObject
7
+ include ActiveModel::Serialization
8
+
9
+ attr_accessor :attributes, :id
10
+ cattr_writer :namespace
11
+
12
+ class << self
13
+ def namespace
14
+ @namespace || self
15
+ end
16
+
17
+ def key(id)
18
+ "#{namespace}:%s" % id
19
+ end
20
+
21
+ # specifically use the generic initializer to rebuild object
22
+ def find(id)
23
+ return nil unless redis.exists key(id)
24
+
25
+ new redis.hgetall(key(id)).to_options
26
+ end
27
+
28
+ def last_id
29
+ redis.get key("LAST_ID")
30
+ end
31
+
32
+ def last
33
+ find last_id
34
+ end
35
+
36
+ def redis(host="localhost", port=6379)
37
+ @redis ||= Redis.new(host: host, port: port)
38
+ end
39
+ end
40
+
41
+ def initialize(params = {})
42
+ @attributes = params
43
+ @id = params[:id] || nil
44
+ end
45
+
46
+ def redis
47
+ self.class.redis
48
+ end
49
+
50
+ def read_attribute_for_validation(key)
51
+ @attributes[key]
52
+ end
53
+ alias :[] :read_attribute_for_validation
54
+
55
+ def key(id=@id)
56
+ self.class.key(id)
57
+ end
58
+
59
+ # store in redis
60
+ def persist
61
+ @attributes[:id] = @id = incr_key
62
+
63
+ redis.hmset key, *serializable_hash.flatten
64
+ self
65
+ end
66
+
67
+ def clear
68
+ redis.del key
69
+ end
70
+
71
+ private
72
+ def incr_key
73
+ redis.incr key("LAST_ID")
74
+ end
75
+ end
76
+ end
77
+
metadata ADDED
@@ -0,0 +1,126 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bookit
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Luke van der Hoeven
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-05-03 00:00:00 -04:00
14
+ default_executable:
15
+ dependencies:
16
+ - !ruby/object:Gem::Dependency
17
+ name: prawn
18
+ prerelease: false
19
+ requirement: &id001 !ruby/object:Gem::Requirement
20
+ none: false
21
+ requirements:
22
+ - - ">="
23
+ - !ruby/object:Gem::Version
24
+ version: "0"
25
+ type: :runtime
26
+ version_requirements: *id001
27
+ - !ruby/object:Gem::Dependency
28
+ name: eeepub
29
+ prerelease: false
30
+ requirement: &id002 !ruby/object:Gem::Requirement
31
+ none: false
32
+ requirements:
33
+ - - ">="
34
+ - !ruby/object:Gem::Version
35
+ version: "0"
36
+ type: :runtime
37
+ version_requirements: *id002
38
+ - !ruby/object:Gem::Dependency
39
+ name: nokogiri
40
+ prerelease: false
41
+ requirement: &id003 !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ">="
45
+ - !ruby/object:Gem::Version
46
+ version: "0"
47
+ type: :runtime
48
+ version_requirements: *id003
49
+ - !ruby/object:Gem::Dependency
50
+ name: redis
51
+ prerelease: false
52
+ requirement: &id004 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ type: :runtime
59
+ version_requirements: *id004
60
+ description: "A quick way to format and output generic book information or articles in multiple formats. Mainly: PDF, EPUB and MOBI"
61
+ email:
62
+ - hungerandthirst@gmail.com
63
+ executables: []
64
+
65
+ extensions: []
66
+
67
+ extra_rdoc_files: []
68
+
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - Rakefile
73
+ - bookit.gemspec
74
+ - examples/article.html
75
+ - examples/article.md
76
+ - examples/article2.html
77
+ - examples/base-article.html
78
+ - examples/base-output.txt
79
+ - lib/bookit.rb
80
+ - lib/bookit/article.rb
81
+ - lib/bookit/content.rb
82
+ - lib/bookit/content/generic.rb
83
+ - lib/bookit/content/header.rb
84
+ - lib/bookit/content/image.rb
85
+ - lib/bookit/content/link.rb
86
+ - lib/bookit/content/list.rb
87
+ - lib/bookit/content/paragraph.rb
88
+ - lib/bookit/content/text.rb
89
+ - lib/bookit/emitter.rb
90
+ - lib/bookit/emitter/abstract.rb
91
+ - lib/bookit/emitter/base.rb
92
+ - lib/bookit/emitter/pdf.rb
93
+ - lib/bookit/parser.rb
94
+ - lib/bookit/parser/html.rb
95
+ - lib/bookit/persistable_object.rb
96
+ - lib/test.pdf
97
+ has_rdoc: true
98
+ homepage: http://github.com/plukevdh/bookit
99
+ licenses: []
100
+
101
+ post_install_message:
102
+ rdoc_options: []
103
+
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: "0"
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: "0"
118
+ requirements: []
119
+
120
+ rubyforge_project: bookit
121
+ rubygems_version: 1.6.2
122
+ signing_key:
123
+ specification_version: 3
124
+ summary: BOOKS WHAT
125
+ test_files: []
126
+