rb-dayone 0.4.1 → 0.6.0

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.
@@ -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: