bibsync 0.0.1 → 0.0.2

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/.travis.yml +10 -0
  3. data/Gemfile +3 -0
  4. data/LICENSE +21 -0
  5. data/README.md +88 -0
  6. data/Rakefile +16 -0
  7. data/bibsync.gemspec +4 -2
  8. data/lib/bibsync/actions/{check_versions.rb → check_arxiv_versions.rb} +6 -6
  9. data/lib/bibsync/actions/determine_arxiv_doi.rb +70 -0
  10. data/lib/bibsync/actions/fetch_from_arxiv.rb +11 -9
  11. data/lib/bibsync/actions/find_my_citations.rb +4 -4
  12. data/lib/bibsync/actions/jabref_format.rb +2 -2
  13. data/lib/bibsync/actions/synchronize_files.rb +5 -6
  14. data/lib/bibsync/actions/synchronize_metadata.rb +14 -57
  15. data/lib/bibsync/actions/validate.rb +16 -6
  16. data/lib/bibsync/actions.rb +1 -7
  17. data/lib/bibsync/bibliography.rb +60 -23
  18. data/lib/bibsync/command.rb +13 -8
  19. data/lib/bibsync/log.rb +22 -20
  20. data/lib/bibsync/transformer.rb +1 -1
  21. data/lib/bibsync/utils.rb +7 -9
  22. data/lib/bibsync/version.rb +1 -1
  23. data/test/actions/test_check_arxiv_versions.rb +4 -0
  24. data/test/actions/test_determine_arxiv_doi.rb +61 -0
  25. data/test/actions/test_fetch_from_arxiv.rb +4 -0
  26. data/test/actions/test_find_my_citations.rb +4 -0
  27. data/test/actions/test_jabref_format.rb +4 -0
  28. data/test/actions/test_synchronize_files.rb +4 -0
  29. data/test/actions/test_synchronize_metadata.rb +34 -0
  30. data/test/actions/test_validate.rb +4 -0
  31. data/test/fixture/FileWithEmbeddedArXiv.pdf +0 -0
  32. data/test/fixture/FileWithEmbeddedArXiv.tex +7 -0
  33. data/test/fixture/FileWithEmbeddedDOI.pdf +0 -0
  34. data/test/fixture/FileWithEmbeddedDOI.tex +7 -0
  35. data/test/fixture/entry.bib +8 -0
  36. data/test/fixture/test.bib +34 -0
  37. data/test/helper.rb +21 -0
  38. data/test/test_bibliography.rb +222 -0
  39. data/test/test_utils.rb +54 -0
  40. metadata +63 -16
@@ -3,16 +3,11 @@ module BibSync
3
3
  include Enumerable
4
4
 
5
5
  attr_reader :file
6
+ attr_accessor :save_hook
6
7
 
7
8
  def initialize(file = nil)
8
- @entries, @file = {}, file
9
- parse(File.read(@file)) if @file && File.exists?(@file)
10
- @dirty = false
11
- @save_hooks = []
12
- end
13
-
14
- def save_hook(hook)
15
- @save_hooks << hook
9
+ @entries, @save_hook = {}, nil
10
+ load(file)
16
11
  end
17
12
 
18
13
  def dirty?
@@ -23,6 +18,14 @@ module BibSync
23
18
  @dirty = true
24
19
  end
25
20
 
21
+ def empty?
22
+ @entries.empty?
23
+ end
24
+
25
+ def size
26
+ @entries.size
27
+ end
28
+
26
29
  def [](key)
27
30
  @entries[key]
28
31
  end
@@ -35,10 +38,17 @@ module BibSync
35
38
  end
36
39
  end
37
40
 
41
+ def clear
42
+ unless @entries.empty?
43
+ @entries.clear
44
+ dirty!
45
+ end
46
+ end
47
+
38
48
  def relative_path(file)
39
49
  raise 'No filename given' unless @file
40
- bibpath = Pathname.new(@file).realpath.parent
41
- Pathname.new(file).realpath.relative_path_from(bibpath).to_s
50
+ bibpath = File.absolute_path(File.dirname(@file))
51
+ Pathname.new(file).realpath.relative_path_from(Pathname.new(bibpath)).to_s
42
52
  end
43
53
 
44
54
  def each(&block)
@@ -49,12 +59,12 @@ module BibSync
49
59
  if file
50
60
  @file = file
51
61
  @parent_path = nil
52
- @dirty = true
62
+ dirty!
53
63
  end
54
64
 
55
65
  raise 'No filename given' unless @file
56
66
  if @dirty
57
- @save_hooks.each {|hook| hook.call(self) }
67
+ @save_hook.call(self) if @save_hook
58
68
  File.open("#{@file}.tmp", 'w') {|f| f.write(self) }
59
69
  File.rename("#{@file}.tmp", @file)
60
70
  @dirty = false
@@ -65,11 +75,25 @@ module BibSync
65
75
  end
66
76
 
67
77
  def <<(entry)
78
+ raise 'Entry has no key' if !entry.key || entry.key.empty?
79
+ raise 'Entry is already existing' if @entries.include?(entry.key)
68
80
  entry.bibliography = self
69
81
  @entries[entry.key] = entry
70
82
  dirty!
71
83
  end
72
84
 
85
+ def load(file)
86
+ parse(File.read(file)) if file && File.exists?(file)
87
+ @file = file
88
+ @dirty = false
89
+ end
90
+
91
+ def load!(file)
92
+ parse(File.read(file))
93
+ @file = file
94
+ @dirty = false
95
+ end
96
+
73
97
  def parse(text)
74
98
  until text.empty?
75
99
  case text
@@ -94,21 +118,34 @@ module BibSync
94
118
  class Entry
95
119
  include Enumerable
96
120
 
97
- attr_accessor :key, :type, :bibliography
121
+ attr_accessor :bibliography, :type
122
+ attr_reader :key
98
123
 
99
124
  def self.parse(text)
100
- entry = Entry.new
101
- entry.parse(text)
102
- entry
125
+ Entry.new.tap {|e| e.parse(text) }
126
+ end
127
+
128
+ def initialize(fields = {})
129
+ self.key = fields.delete(:key) if fields.include?(:key)
130
+ self.type = fields.delete(:type) if fields.include?(:type)
131
+ @fields = fields
103
132
  end
104
133
 
105
- def initialize
106
- @fields = {}
134
+ def key=(key)
135
+ key = key.to_s
136
+ raise 'Key cannot be empty' if key.empty?
137
+ if bib = bibliography
138
+ bib.delete(self)
139
+ @key = key
140
+ bib << self
141
+ else
142
+ @key = key
143
+ end
107
144
  end
108
145
 
109
146
  def file=(file)
110
147
  raise 'No bibliography set' unless bibliography
111
- file =~ /\.(\w+)$/
148
+ file =~ /\.(\w+)\Z/
112
149
  self[:file] = ":#{bibliography.relative_path(file)}:#{$1.upcase}" # JabRef file format "description:path:type"
113
150
  file
114
151
  end
@@ -116,9 +153,9 @@ module BibSync
116
153
  def file
117
154
  if self[:file]
118
155
  raise 'No bibliography set' unless bibliography
119
- description, file, type = self[:file].split(':', 3)
120
- path = (Pathname.new(bibliography.file).realpath.parent + file).to_s
121
- { :name => File.basename(path), :type => type.upcase.to_sym, :path => path }
156
+ _, file, type = self[:file].split(':', 3)
157
+ path = File.join(File.absolute_path(File.dirname(bibliography.file)), file)
158
+ { name: File.basename(path), type: type.upcase.to_sym, path: path }
122
159
  end
123
160
  end
124
161
 
@@ -127,7 +164,7 @@ module BibSync
127
164
  end
128
165
 
129
166
  def []=(key, value)
130
- if value then
167
+ if value
131
168
  key = convert_key(key)
132
169
  value = RawValue === value ? RawValue.new(value.to_s.strip) : value.to_s.strip
133
170
  if @fields[key] != value || @fields[key].class != value.class
@@ -14,7 +14,7 @@ module BibSync
14
14
  process
15
15
  exit 0
16
16
  rescue Exception => ex
17
- raise ex if Log.trace? || SystemExit === ex
17
+ raise ex if Log.trace || SystemExit === ex
18
18
  $stderr.print "#{ex.class}: " if ex.class != RuntimeError
19
19
  $stderr.puts ex.message
20
20
  $stderr.puts ' Use --trace for backtrace.'
@@ -63,11 +63,11 @@ module BibSync
63
63
  end
64
64
 
65
65
  opts.on('-V', '--verbose', 'Verbose output') do
66
- Log.verbose!
66
+ Log.level = :debug
67
67
  end
68
68
 
69
69
  opts.on('--trace', 'Show a full traceback on error') do
70
- Log.trace!
70
+ Log.trace = true
71
71
  end
72
72
 
73
73
  opts.on('-h', '--help', 'Display this help') do
@@ -90,16 +90,21 @@ module BibSync
90
90
 
91
91
  if @options[:bib]
92
92
  @options[:bib] = Bibliography.new(@options[:bib])
93
- @options[:bib].save_hook(Transformer.new)
93
+ @options[:bib].save_hook = Transformer.new
94
94
  end
95
95
 
96
96
  actions = []
97
97
  actions << :FetchFromArXiv if @options[:fetch]
98
- actions << :CheckVersions if @options[:check_versions] || @options[:update]
99
- actions << :SynchronizeFiles << :SynchronizeMetadata if @options[:sync] || @options[:resync]
98
+ actions << :CheckArXivVersions if @options[:check_versions] || @options[:update]
99
+ actions << :SynchronizeFiles << :DetermineArXivDOI << :SynchronizeMetadata if @options[:sync] || @options[:resync]
100
100
  actions << :FindMyCitations if @options[:citedbyme]
101
- actions << :Validate
102
- actions << :JabrefFormat if @options[:jabref]
101
+ actions << :Validate if @options[:bib]
102
+ actions << :JabRefFormat if @options[:jabref]
103
+
104
+ if actions.empty?
105
+ puts "Please specify actions! See #{$0} --help"
106
+ exit
107
+ end
103
108
 
104
109
  actions.map {|a| Actions.const_get(a).new(@options) }.each {|a| a.run }
105
110
  end
data/lib/bibsync/log.rb CHANGED
@@ -5,53 +5,55 @@ module BibSync
5
5
  Yellow = "\e[33m"
6
6
  Blue = "\e[36m"
7
7
 
8
- def self.verbose?
9
- @verbose
10
- end
8
+ Level = {
9
+ debug: nil,
10
+ info: nil,
11
+ notice: Blue,
12
+ warning: Yellow,
13
+ error: Red,
14
+ }
11
15
 
12
- def self.verbose!
13
- @verbose = true
16
+ class << self
17
+ attr_accessor :level, :trace
14
18
  end
15
19
 
16
- def self.trace?
17
- @trace
18
- end
19
-
20
- def self.trace!
21
- @trace = true
22
- end
20
+ self.trace = false
21
+ self.level = :info
23
22
 
24
23
  def debug(message, opts = {})
25
- info(message, opts) if Log.verbose?
24
+ log(:debug, message, opts)
26
25
  end
27
26
 
28
27
  def info(message, opts = {})
29
- log(message, opts)
28
+ log(:info, message, opts)
30
29
  end
31
30
 
32
31
  def notice(message, opts = {})
33
- log(message, opts.merge(:color => Blue))
32
+ log(:notice, message, opts)
34
33
  end
35
34
 
36
35
  def warning(message, opts = {})
37
- log(message, opts.merge(:color => Yellow))
36
+ log(:warning, message, opts)
38
37
  end
39
38
 
40
39
  def error(message, opts = {})
41
- log(message, opts.merge(:color => Red))
40
+ log(:error, message, opts)
42
41
  end
43
42
 
44
- def log(message, opts = {})
43
+ def log(level, message, opts = {})
44
+ return if Level.keys.index(level) < Level.keys.index(Log.level)
45
45
  if ex = opts[:ex]
46
46
  message = "#{message} - #{ex.message}"
47
47
  end
48
- message = "#{opts[:color]}#{message}#{Reset}" if opts[:color]
48
+ if color = Level[level]
49
+ message = "#{color}#{message}#{Reset}"
50
+ end
49
51
  if key = opts[:key]
50
52
  key = key.key if key.respond_to? :key
51
53
  message = "#{key} : #{message}"
52
54
  end
53
55
  puts(message)
54
- if Log.trace? && ex = opts[:ex]
56
+ if Log.trace && ex = opts[:ex]
55
57
  puts(ex.backtrace.join("\n"))
56
58
  end
57
59
  end
@@ -55,7 +55,7 @@ module BibSync
55
55
  entry[:shortjournal] = 'RMP'
56
56
  when /\ANew Journal of Physics\Z/i
57
57
  entry[:shortjournal] = 'NJP'
58
- when /\A#{ArXivJournal}\Z/i
58
+ when /\AArXiv e-prints\Z/i
59
59
  entry[:shortjournal] = 'arXiv'
60
60
  when /\AEurophysics Letters\Z/i
61
61
  entry[:shortjournal] = 'EPL'
data/lib/bibsync/utils.rb CHANGED
@@ -1,10 +1,8 @@
1
1
  module BibSync
2
2
  module Utils
3
- ArXivJournal = 'ArXiv e-prints'
4
-
5
- def split_filename(file)
6
- file =~ /^(.*?)\.(\w+)$/
7
- return $1, $2.upcase
3
+ def name_without_ext(file)
4
+ file =~ /\A(.*?)\.\w+\Z/
5
+ $1
8
6
  end
9
7
 
10
8
  def fetch(url, headers = {})
@@ -17,7 +15,7 @@ module BibSync
17
15
 
18
16
  def arxiv_download(dir, id)
19
17
  url = "http://arxiv.org/pdf/#{id}"
20
- file = File.join(dir, "#{arxiv_id(id, :version => true, :prefix => false)}.pdf")
18
+ file = File.join(dir, "#{arxiv_id(id, version: true, prefix: false)}.pdf")
21
19
  result = `curl --stderr - -S -s -L -o #{Shellwords.escape file} #{Shellwords.escape url}`
22
20
  raise result.chomp if $? != 0
23
21
  end
@@ -36,10 +34,10 @@ module BibSync
36
34
  raise unless opts.include?(:prefix) && opts.include?(:version)
37
35
  arxiv = arxiv[:arxiv] if Bibliography::Entry === arxiv
38
36
  if arxiv
39
- arxiv = arxiv.sub(/^.*\//, '') unless opts[:prefix]
40
- arxiv = arxiv.sub(/v\d+$/, '') unless opts[:version]
41
- arxiv
37
+ arxiv = arxiv.sub(/\A.*\//, '') unless opts[:prefix]
38
+ arxiv = arxiv.sub(/v\d+\Z/, '') unless opts[:version]
42
39
  end
40
+ arxiv
43
41
  end
44
42
  end
45
43
  end
@@ -1,3 +1,3 @@
1
1
  module BibSync
2
- VERSION = '0.0.1'
2
+ VERSION = '0.0.2'
3
3
  end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::CheckArXivVersions do
4
+ end
@@ -0,0 +1,61 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::DetermineArXivDOI do
4
+ before do
5
+ @tmpfile = File.join(fixturedir, 'tmp.bib')
6
+ end
7
+
8
+ after do
9
+ File.unlink(@tmpfile) if File.exists?(@tmpfile)
10
+ end
11
+
12
+ let(:bib) do
13
+ BibSync::Bibliography.new(@tmpfile)
14
+ end
15
+
16
+ let(:fixturebib) do
17
+ BibSync::Bibliography.new(File.join(fixturedir, 'test.bib'))
18
+ end
19
+
20
+ let(:action) do
21
+ BibSync::Actions::DetermineArXivDOI.new(bib: bib)
22
+ end
23
+
24
+ it 'should find arXiv identifier in pdf file' do
25
+ entry = fixturebib['FileWithEmbeddedArXiv']
26
+ bib << entry
27
+ action.run
28
+ entry[:arxiv].must_equal '0911.2512v3'
29
+ end
30
+
31
+ it 'should find DOI identifier in file and add missing arXiv identifier' do
32
+ entry = fixturebib['FileWithEmbeddedDOI']
33
+ bib << entry
34
+ action.run
35
+ entry[:arxiv].must_equal '0911.2512v3'
36
+ entry[:doi].must_equal '10.1103/PhysRevLett.104.106404'
37
+ end
38
+
39
+ it 'should interpret file name as arXiv identifier' do
40
+ entry = fixturebib['0911.2512v3']
41
+ bib << entry
42
+ action.run
43
+ entry[:arxiv].must_equal '0911.2512v3'
44
+ end
45
+
46
+ it 'should interpret file name as DOI identifier and add missing arXiv identifier' do
47
+ entry = fixturebib['PhysRevLett.104.106404']
48
+ bib << entry
49
+ action.run
50
+ entry[:arxiv].must_equal '0911.2512v3'
51
+ entry[:doi].must_equal '10.1103/PhysRevLett.104.106404'
52
+ end
53
+
54
+ it 'should add missing arXiv identifier' do
55
+ entry = fixturebib['HasDOI']
56
+ bib << entry
57
+ entry[:doi].must_equal '10.1103/PhysRevLett.104.106404'
58
+ action.run
59
+ entry[:arxiv].must_equal '0911.2512v3'
60
+ end
61
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::FetchFromArXiv do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::FindMyCitations do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::JabRefFormat do
4
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::SynchronizeFiles do
4
+ end
@@ -0,0 +1,34 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::SynchronizeMetadata do
4
+ before do
5
+ @tmpfile = File.join(fixturedir, 'tmp.bib')
6
+ end
7
+
8
+ after do
9
+ File.unlink(@tmpfile) if File.exists?(@tmpfile)
10
+ end
11
+
12
+ let(:bib) do
13
+ BibSync::Bibliography.new(@tmpfile)
14
+ end
15
+
16
+ let(:fixturebib) do
17
+ BibSync::Bibliography.new(File.join(fixturedir, 'test.bib'))
18
+ end
19
+
20
+ let(:action) do
21
+ BibSync::Actions::SynchronizeMetadata.new(bib: bib)
22
+ end
23
+
24
+ it 'should download metadata' do
25
+ entry = fixturebib['HasArXiv']
26
+ bib << entry
27
+ action.run
28
+ entry[:title].must_equal 'Chirality Induced Tilted-Hill Giant Nernst Signal'
29
+ entry[:journal].must_equal 'Physical Review Letters'
30
+ entry[:doi].must_equal '10.1103/PhysRevLett.104.106404'
31
+ entry[:arxiv].must_equal '0911.2512v3'
32
+ entry[:url].must_match(/dx\.doi\.org/)
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ require 'helper'
2
+
3
+ describe BibSync::Actions::Validate do
4
+ end
@@ -0,0 +1,7 @@
1
+ \documentclass{article}
2
+
3
+ \begin{document}
4
+
5
+ arXiv: 0911.2512v3
6
+
7
+ \end{document}
@@ -0,0 +1,7 @@
1
+ \documentclass{article}
2
+
3
+ \begin{document}
4
+
5
+ doi: 10.1103/PhysRevLett.104.106404
6
+
7
+ \end{document}
@@ -0,0 +1,8 @@
1
+ @BOOK{TestBook,
2
+ title = {BookTitle},
3
+ publisher = {BookPublisher},
4
+ year = {2000},
5
+ month = jan,
6
+ author = {BookAuthor},
7
+ volume = {BookVolume}
8
+ }
@@ -0,0 +1,34 @@
1
+ @BOOK{TestBook,
2
+ title = {BookTitle},
3
+ publisher = {BookPublisher},
4
+ year = {2000},
5
+ month = jan,
6
+ author = {BookAuthor},
7
+ volume = {BookVolume}
8
+ }
9
+
10
+ @ARTICLE{FileWithEmbeddedArXiv,
11
+ file = {:FileWithEmbeddedArXiv.pdf:PDF}
12
+ }
13
+
14
+ @ARTICLE{FileWithEmbeddedDOI,
15
+ file = {:FileWithEmbeddedDOI.pdf:PDF}
16
+ }
17
+
18
+ @ARTICLE{HasArXiv,
19
+ arxiv = {0911.2512v3}
20
+ }
21
+
22
+ @ARTICLE{HasDOI,
23
+ doi = {10.1103/PhysRevLett.104.106404}
24
+ }
25
+
26
+ @ARTICLE{0911.2512v3,
27
+ file = {:0911.2512v3.txt:TXT}
28
+ }
29
+
30
+ @ARTICLE{PhysRevLett.104.106404,
31
+ file = {:PhysRevLett.104.106404.txt:TXT}
32
+ }
33
+
34
+ @COMMENT{TestComment}
data/test/helper.rb ADDED
@@ -0,0 +1,21 @@
1
+ require 'minitest/spec'
2
+ require 'minitest/autorun'
3
+ require 'bibsync'
4
+ require 'fileutils'
5
+
6
+ BibSync::Log.level = :error
7
+ BibSync::Log.trace = true
8
+
9
+ module Helper
10
+ def fixturedir
11
+ File.join(File.dirname(__FILE__), 'fixture')
12
+ end
13
+ end
14
+
15
+ class MiniTest::Spec
16
+ include Helper
17
+
18
+ after do
19
+ FileUtils.rm_rf(File.join(File.dirname(__FILE__), 'tmp'))
20
+ end
21
+ end