enwrite 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 29b7e3af7cfaaa08cf0be33569aa558a35daa502
4
+ data.tar.gz: 4ab795bb5115e458b772903e303318270f6d22bb
5
+ SHA512:
6
+ metadata.gz: e3db52be55a5648a98440ef78d7e6a6a7e4de825f81a87a60cf4620049666deefe81a02b478a31fadd8159ae7db41caf492fe90031e8b9fb28338769686a19db
7
+ data.tar.gz: ba8b5c411395d07ed358304346b81915b6e1e942ef962bb0c700e9b3513b2c3448f6e03f846e6e61449047eff00da70df5e08d3a1c0066ee1904e8a09d510174
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ lib/**/*.rb
2
+ bin/*
3
+ -
4
+ features/**/*.feature
5
+ LICENSE.txt
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ gem "colorize", "~> 0.7"
7
+ gem "deep_merge", "~> 1.0"
8
+ gem "evernote-thrift", "~> 1.25"
9
+ gem "evernote_oauth", "~> 0.2"
10
+ gem "htmlentities", "~> 4.3"
11
+
12
+ # Add dependencies to develop your gem here.
13
+ # Include everything needed to run rake, tests, features, etc.
14
+ group :development do
15
+ gem "rdoc", "~> 3.12"
16
+ gem "bundler", "~> 1.0"
17
+ gem "jeweler", "~> 2.0"
18
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,69 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ addressable (2.3.8)
5
+ builder (3.2.2)
6
+ colorize (0.7.5)
7
+ deep_merge (1.0.1)
8
+ descendants_tracker (0.0.4)
9
+ thread_safe (~> 0.3, >= 0.3.1)
10
+ evernote-thrift (1.25.1)
11
+ evernote_oauth (0.2.3)
12
+ evernote-thrift
13
+ oauth (>= 0.4.1)
14
+ faraday (0.9.1)
15
+ multipart-post (>= 1.2, < 3)
16
+ git (1.2.9.1)
17
+ github_api (0.12.3)
18
+ addressable (~> 2.3)
19
+ descendants_tracker (~> 0.0.4)
20
+ faraday (~> 0.8, < 0.10)
21
+ hashie (>= 3.3)
22
+ multi_json (>= 1.7.5, < 2.0)
23
+ nokogiri (~> 1.6.3)
24
+ oauth2
25
+ hashie (3.4.1)
26
+ highline (1.7.2)
27
+ htmlentities (4.3.3)
28
+ jeweler (2.0.1)
29
+ builder
30
+ bundler (>= 1.0)
31
+ git (>= 1.2.5)
32
+ github_api
33
+ highline (>= 1.6.15)
34
+ nokogiri (>= 1.5.10)
35
+ rake
36
+ rdoc
37
+ json (1.8.2)
38
+ jwt (1.4.1)
39
+ mini_portile (0.6.2)
40
+ multi_json (1.11.0)
41
+ multi_xml (0.5.5)
42
+ multipart-post (2.0.0)
43
+ nokogiri (1.6.6.2)
44
+ mini_portile (~> 0.6.0)
45
+ oauth (0.4.7)
46
+ oauth2 (1.0.0)
47
+ faraday (>= 0.8, < 0.10)
48
+ jwt (~> 1.0)
49
+ multi_json (~> 1.3)
50
+ multi_xml (~> 0.5)
51
+ rack (~> 1.2)
52
+ rack (1.6.0)
53
+ rake (10.4.2)
54
+ rdoc (3.12.2)
55
+ json (~> 1.4)
56
+ thread_safe (0.3.5)
57
+
58
+ PLATFORMS
59
+ ruby
60
+
61
+ DEPENDENCIES
62
+ bundler (~> 1.0)
63
+ colorize (~> 0.7)
64
+ deep_merge (~> 1.0)
65
+ evernote-thrift (~> 1.25)
66
+ evernote_oauth (~> 0.2)
67
+ htmlentities (~> 4.3)
68
+ jeweler (~> 2.0)
69
+ rdoc (~> 3.12)
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Diego Zamboni
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
22
+
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2015 Diego Zamboni
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # Enwrite
2
+
3
+ > What wild heart-histories seemed to lie enwritten<br/>
4
+ > Upon those crystalline, celestial spheres!
5
+ <p align="right">&mdash;Edgar Allan Poe</p>
6
+
7
+ Evernote-powered statically-generated blogs and websites.
8
+
9
+ Still work-in-progress but functional, more docs to come soon.
10
+
11
+ The first time you run it (or if you use the `--auth` flag afterward)
12
+ you will be asked to open an Evernote authentication page, and then to
13
+ provide the authentication code to Enwrite.
14
+
15
+ For now it produces output suitable for [Hugo](http://gohugo.io). You
16
+ need to have an existing Hugo install.
17
+
18
+ ## Getting started
19
+
20
+ Install using gem:
21
+
22
+ $ gem install enwrite
23
+
24
+ Create a new Hugo site for testing (if you don't have one already):
25
+
26
+ $ cd ~/tmp
27
+ $ hugo new site my-hugo-blog
28
+ $ mkdir my-hugo-blog/themes; git clone https://github.com/zyro/hyde-x.git my-hugo-blog/themes/hyde-x
29
+
30
+ Populate it with contents from Evernote:
31
+
32
+ $ enwrite --help
33
+ Enwrite v0.2.0
34
+
35
+ Usage: /usr/local/bin/enwrite [options] (at least one of -n or -s has to be specified)
36
+
37
+ Search options:
38
+ -n, --notebook NOTEBOOK Process notes from specified notebook.
39
+ -t, --tag TAG Process only notes that have this tag
40
+ within the given notebook.
41
+ --remove-tags [t1,t2,t3] List of tags to remove from output posts.
42
+ If no argument given, defaults to --tag.
43
+ -s, --search SEARCHEXP Process notes that match given search
44
+ expression. If specified, --notebook
45
+ and --tag are ignored.
46
+ Output options:
47
+ -p, --output-plugin PLUGIN Output plugin to use (Valid values: hugo)
48
+ -o, --output-dir OUTDIR Base dir of hugo output installation
49
+ --rebuild-all Process all notes that match the given
50
+ conditions (normally only updated notes
51
+ are processed)
52
+ Other options:
53
+ --auth [TOKEN] Force Evernote reauthentication (will
54
+ happen automatically if needed). Use
55
+ TOKEN if given, otherwise get one
56
+ interactively.
57
+ --config-tag TAG Specify tag to determine config notes
58
+ (default: _enwrite_config)
59
+ --verbose Verbose mode
60
+ -v, --debug Debug output mode
61
+ --version Show version
62
+ -h, --help Shows this help message
63
+
64
+ Generate posts from all notes tagged `published` in notebook
65
+ `my_notebook`:
66
+
67
+ $ enwrite -n my_notebook -t published -o ~/tmp/my-hugo-blog
68
+ $ cd ~/tmp/my-hugo-blog
69
+ $ hugo server --watch
70
+
71
+ Generate posts from all notes matching `some search expression`:
72
+
73
+ $ enwrite -s 'some search expression' -o /tmp/my-hugo-blog
74
+
75
+ Images, audio and video are embedded in the generated posts (audio
76
+ and video are done using HTML5 `<audio>` and `<video>` tags). Other
77
+ file types are stored and linked to with their filename.
78
+
79
+ The following shortcuts are recognized:
80
+
81
+ Embed Youtube video by URL or ID. You can optionally specify `width`
82
+ and `height`. All arguments must be enclosed in double quotes.
83
+
84
+ [youtube url="https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
85
+ [youtube src="https://www.youtube.com/watch?v=dQw4w9WgXcQ"]
86
+ [youtube id="dQw4w9WgXcQ" width="640px" height="480px"]
87
+
88
+ Embed gist:
89
+
90
+ [gist url="https://gist.github.com/zzamboni/843142d3f759e582fe8f"]
data/Rakefile ADDED
@@ -0,0 +1,56 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ begin
6
+ Bundler.setup(:default, :development)
7
+ rescue Bundler::BundlerError => e
8
+ $stderr.puts e.message
9
+ $stderr.puts "Run `bundle install` to install missing gems"
10
+ exit e.status_code
11
+ end
12
+ require 'rake'
13
+ require './lib/enwrite.rb'
14
+
15
+ require 'jeweler'
16
+ Jeweler::Tasks.new do |gem|
17
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
18
+ gem.name = "enwrite"
19
+ gem.homepage = "http://github.com/zzamboni/enwrite"
20
+ gem.license = "MIT"
21
+ gem.summary = %Q{Enwrite: Power a web site using Evernote}
22
+ gem.description = %Q{Enwrite allows you to generate a website from contents stored in Evernote.
23
+ At the moment only Hugo (http://gohugo.io) is supported as an output format,
24
+ but others can be added through plugins.}
25
+ gem.email = "diego@zzamboni.org"
26
+ gem.authors = ["Diego Zamboni"]
27
+ # dependencies defined in Gemfile
28
+ gem.executables = [ 'enwrite' ]
29
+ gem.version = Enwrite::Version::STRING
30
+ end
31
+ Jeweler::RubygemsDotOrgTasks.new
32
+
33
+ require 'rake/testtask'
34
+ Rake::TestTask.new(:test) do |test|
35
+ test.libs << 'lib' << 'test'
36
+ test.pattern = 'test/**/test_*.rb'
37
+ test.verbose = true
38
+ end
39
+
40
+ desc "Code coverage detail"
41
+ task :simplecov do
42
+ ENV['COVERAGE'] = "true"
43
+ Rake::Task['test'].execute
44
+ end
45
+
46
+ task :default => :test
47
+
48
+ require 'rdoc/task'
49
+ Rake::RDocTask.new do |rdoc|
50
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
51
+
52
+ rdoc.rdoc_dir = 'rdoc'
53
+ rdoc.title = "enwrite #{version}"
54
+ rdoc.rdoc_files.include('README*')
55
+ rdoc.rdoc_files.include('lib/**/*.rb')
56
+ end
data/bin/enwrite ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ #
4
+ # enwrite - power a web site using Evernote
5
+ #
6
+ # Diego Zamboni, March 2015
7
+ # Time-stamp: <2015-04-28 13:26:56 diego>
8
+
9
+ require 'enwrite'
10
+
11
+ Enwrite.run
data/lib/enml-utils.rb ADDED
@@ -0,0 +1,196 @@
1
+ # coding: utf-8
2
+ #
3
+ # ENML Processing class
4
+ #
5
+ # Diego Zamboni, March 2015
6
+ # Time-stamp: <2015-04-30 00:45:35 diego>
7
+
8
+ require 'digest'
9
+ require 'htmlentities'
10
+ require 'rexml/parsers/sax2parser'
11
+ require 'rexml/sax2listener'
12
+ require 'rexml/document'
13
+ require 'rexml/element'
14
+ require 'util'
15
+ include REXML
16
+
17
+ class ENML_Listener
18
+ include REXML::SAX2Listener
19
+
20
+ def initialize(resources, to_text, static_dirs, note_guid)
21
+ @to_text = to_text
22
+ @files = []
23
+ @resources = resources || []
24
+ @dirs = static_dirs
25
+ @note_guid = note_guid
26
+ @resource_index = {}
27
+ @resources.each do |res|
28
+ hash = Digest.hexencode(res.data.bodyHash)
29
+ @resource_index[hash] = res
30
+ verbose "Stored index for resource with hash #{hash}"
31
+ end
32
+ end
33
+
34
+ def start_document
35
+ @outdoc = Document.new
36
+ @stack = [@outdoc]
37
+ end
38
+
39
+ def process_file(attributes, type, subtype)
40
+ resource = @resource_index[attributes['hash']]
41
+ if resource.nil?
42
+ error "An error occurred - I don't have a resource with hash #{attributes['hash']}"
43
+ return nil
44
+ else
45
+ new_file = {}
46
+ if (!resource.attributes.nil? && !resource.attributes.fileName.nil?)
47
+ new_file[:basename] = resource.attributes.fileName
48
+ else
49
+ new_file[:basename] = "#{attributes['hash']}.#{subtype}"
50
+ end
51
+ if @dirs[type]
52
+ new_file[:fname] = "#{@dirs[type][0]}/#{attributes['hash']}/#{new_file[:basename]}"
53
+ new_file[:url] = "#{@dirs[type][1]}/#{attributes['hash']}/#{new_file[:basename]}"
54
+ end
55
+ new_file[:data] = resource.data.body
56
+ return new_file
57
+ end
58
+ end
59
+
60
+ def start_element(uri, localname, qname, attributes)
61
+ new_elem = nil
62
+ if localname == 'en-note'
63
+ # Convert <en-note> to <span>
64
+ new_elem = Element.new('span')
65
+ new_elem.add_attributes(attributes)
66
+ elsif localname == 'en-todo'
67
+ unless @to_text
68
+ new_elem = Element.new('input')
69
+ new_elem.add_attribute('type', 'checkbox')
70
+ if attributes and attributes['checked'] == 'true'
71
+ new_elem.add_attribute('checked', 'checked')
72
+ end
73
+ else
74
+ if attributes and attributes['checked'] == 'true'
75
+ @stack[-1].add_text("[x] ")
76
+ else
77
+ @stack[-1].add_text("[ ] ")
78
+ end
79
+ end
80
+ elsif localname == 'en-media'
81
+ if attributes['type'] =~ /^image\/(.+)/
82
+ subtype = $1
83
+ new_file = process_file(attributes, 'image', subtype)
84
+ if new_file
85
+ new_elem = Element.new('img')
86
+ new_elem.add_attributes(attributes)
87
+ new_elem.add_attribute('src', new_file[:url])
88
+ @files.push(new_file)
89
+ end
90
+ elsif attributes['type'] =~ /^(audio|video)\/(.*)/
91
+ type = $1
92
+ subtype = $2
93
+ new_file = process_file(attributes, type, subtype)
94
+ if new_file
95
+ new_elem = Element.new(type)
96
+ new_elem.add_attribute('controls', "1")
97
+ new_elem.add_element 'source', { 'src' => new_file[:url], 'type' => attributes['type'] }
98
+ new_elem.add_text "Sorry, your browser does not support the #{type} tag."
99
+ @files.push(new_file)
100
+ end
101
+ elsif attributes['type'] =~ /^(\S+)\/(\S+)/
102
+ type = $1
103
+ subtype = $2
104
+ new_file = process_file(attributes, 'files', subtype)
105
+ if new_file
106
+ new_elem = Element.new('a')
107
+ new_elem.add_attribute('href', new_file[:url])
108
+ new_elem.add_text(new_file[:basename])
109
+ @files.push(new_file)
110
+ end
111
+ else
112
+ error "Sorry, I don't know how to handle attachments of this type: #{attributes['type']}"
113
+ end
114
+ else
115
+ new_elem = Element.new(localname)
116
+ new_elem.add_attributes(attributes)
117
+ end
118
+ @stack.push(new_elem)
119
+ end
120
+
121
+ def end_element(uri, localname, qname)
122
+ new_elem = @stack.pop
123
+ @stack[-1].add_element(new_elem) unless new_elem.nil?
124
+ end
125
+
126
+ def characters(text)
127
+ @stack[-1].add_text(text)
128
+ end
129
+
130
+ def end_document
131
+ @output = ""
132
+ @stack[-1].write(@output)
133
+ decoder = HTMLEntities.new
134
+ # One pass of entity decoding for HTML output...
135
+ @output = decoder.decode(@output)
136
+ if @to_text
137
+ # Clean up some tags, extra empty lines, prettyfied characters, etc.
138
+ @output.gsub!(/<\/?span[^>]*>/, '')
139
+ @output.gsub!(/\t*<div[^>]*>/, '')
140
+ @output.gsub!(/<\/div>/, "\n")
141
+ @output.gsub!(/^\s+$/, '')
142
+ @output.gsub!(/\n+/, "\n")
143
+ @output.gsub!(/<br[^>]*\/>/, "\n")
144
+ @output.gsub!(/“/, '"')
145
+ @output.gsub!(/”/, '"')
146
+ @output.gsub!(/‘/, "'")
147
+ @output.gsub!(/’/, "'")
148
+ @output.gsub!(/\u{a0}/, " ") # Unicode non-breaking space
149
+ # ...two passes of decoding for text output.
150
+ @output = decoder.decode(@output)
151
+ end
152
+ end
153
+
154
+ def output
155
+ @output
156
+ end
157
+ def files
158
+ @files
159
+ end
160
+ end
161
+
162
+ class ENML_utils
163
+ def initialize(text, resources = nil, static_dirs = {}, note_guid = "")
164
+ @text = text or ""
165
+ @resources = resources or []
166
+ @static_dirs = static_dirs
167
+ @note_guid = note_guid
168
+ @parsed = false
169
+ end
170
+
171
+ def to_html(to_text=false)
172
+ parser = Parsers::SAX2Parser.new( @text )
173
+ debug "to_html input text:"
174
+ debug "-----"
175
+ debug @text
176
+ debug "-----"
177
+ listener = ENML_Listener.new(@resources, to_text, @static_dirs, @note_guid)
178
+ parser.listen(listener)
179
+ parser.parse
180
+ @files = listener.files
181
+ @parsed = true
182
+ debug "to_html output:"
183
+ debug listener.output
184
+ debug "-----"
185
+ return listener.output
186
+ end
187
+
188
+ def to_text
189
+ return to_html(true)
190
+ end
191
+
192
+ def resource_files
193
+ to_html unless @parsed
194
+ return @files
195
+ end
196
+ end