bibsync 0.0.1 → 0.0.2

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