enwrite 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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