synclenote 0.1.0 → 0.1.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1c352d27660c56aff8d5d3b774d1638375ebb74a422e3ed7406357b4e5a3581e
4
- data.tar.gz: '09925a4b803b45996af5b343aee9d6d851dae75369a8d035dc3a4c3e4707ec4c'
3
+ metadata.gz: 6842ad72e5f7f899d1b5d962b8af0db7cae65c99e16693cedd43de396b51c529
4
+ data.tar.gz: 296010bb3a8b526e43a49986314076bb85be2cd9a7c2cade2fd4a50eb6cf361f
5
5
  SHA512:
6
- metadata.gz: 10752a91ecb12fa3357153e636cb00085c9249d32dc20a3634d4742cce7d464879d0db21c45543e6aef52880fa6afcf0ef2366379edc6cdfdfc8b4a6c440a8fd
7
- data.tar.gz: bbcd3faa70e11247052fdc3d56b35d0adf448ebff62977afdca80a2256be1fe6bbf132952dca7bdc6abe353955b7ec8a6f35c35c6752a370489a2e5ffec2b91d
6
+ metadata.gz: b64e8832799ca114404b838d28fb159d4730072041caf405601d372ada55a25816741486cdcc864e89b4ea8c500a7b4de640d7f5bf26d1c01c4abc5a3beddc92
7
+ data.tar.gz: 7f1a3179d818e0fcff4e3174ce66d517d05e87ed8f31447cbaaa6c80e6ef2888501286c315107ca7050e380fb8be94d1ccd8252a749c0897fda393a89d472821
@@ -0,0 +1,68 @@
1
+ name: test
2
+
3
+ on:
4
+ - pull_request
5
+ - push
6
+ - workflow_dispatch
7
+
8
+ jobs:
9
+ test:
10
+ strategy:
11
+ fail-fast: false
12
+ matrix:
13
+ os:
14
+ - macos
15
+ - ubuntu
16
+ - windows
17
+ ruby_version:
18
+ - 3.3
19
+ - 3.2
20
+ - 3.1
21
+ runs-on: ${{ matrix.os }}-latest
22
+ steps:
23
+ - uses: actions/checkout@v3
24
+ - uses: kenchan0130/actions-system-info@1.2.0
25
+ id: system-info
26
+ - name: Cache rubygems
27
+ uses: actions/cache@v3.2.2
28
+ with:
29
+ path: vendor/bundle
30
+ key: ${{ runner.os }}-${{ steps.system-info.outputs.release }}-rubygems-${{ matrix.ruby_version }}-${{ hashFiles('**/Gemfile') }}-${{ hashFiles('**/*.gemspec') }}
31
+ restore-keys: |
32
+ ${{ runner.os }}-${{ steps.system-info.outputs.release }}-rubygems-${{ matrix.ruby_version }}
33
+ - name: Set up Ruby
34
+ uses: ruby/setup-ruby@v1
35
+ with:
36
+ ruby-version: ${{ matrix.ruby_version }}
37
+ - name: Install dependencies
38
+ shell: bash
39
+ run: |
40
+ set -eux
41
+ gem update --system
42
+ bundle config path vendor/bundle
43
+ bundle install --jobs "${{ steps.system-info.outputs.cpu-core }}" --retry 3
44
+ - name: Run tests
45
+ run: bundle exec rake
46
+ - name: Output versions
47
+ if: always()
48
+ shell: bash
49
+ run: |
50
+ set -eux
51
+ run_if_exist() {
52
+ if test "x$(which $1)" != "x"
53
+ then
54
+ "$@"
55
+ fi
56
+ }
57
+ uname -a
58
+ run_if_exist sw_vers # macos
59
+ run_if_exist lsb_release --all # ubuntu
60
+ ruby --version
61
+ gem --version
62
+ bundle --version
63
+ - name: Output gem versions
64
+ if: always()
65
+ run: bundle list
66
+ - name: Output deb package versions
67
+ if: always() && matrix.os == 'ubuntu'
68
+ run: dpkg -l
data/Rakefile CHANGED
@@ -1,3 +1,9 @@
1
1
  require "bundler/gem_tasks"
2
+ require "rake/testtask"
2
3
 
3
- task default: %i(build)
4
+ Rake::TestTask.new do |t|
5
+ t.libs << "test"
6
+ t.test_files = FileList["test/**/*_test.rb"]
7
+ end
8
+
9
+ task(default: %i[test build])
@@ -1,5 +1,3 @@
1
- require "synclenote"
2
-
3
1
  require "yaml"
4
2
  require "pathname"
5
3
  require "logger"
@@ -137,18 +135,6 @@ EOS
137
135
 
138
136
  EOS
139
137
 
140
- HEADER = <<EOS.freeze
141
- <?xml version="1.0" encoding="UTF-8"?>
142
- <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
143
- <en-note>
144
- EOS
145
-
146
- FOOTER = <<EOS.freeze
147
- </en-note>
148
- EOS
149
-
150
- TITLE_IF_EMPTY = "no title"
151
-
152
138
  def load_yaml_path(path)
153
139
  return YAML.safe_load(
154
140
  path.read,
@@ -183,46 +169,8 @@ EOS
183
169
  return 4 # 4 sec for sandbox
184
170
  end
185
171
 
186
- def build_html(markdown_text)
187
- @formatter ||= Redcarpet::Markdown.new(
188
- Redcarpet::Render::HTML.new(
189
- filter_html: true,
190
- hard_wrap: true,
191
- ),
192
- underline: true,
193
- lax_spacing: true,
194
- footnotes: true,
195
- no_intra_emphasis: true,
196
- superscript: true,
197
- strikethrough: true,
198
- tables: true,
199
- space_after_headers: true,
200
- fenced_code_blocks: true,
201
- # autolink: true,
202
- )
203
- html = @formatter.render(markdown_text)
204
- return html
205
- end
206
-
207
- def create_note(note_path, options = {})
208
- title = nil
209
- tags = ["syncle"]
210
- body = nil
211
- note_path.open do |f|
212
- title = f.gets.chomp.sub(/\A#\s*/, "")
213
- if md = /\A(?<tags>(?:\[.*?\])+)(?:\s|\z)/.match(title)
214
- tags += md[:tags].scan(/\[(.*?)\]/).flatten
215
- title = md.post_match
216
- end
217
- body = f.read
218
- end
219
- html = build_html(body)
220
- content = HEADER +
221
- html.gsub(/ class=\".*?\"/, "").gsub(/<(br|hr|img).*?>/, "\\&</\\1>") +
222
- FOOTER
223
- title = TITLE_IF_EMPTY if /\A\s*\z/ === title
224
- o = options.merge(title: title, content: content, tagNames: tags)
225
- return Evernote::EDAM::Type::Note.new(o)
172
+ def create_note(note_path, **options)
173
+ return Synclenote::NoteBuilder.(note_path, **options)
226
174
  end
227
175
 
228
176
  def target_note?(note)
@@ -1,5 +1,3 @@
1
- require "synclenote"
2
-
3
1
  require "ostruct"
4
2
 
5
3
  module Synclenote::Configuration
@@ -0,0 +1,52 @@
1
+ require "nokogiri"
2
+ require "redcarpet"
3
+
4
+ class Synclenote::MarkdownToEnmlBuilder
5
+ HEADER = <<~EOS
6
+ <?xml version="1.0" encoding="UTF-8"?>
7
+ <!DOCTYPE en-note SYSTEM "http://xml.evernote.com/pub/enml2.dtd">
8
+ <en-note>
9
+ EOS
10
+
11
+ FOOTER = <<~EOS
12
+ </en-note>
13
+ EOS
14
+
15
+ class << self
16
+ def call(markdown_text)
17
+ formatter = Redcarpet::Markdown.new(
18
+ Redcarpet::Render::HTML.new(
19
+ filter_html: true,
20
+ hard_wrap: true,
21
+ ),
22
+ underline: true,
23
+ lax_spacing: true,
24
+ footnotes: true,
25
+ no_intra_emphasis: true,
26
+ superscript: true,
27
+ strikethrough: true,
28
+ tables: true,
29
+ space_after_headers: true,
30
+ fenced_code_blocks: true,
31
+ # autolink: true,
32
+ )
33
+ html = formatter.render(markdown_text)
34
+ root = Nokogiri::HTML::DocumentFragment.parse(html)
35
+
36
+ root.css("*[class]").each do |node|
37
+ node.remove_attribute("class")
38
+ end
39
+
40
+ # '<a href="example.html?foo=bar&baz=quux">'
41
+ # =>
42
+ # '<a href="example.html?foo=bar&amp;baz=quux">'
43
+ treated_html = root.to_html
44
+ content = [
45
+ HEADER,
46
+ treated_html.gsub(/<(br|hr|img).*?>/, "\\&</\\1>"),
47
+ FOOTER,
48
+ ].join
49
+ return content
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,41 @@
1
+ module Synclenote::NoteBuilder
2
+ DEFAULT_TAGS = %w[syncle]
3
+
4
+ TITLE_IF_EMPTY = "no title"
5
+
6
+ class << self
7
+ def call(raw_note, guid: nil)
8
+ lines = raw_note.each_line.lazy
9
+ title, tags_in_raw_note, body_markdown = *parse_lines(lines)
10
+ title = TITLE_IF_EMPTY if /\A\s*\z/ === title
11
+ tags = (DEFAULT_TAGS + tags_in_raw_note).uniq
12
+ enml = Synclenote::MarkdownToEnmlBuilder.(body_markdown)
13
+
14
+ options = {
15
+ title:,
16
+ content: enml,
17
+ tagNames: tags,
18
+ }
19
+ options[:guid] = guid if guid
20
+ return Evernote::EDAM::Type::Note.new(options)
21
+ end
22
+
23
+ private
24
+
25
+ def parse_lines(lines)
26
+ tags = []
27
+ title = lines.first.chomp.sub(/\A#\s*/, "")
28
+ lines = lines.drop(1)
29
+ md = /\A(?<tags>(?:\[.*?\])+)(?:\s|\z)/.match(title)
30
+ if md
31
+ tags = md[:tags].scan(/\[(.*?)\]/).flatten
32
+ title = md.post_match
33
+ end
34
+
35
+ lines = lines.drop_while { |l| /\A\s*\z/ === l }
36
+ body_markdown = lines.to_a.join
37
+
38
+ return [title, tags, body_markdown]
39
+ end
40
+ end
41
+ end
@@ -1,3 +1,3 @@
1
1
  module Synclenote
2
- VERSION = "0.1.0"
2
+ VERSION = "0.1.1"
3
3
  end
data/lib/synclenote.rb CHANGED
@@ -1,12 +1,14 @@
1
1
  # This module is a namespace for this gem.
2
2
  module Synclenote
3
3
  DRY_RUN = false
4
- autoload :VERSION, "synclenote/varsion"
5
-
6
- autoload :Command, "synclenote/command"
7
- autoload :Configuration, "synclenote/configuration"
8
4
 
9
5
  def self.configure(version, &block)
10
6
  Configuration.run(version, &block)
11
7
  end
12
8
  end
9
+
10
+ (Pathname(__dir__).glob("**/*.rb") - [Pathname(__FILE__)]).map { |path|
11
+ path.sub_ext("")
12
+ }.sort.each do |path|
13
+ require(path.relative_path_from(__dir__))
14
+ end
data/synclenote.gemspec CHANGED
@@ -24,10 +24,13 @@ Gem::Specification.new do |spec|
24
24
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
25
25
  spec.require_paths = ["lib"]
26
26
 
27
+ spec.add_runtime_dependency "base64" # suppress warnings
27
28
  spec.add_runtime_dependency "evernote_oauth"
29
+ spec.add_runtime_dependency "nokogiri"
28
30
  spec.add_runtime_dependency "redcarpet"
29
31
  spec.add_runtime_dependency "thor"
30
32
  spec.add_development_dependency "bundler"
31
33
  spec.add_development_dependency "debug"
32
34
  spec.add_development_dependency "rake"
35
+ spec.add_development_dependency "test-unit"
33
36
  end
@@ -0,0 +1,58 @@
1
+ require "test_helper"
2
+
3
+ class Synclenote::MarkdownToEnmlBuilderTest < Test::Unit::TestCase
4
+ sub_test_case(".call") do
5
+ data(
6
+ regular_text: {
7
+ markdown_text: <<~EOS,
8
+ body
9
+ EOS
10
+ expected_enml_text: <<~EOS,
11
+ <p>body</p>
12
+ EOS
13
+ },
14
+ fenced_code_blocks: {
15
+ markdown_text: <<~EOS,
16
+ ```ruby
17
+ 1 + 2 * 3
18
+ ```
19
+ EOS
20
+ # remove "class" attribute
21
+ expected_enml_text: <<~EOS,
22
+ <pre><code>1 + 2 * 3
23
+ </code></pre>
24
+ EOS
25
+ },
26
+ hard_wrap: {
27
+ markdown_text: <<~EOS,
28
+ abc
29
+ def
30
+ EOS
31
+ # convert "<br/>" to "<br></br>"
32
+ expected_enml_text: <<~EOS,
33
+ <p>abc<br></br>
34
+ def</p>
35
+ EOS
36
+ },
37
+ hyperlink: {
38
+ markdown_text: <<~EOS,
39
+ [link text](https://example.org/?foo=bar&baz=quux;hoge=fuga#anchor)
40
+ EOS
41
+ # convert "&" to "&amp;"
42
+ expected_enml_text: <<~EOS,
43
+ <p><a href="https://example.org/?foo=bar&amp;baz=quux;hoge=fuga#anchor">link text</a></p>
44
+ EOS
45
+ },
46
+ )
47
+ test("returns ENML String") do |h|
48
+ assert_equal(
49
+ [
50
+ Synclenote::MarkdownToEnmlBuilder::HEADER,
51
+ h[:expected_enml_text],
52
+ Synclenote::MarkdownToEnmlBuilder::FOOTER,
53
+ ].join,
54
+ Synclenote::MarkdownToEnmlBuilder.(h[:markdown_text]),
55
+ )
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,68 @@
1
+ require "test_helper"
2
+
3
+ class Synclenote::NoteBuilderTest < Test::Unit::TestCase
4
+ sub_test_case(".call") do
5
+ data(
6
+ regular: {
7
+ raw_note_text: <<~EOS,
8
+ # title
9
+
10
+ body
11
+ EOS
12
+ expected_title: "title",
13
+ expected_additional_tag_names: [],
14
+ expected_content_body: <<~EOS,
15
+ <p>body</p>
16
+ EOS
17
+ },
18
+ no_title: {
19
+ raw_note_text: <<~EOS,
20
+ # \t
21
+
22
+ body
23
+ EOS
24
+ expected_title: Synclenote::NoteBuilder::TITLE_IF_EMPTY,
25
+ expected_additional_tag_names: [],
26
+ expected_content_body: <<~EOS,
27
+ <p>body</p>
28
+ EOS
29
+ },
30
+ have_tags: {
31
+ raw_note_text: <<~EOS,
32
+ # [foo][bar-baz] title
33
+
34
+ body
35
+ EOS
36
+ expected_title: "title",
37
+ expected_additional_tag_names: %w[foo bar-baz],
38
+ expected_content_body: <<~EOS,
39
+ <p>body</p>
40
+ EOS
41
+ },
42
+ )
43
+ test("returns Note object") do |h|
44
+ Tempfile.create do |f|
45
+ f.write(h[:raw_note_text])
46
+ f.close
47
+ raw_note_path = Pathname(f.path)
48
+ note = Synclenote::NoteBuilder.(raw_note_path)
49
+ assert_equal(h[:expected_title], note.title)
50
+ assert_equal(
51
+ [
52
+ *Synclenote::NoteBuilder::DEFAULT_TAGS,
53
+ *h[:expected_additional_tag_names],
54
+ ].sort,
55
+ note.tagNames.sort,
56
+ )
57
+ assert_equal(
58
+ [
59
+ Synclenote::MarkdownToEnmlBuilder::HEADER,
60
+ h[:expected_content_body],
61
+ Synclenote::MarkdownToEnmlBuilder::FOOTER,
62
+ ].join,
63
+ note.content,
64
+ )
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,3 @@
1
+ require "test/unit"
2
+
3
+ require "synclenote"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: synclenote
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuya.Nishida.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-01-13 00:00:00.000000000 Z
11
+ date: 2024-01-18 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: base64
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: evernote_oauth
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -24,6 +38,20 @@ dependencies:
24
38
  - - ">="
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: nokogiri
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
27
55
  - !ruby/object:Gem::Dependency
28
56
  name: redcarpet
29
57
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +122,20 @@ dependencies:
94
122
  - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: test-unit
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
97
139
  description: A synchronization tool for GFM (GitHub Flavored Markdown) files and Evernote.
98
140
  email:
99
141
  - yuya@j96.org
@@ -103,6 +145,7 @@ extensions: []
103
145
  extra_rdoc_files: []
104
146
  files:
105
147
  - ".github/dependabot.yml"
148
+ - ".github/workflows/test.yml"
106
149
  - ".gitignore"
107
150
  - ".travis.yml"
108
151
  - Gemfile
@@ -114,8 +157,13 @@ files:
114
157
  - lib/synclenote.rb
115
158
  - lib/synclenote/command.rb
116
159
  - lib/synclenote/configuration.rb
160
+ - lib/synclenote/markdown_to_enml_builder.rb
161
+ - lib/synclenote/note_builder.rb
117
162
  - lib/synclenote/version.rb
118
163
  - synclenote.gemspec
164
+ - test/lib/synclenote/markdown_to_enml_builder_test.rb
165
+ - test/lib/synclenote/note_builder_test.rb
166
+ - test/test_helper.rb
119
167
  homepage: https://github.com/nishidayuya/synclenote
120
168
  licenses:
121
169
  - X11
@@ -139,4 +187,7 @@ rubygems_version: 3.5.3
139
187
  signing_key:
140
188
  specification_version: 4
141
189
  summary: A synchronization tool for GFM (GitHub Flavored Markdown) files and Evernote.
142
- test_files: []
190
+ test_files:
191
+ - test/lib/synclenote/markdown_to_enml_builder_test.rb
192
+ - test/lib/synclenote/note_builder_test.rb
193
+ - test/test_helper.rb