rb-dayone 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,6 +1,3 @@
1
- require 'libxml'
2
- require 'fileutils'
3
-
4
1
  # A text-only journal entry for DayOne.
5
2
  class DayOne::Entry
6
3
 
@@ -21,6 +18,12 @@ class DayOne::Entry
21
18
 
22
19
  # Path to the entry image
23
20
  attr_accessor :image
21
+
22
+ # An entry's tags
23
+ attr_accessor :tags
24
+
25
+ # The location of the entry.
26
+ attr_accessor :location
24
27
 
25
28
  # The PList doctype, used for XML export
26
29
  DOCTYPE = [:DOCTYPE, :plist, :PUBLIC, "-//Apple//DTD PLIST 1.0//EN", "http://www.apple.com/DTDs/PropertyList-1.0.dtd"]
@@ -35,6 +38,8 @@ class DayOne::Entry
35
38
  @starred = false
36
39
  @entry_text = entry_text
37
40
  @saved = false
41
+ @tags = []
42
+ @location = DayOne::Location.new hsh.delete(:location)
38
43
 
39
44
  hsh.each do |k,v|
40
45
  setter = "#{k}="
@@ -48,6 +53,26 @@ class DayOne::Entry
48
53
  def uuid
49
54
  @uuid ||= `uuidgen`.gsub('-','').strip
50
55
  end
56
+
57
+ # Add a tag to the entry
58
+ # @param [String] str The string tag to add to the list.
59
+ def tag str
60
+ @tags << str.to_s
61
+ end
62
+
63
+ # Searches the entry text for tags (#foo) and adds
64
+ # them to the entry's tags. Will avoid adding punctuation
65
+ # to the end of the tag (so e.g. '#foo.' will detect as #foo).
66
+ def add_tags_from_entry_text
67
+ @entry_text.scan(/(?<=[ \t])#(\S+)/) do |m|
68
+ m = m.shift
69
+ if m[-1] !~ /[a-zA-Z0-9]/
70
+ # Then the last char is a symbol or punctuation, and we should wipe it
71
+ m = m[0..-2]
72
+ end
73
+ tag m
74
+ end
75
+ end
51
76
 
52
77
  # The same as calling Entry#saved
53
78
  def saved?
@@ -67,6 +92,17 @@ class DayOne::Entry
67
92
 
68
93
  builder.key 'Entry Text'
69
94
  builder.string entry_text
95
+
96
+ if !tags.empty?
97
+ builder.key 'Tags'
98
+ builder.array do
99
+ tags.each do |t|
100
+ builder.string t
101
+ end
102
+ end
103
+ end
104
+
105
+ location.to_xml(builder) unless location.left_blank?
70
106
 
71
107
  builder.key 'Starred'
72
108
  if starred
@@ -100,14 +136,8 @@ class DayOne::Entry
100
136
 
101
137
  # Check to make sure that we output valid xml
102
138
  def xml_valid?
103
- LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
104
- begin
105
- LibXML::XML::Parser.string(to_xml).parse
106
- rescue LibXML::XML::Error
107
- return false
108
- else
109
- return true
110
- end
139
+ doc = Nokogiri::XML(to_xml)
140
+ doc.errors.empty?
111
141
  end
112
142
 
113
143
  # Assign an image to the entry
@@ -1,5 +1,3 @@
1
- require 'libxml'
2
-
3
1
  # Imports DayOne entries from XML files or plain text
4
2
  class DayOne::EntryImporter
5
3
 
@@ -12,16 +10,16 @@ class DayOne::EntryImporter
12
10
  # Create a new entry based on a string. To import from a file,
13
11
  # use EntryImporter.from_file.
14
12
  # @param [String] data The raw data for the importer to process
15
- def initialize data
13
+ # @param [String] file The file this data came from. Defaults to +nil+.
14
+ def initialize data, file=nil
16
15
  @data = data
16
+ @file = file
17
17
  end
18
18
 
19
19
  # Create a new entry from a file
20
20
  # @param [String] file The file to import
21
21
  def self.from_file file
22
- ei = new(File.read(file))
23
- ei.file = file
24
- ei
22
+ new(File.read(file), file)
25
23
  end
26
24
 
27
25
  # Access entry data by key
@@ -34,35 +32,7 @@ class DayOne::EntryImporter
34
32
  # Generate and return the data contained within the doentry file, as a hash
35
33
  # @return [Hash] the processed data
36
34
  def processed_data
37
- if !@processed_data
38
- @processed_data = {}
39
- LibXML::XML::Error.set_handler(&LibXML::XML::Error::QUIET_HANDLER)
40
-
41
- begin
42
- document = LibXML::XML::Parser.string(data).parse
43
- key = nil
44
-
45
- document.find('//plist/dict/*').each do |elem|
46
- case elem.name
47
- when 'key'
48
- key = elem.content
49
- when 'date'
50
- if elem.content =~ /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z/
51
- @processed_data[key] = Time.new($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
52
- end
53
- when 'true'
54
- @processed_data[key] = true
55
- when 'false'
56
- @processed_data[key] = false
57
- else
58
- @processed_data[key] = elem.content
59
- end
60
- end
61
- rescue LibXML::XML::Error
62
- $stderr.puts "Error parsing #{file ? "file #{file}" : "data"}. Skipping."
63
- end
64
- end
65
- @processed_data
35
+ @processed_data ||= process_dict(Nokogiri::XML(data).xpath('//plist/dict').first)
66
36
  end
67
37
 
68
38
  # Generate an entry from this importer
@@ -72,7 +42,52 @@ class DayOne::EntryImporter
72
42
  self['Entry Text'],
73
43
  starred: self['Starred'],
74
44
  creation_date: self['Creation Date'],
75
- saved: true
45
+ saved: true,
46
+ tags: self['Tags']||[],
47
+ location: self['Location']||{}
76
48
  )
77
49
  end
50
+
51
+ private
52
+
53
+ # Process an XML tag. Returns a the value of the tag as a ruby value.
54
+ # @param [Nokogiri::XML::Element] element The element to process
55
+ # @return The values contained within the element
56
+ def process_value element
57
+ case element.name
58
+ when 'date'
59
+ if element.content =~ /(\d+)-(\d+)-(\d+)T(\d+):(\d+):(\d+)Z/
60
+ Time.utc($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
61
+ end
62
+ when 'string'
63
+ element.content
64
+ when 'real'
65
+ element.content.to_f
66
+ when 'true'
67
+ true
68
+ when 'false'
69
+ false
70
+ when 'array'
71
+ element.xpath('*').map{ |e| process_value(e) }
72
+ when 'dict'
73
+ process_dict(element)
74
+ end
75
+ end
76
+
77
+ # Process an XML dict element. Returns a hash of values
78
+ # @param [Nokogiri::XML::Element] element The dictionary element to process
79
+ # @return [Hash] The values contained within the element
80
+ def process_dict dict
81
+ processed_data = {}
82
+ key = nil
83
+
84
+ dict.xpath('*').each do |elem|
85
+ if elem.name == 'key'
86
+ key = elem.content
87
+ else
88
+ processed_data[key] = process_value(elem)
89
+ end
90
+ end
91
+ processed_data
92
+ end
78
93
  end
@@ -0,0 +1,74 @@
1
+ # This represents a location as recognised by DayOne.app
2
+ class DayOne::Location
3
+
4
+ # An array of components that make up the location
5
+ STRING_COMPONENTS = [:country, :locality, :administrative_area, :place_name]
6
+ REAL_COMPONENTS = [:latitude, :longitude]
7
+ ALL_COMPONENTS = STRING_COMPONENTS + REAL_COMPONENTS
8
+
9
+ # These are all instance variables
10
+ attr_accessor *ALL_COMPONENTS
11
+
12
+ # Initialize the location with a hash of values.
13
+ # @param [Hash] hsh The values to be assigned on initalization
14
+ def initialize hsh=nil
15
+ if hsh
16
+ hsh.each do |k,v|
17
+ setter = "#{symbol(k)}="
18
+ send(setter,v) if respond_to?(setter)
19
+ end
20
+ end
21
+ end
22
+
23
+ # Has this location been left blank?
24
+ # @return [bool] If any value in this instance is not nil or blank
25
+ def left_blank?
26
+ STRING_COMPONENTS.all?{ |s| [nil,''].include?(send(s)) } &&
27
+ REAL_COMPONENTS.all?{ |s| [nil,0.0].include?(send(s)) }
28
+ end
29
+
30
+ # Converts the location to xml. A +builder+ must be supplied, and
31
+ # this method will run operations on the builder and return
32
+ # a boolean indicating its success.
33
+ # @param [Builder] builder The builder this object will be put in
34
+ # @return [bool] The success of the operation
35
+ def to_xml builder
36
+ builder.key 'Location'
37
+ builder.dict do
38
+ STRING_COMPONENTS.each do |s|
39
+ builder.key xml_string(s)
40
+ builder.string send(s)
41
+ end
42
+
43
+ REAL_COMPONENTS.each do |s|
44
+ builder.key xml_string(s)
45
+ builder.real send(s)
46
+ end
47
+ end
48
+ true
49
+ end
50
+
51
+ private
52
+
53
+ # Gives the XML string for a particular symbol
54
+ # @param [Symbol] symbol The symbol to find the XML string for
55
+ # @return [String] The string for this symbol
56
+ def xml_string symbol
57
+ if symbol.is_a? String
58
+ symbol
59
+ else
60
+ symbol.to_s.gsub('_',' ').gsub(/\b./, &:upcase)
61
+ end
62
+ end
63
+
64
+ # Gives the symbol for a particular XML string
65
+ # @param [String] string The XML string to find the symbol for
66
+ # @return [Symbol] The symbol for this symbol
67
+ def symbol xml_string
68
+ if xml_string.is_a? Symbol
69
+ xml_string
70
+ else
71
+ xml_string.downcase.gsub(' ','_').intern
72
+ end
73
+ end
74
+ end
@@ -1,5 +1,3 @@
1
- require 'nokogiri'
2
-
3
1
  # The PListReader is a class that reads PLists.
4
2
  # It is used to read the DayOne preferences PList.
5
3
  class DayOne::PlistReader
@@ -59,7 +57,7 @@ class DayOne::PlistReader
59
57
  # @return [String] the path to the current DayOne journal location
60
58
  def journal_location
61
59
  key('JournalPackageURL') || # First, try the 1.7 location...
62
- key('NSNavLastRootDirectory')
60
+ key('NSNavLastRootDirectory') # Then try the pre-1.7 location
63
61
  end
64
62
 
65
63
  # This allows us to access the body's method as well as the reader's.
@@ -8,20 +8,27 @@ class DayOne::Search
8
8
 
9
9
  # The entry must be starred
10
10
  attr_accessor :starred
11
+
12
+ # The entry must have this tag
13
+ attr_accessor :tag
11
14
 
12
- # Initialize the search. Currently you can only search by entry text and starred status,
13
- # but both of these can be passed in via hash.
15
+ # Initialize the search. Currently you can search by:
16
+ # * entry text
17
+ # * starred status
18
+ # * tag
19
+ #
20
+ # These can be passed in via hash.
14
21
  # @param [Hash] hash a hash of search criteria, including:
15
- # * +entry_text+: Text that the entry must include
16
- # * +starred+: whether the entry is starred or not
22
+ # * +:entry_text+ - Text that the entry must include
23
+ # * +:starred+ - whether the entry is starred or not
24
+ # * +:tag+ - a tag that the entry must have
17
25
  def initialize hash={}
18
- @entry_text = ''
19
26
  hash.each do |k,v|
20
27
  setter = "#{k}="
21
28
  self.send(setter, v) if self.respond_to?(setter)
22
29
  end
23
30
  end
24
-
31
+
25
32
  # Fetch the results by searching. Uses a cached version of the DayOne database.
26
33
  # @return [Array] all entries matching your results
27
34
  def results
@@ -29,8 +36,9 @@ class DayOne::Search
29
36
  @results = []
30
37
  working_results = cache
31
38
 
32
- working_results = working_results.select{ |e| e.entry_text.include? entry_text } unless entry_text == ''
39
+ working_results = working_results.select{ |e| e.entry_text.include? entry_text } unless entry_text.nil?
33
40
  working_results = working_results.select{ |e| e.starred == starred } unless starred.nil?
41
+ working_results = working_results.select{ |e| e.tags.include? tag } unless tag.nil?
34
42
 
35
43
  @results = working_results
36
44
  end
data/rb-dayone.gemspec CHANGED
@@ -11,27 +11,15 @@ Gem::Specification.new do |s|
11
11
  s.email = 'janyves.ruzicka@gmail.com'
12
12
  s.homepage = 'https://github.com/jyruzicka/rb-dayone'
13
13
 
14
- s.files = File.read('Manifest').split("\n")
14
+ s.files = File.read('Manifest').split("\n").select{ |l| !l.start_with?('#') && l != ''}
15
15
  s.require_paths << 'lib'
16
- s.bindir = 'bin'
17
- s.executables << 'dayone'
18
16
  s.extra_rdoc_files = ['README.md']
17
+
18
+ s.executables << 'dayone'
19
+
19
20
 
20
21
  s.add_runtime_dependency 'builder', '~> 2.0'
21
22
  s.add_runtime_dependency 'commander', '~> 4.1.2'
22
23
  s.add_runtime_dependency 'libxml-ruby', '~> 2.3.3'
23
-
24
- s.post_install_message = <<-end
25
- #{'-'*80}
26
- Hi there! If you're upgrading from version <= 0.2.0 of this gem, I've been a
27
- horrid dev and let a few bugs through in my XML building code. In order to fix
28
- this, you can run `dayone verify` to see if your database needs fixing. If any
29
- of the errors are my fault, you should be able to fix them with `dayone
30
- repair`.
31
-
32
- Sorry if this has caused any inconvenience. If you still have trouble repairing
33
- any of your .doentry files, send me an email and I'll see what I can do to
34
- help.
35
- #{'-'*80}
36
- end
24
+ s.add_runtime_dependency 'nokogiri', '~> 1.5.0'
37
25
  end
data/version.txt CHANGED
@@ -1 +1 @@
1
- 0.4.1
1
+ 0.6.0
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rb-dayone
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-14 00:00:00.000000000 Z
12
+ date: 2013-02-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: builder
16
- requirement: !ruby/object:Gem::Requirement
16
+ requirement: &70362709355040 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,15 +21,10 @@ dependencies:
21
21
  version: '2.0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
- requirements:
27
- - - ~>
28
- - !ruby/object:Gem::Version
29
- version: '2.0'
24
+ version_requirements: *70362709355040
30
25
  - !ruby/object:Gem::Dependency
31
26
  name: commander
32
- requirement: !ruby/object:Gem::Requirement
27
+ requirement: &70362709354080 !ruby/object:Gem::Requirement
33
28
  none: false
34
29
  requirements:
35
30
  - - ~>
@@ -37,15 +32,10 @@ dependencies:
37
32
  version: 4.1.2
38
33
  type: :runtime
39
34
  prerelease: false
40
- version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
- requirements:
43
- - - ~>
44
- - !ruby/object:Gem::Version
45
- version: 4.1.2
35
+ version_requirements: *70362709354080
46
36
  - !ruby/object:Gem::Dependency
47
37
  name: libxml-ruby
48
- requirement: !ruby/object:Gem::Requirement
38
+ requirement: &70362709352740 !ruby/object:Gem::Requirement
49
39
  none: false
50
40
  requirements:
51
41
  - - ~>
@@ -53,12 +43,18 @@ dependencies:
53
43
  version: 2.3.3
54
44
  type: :runtime
55
45
  prerelease: false
56
- version_requirements: !ruby/object:Gem::Requirement
46
+ version_requirements: *70362709352740
47
+ - !ruby/object:Gem::Dependency
48
+ name: nokogiri
49
+ requirement: &70362709350740 !ruby/object:Gem::Requirement
57
50
  none: false
58
51
  requirements:
59
52
  - - ~>
60
53
  - !ruby/object:Gem::Version
61
- version: 2.3.3
54
+ version: 1.5.0
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *70362709350740
62
58
  description: Create or search [DayOne](http://www.dayoneapp.com) journal entries simply
63
59
  and easily in ruby. Currently only supports text entries, image entries to come.
64
60
  email: janyves.ruzicka@gmail.com
@@ -68,42 +64,24 @@ extensions: []
68
64
  extra_rdoc_files:
69
65
  - README.md
70
66
  files:
67
+ - .gitignore
71
68
  - bin/dayone
72
69
  - Gemfile
73
- - History.rdoc
70
+ - Gemfile.lock
74
71
  - lib/rb-dayone.rb
75
72
  - lib/rb-dayone/entry.rb
76
73
  - lib/rb-dayone/entry_importer.rb
74
+ - lib/rb-dayone/location.rb
77
75
  - lib/rb-dayone/plist_reader.rb
78
76
  - lib/rb-dayone/search.rb
79
77
  - Manifest
80
78
  - Rakefile
81
79
  - rb-dayone.gemspec
82
80
  - README.md
83
- - spec/data/entries/748AD5D252F44149920485B0CEA478E3.doentry
84
- - spec/data/entries/F13723CDFD454481B24312DCD627BFD7.doentry
85
- - spec/data/foo.doentry
86
- - spec/data/locations/location-auto/location
87
- - spec/data/locations/location-specified/location
88
- - spec/data/sample.doentry
89
- - spec/data/sample.plist
90
- - spec/data/utf.doentry
91
- - spec/dayone_spec.rb
92
- - spec/entry_importer_spec.rb
93
- - spec/entry_spec.rb
94
- - spec/plist_reader_spec.rb
95
- - spec/search_spec.rb
96
- - spec/spec_helper.rb
97
81
  - version.txt
98
82
  homepage: https://github.com/jyruzicka/rb-dayone
99
83
  licenses: []
100
- post_install_message: ! "--------------------------------------------------------------------------------\nHi
101
- there! If you're upgrading from version <= 0.2.0 of this gem, I've been a\nhorrid
102
- dev and let a few bugs through in my XML building code. In order to fix\nthis, you
103
- can run `dayone verify` to see if your database needs fixing. If any \nof the errors
104
- are my fault, you should be able to fix them with `dayone\nrepair`.\n\nSorry if
105
- this has caused any inconvenience. If you still have trouble repairing\nany of your
106
- .doentry files, send me an email and I'll see what I can do to\nhelp.\n--------------------------------------------------------------------------------\n"
84
+ post_install_message:
107
85
  rdoc_options: []
108
86
  require_paths:
109
87
  - lib
@@ -122,9 +100,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
122
100
  version: '0'
123
101
  requirements: []
124
102
  rubyforge_project:
125
- rubygems_version: 1.8.23
103
+ rubygems_version: 1.8.10
126
104
  signing_key:
127
105
  specification_version: 3
128
106
  summary: Create DayOne journal entries in ruby.
129
107
  test_files: []
130
- has_rdoc: