tagcrumbs 0.3.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.
Files changed (178) hide show
  1. data/History.txt +9 -0
  2. data/License.txt +22 -0
  3. data/Manifest.txt +177 -0
  4. data/PostInstall.txt +2 -0
  5. data/README.rdoc +45 -0
  6. data/Rakefile +35 -0
  7. data/TODO.txt +17 -0
  8. data/bin/tagcrumbs +5 -0
  9. data/examples/address_update.rb +15 -0
  10. data/examples/comment_new.rb +19 -0
  11. data/examples/favorite_new.rb +17 -0
  12. data/examples/filter_new.rb +15 -0
  13. data/examples/friendship_create.rb +19 -0
  14. data/examples/link_new.rb +16 -0
  15. data/examples/location_update.rb +16 -0
  16. data/examples/picture_update.rb +16 -0
  17. data/examples/placemark_new_simple.rb +24 -0
  18. data/examples/placemark_new_suggestions.rb +24 -0
  19. data/examples/profile_link_new.rb +14 -0
  20. data/examples/profile_update.rb +12 -0
  21. data/examples/request_authorization.rb +25 -0
  22. data/examples/settings_update.rb +12 -0
  23. data/examples/subscription_new.rb +15 -0
  24. data/examples/tagcrumbs_find.rb +25 -0
  25. data/examples/tagcrumbs_search.rb +7 -0
  26. data/examples/user.rb +28 -0
  27. data/examples/user_recommendation_new.rb +15 -0
  28. data/lib/tagcrumbs/builders/builder.rb +45 -0
  29. data/lib/tagcrumbs/builders/json_builder.rb +38 -0
  30. data/lib/tagcrumbs/builders/xml_builder.rb +30 -0
  31. data/lib/tagcrumbs/cli.rb +268 -0
  32. data/lib/tagcrumbs/config_store.rb +35 -0
  33. data/lib/tagcrumbs/exceptions.rb +13 -0
  34. data/lib/tagcrumbs/node.rb +43 -0
  35. data/lib/tagcrumbs/parsers/json_parser.rb +79 -0
  36. data/lib/tagcrumbs/parsers/parser.rb +24 -0
  37. data/lib/tagcrumbs/parsers/xml_parser.rb +54 -0
  38. data/lib/tagcrumbs/proxy.rb +32 -0
  39. data/lib/tagcrumbs/requestor.rb +140 -0
  40. data/lib/tagcrumbs/resources/array.rb +108 -0
  41. data/lib/tagcrumbs/resources/models/accessors.rb +252 -0
  42. data/lib/tagcrumbs/resources/models/activity.rb +19 -0
  43. data/lib/tagcrumbs/resources/models/address.rb +16 -0
  44. data/lib/tagcrumbs/resources/models/city.rb +13 -0
  45. data/lib/tagcrumbs/resources/models/comment.rb +12 -0
  46. data/lib/tagcrumbs/resources/models/country.rb +15 -0
  47. data/lib/tagcrumbs/resources/models/fanship.rb +12 -0
  48. data/lib/tagcrumbs/resources/models/favorite.rb +22 -0
  49. data/lib/tagcrumbs/resources/models/filter.rb +32 -0
  50. data/lib/tagcrumbs/resources/models/flag.rb +10 -0
  51. data/lib/tagcrumbs/resources/models/friendship.rb +12 -0
  52. data/lib/tagcrumbs/resources/models/geoname.rb +8 -0
  53. data/lib/tagcrumbs/resources/models/link.rb +13 -0
  54. data/lib/tagcrumbs/resources/models/location.rb +13 -0
  55. data/lib/tagcrumbs/resources/models/model.rb +148 -0
  56. data/lib/tagcrumbs/resources/models/picture.rb +28 -0
  57. data/lib/tagcrumbs/resources/models/place.rb +17 -0
  58. data/lib/tagcrumbs/resources/models/placemark.rb +30 -0
  59. data/lib/tagcrumbs/resources/models/profile.rb +16 -0
  60. data/lib/tagcrumbs/resources/models/profile_link.rb +12 -0
  61. data/lib/tagcrumbs/resources/models/root.rb +19 -0
  62. data/lib/tagcrumbs/resources/models/settings.rb +14 -0
  63. data/lib/tagcrumbs/resources/models/state.rb +14 -0
  64. data/lib/tagcrumbs/resources/models/subscription.rb +13 -0
  65. data/lib/tagcrumbs/resources/models/suggestions.rb +11 -0
  66. data/lib/tagcrumbs/resources/models/tag.rb +6 -0
  67. data/lib/tagcrumbs/resources/models/tagcrumb.rb +58 -0
  68. data/lib/tagcrumbs/resources/models/user.rb +101 -0
  69. data/lib/tagcrumbs/resources/models/user_recommendation.rb +18 -0
  70. data/lib/tagcrumbs/resources/resource.rb +76 -0
  71. data/lib/tagcrumbs/validations.rb +301 -0
  72. data/lib/tagcrumbs.rb +232 -0
  73. data/script/console +10 -0
  74. data/script/destroy +14 -0
  75. data/script/generate +14 -0
  76. data/spec/fixtures/activity.json +210 -0
  77. data/spec/fixtures/activity.xml +66 -0
  78. data/spec/fixtures/address.json +183 -0
  79. data/spec/fixtures/address.xml +58 -0
  80. data/spec/fixtures/city.json +88 -0
  81. data/spec/fixtures/city.xml +34 -0
  82. data/spec/fixtures/comment.json +286 -0
  83. data/spec/fixtures/comment.xml +87 -0
  84. data/spec/fixtures/country.json +32 -0
  85. data/spec/fixtures/country.xml +13 -0
  86. data/spec/fixtures/fanship.json +180 -0
  87. data/spec/fixtures/fanship.xml +54 -0
  88. data/spec/fixtures/favorite.json +332 -0
  89. data/spec/fixtures/favorite.xml +109 -0
  90. data/spec/fixtures/filter.json +243 -0
  91. data/spec/fixtures/filter.xml +80 -0
  92. data/spec/fixtures/friendship.json +180 -0
  93. data/spec/fixtures/friendship.xml +54 -0
  94. data/spec/fixtures/geoname.json +18 -0
  95. data/spec/fixtures/geoname.xml +6 -0
  96. data/spec/fixtures/json_parser.json +25 -0
  97. data/spec/fixtures/link.json +288 -0
  98. data/spec/fixtures/link.xml +88 -0
  99. data/spec/fixtures/location.json +204 -0
  100. data/spec/fixtures/location.xml +71 -0
  101. data/spec/fixtures/picture.json +154 -0
  102. data/spec/fixtures/picture.png +0 -0
  103. data/spec/fixtures/picture.xml +49 -0
  104. data/spec/fixtures/placemark.json +357 -0
  105. data/spec/fixtures/placemark.xml +58 -0
  106. data/spec/fixtures/placemarks.json +887 -0
  107. data/spec/fixtures/placemarks.xml +348 -0
  108. data/spec/fixtures/profile.json +282 -0
  109. data/spec/fixtures/profile.xml +100 -0
  110. data/spec/fixtures/profile_link.json +156 -0
  111. data/spec/fixtures/profile_link.xml +49 -0
  112. data/spec/fixtures/root.json +46 -0
  113. data/spec/fixtures/root.xml +14 -0
  114. data/spec/fixtures/settings.json +147 -0
  115. data/spec/fixtures/settings.xml +45 -0
  116. data/spec/fixtures/state.json +62 -0
  117. data/spec/fixtures/state.xml +23 -0
  118. data/spec/fixtures/subscription.json +283 -0
  119. data/spec/fixtures/subscription.xml +86 -0
  120. data/spec/fixtures/suggestions.json +1877 -0
  121. data/spec/fixtures/suggestions.xml +724 -0
  122. data/spec/fixtures/tag.json +9 -0
  123. data/spec/fixtures/tag.xml +4 -0
  124. data/spec/fixtures/user.json +105 -0
  125. data/spec/fixtures/user.xml +31 -0
  126. data/spec/fixtures/user_recommendation.json +349 -0
  127. data/spec/fixtures/user_recommendation.xml +106 -0
  128. data/spec/fixtures/validation_errors.json +5 -0
  129. data/spec/fixtures/validation_errors.xml +5 -0
  130. data/spec/fixtures/xml_parser.xml +13 -0
  131. data/spec/spec.opts +1 -0
  132. data/spec/spec_helper.rb +75 -0
  133. data/spec/tagcrumbs/builders/builder_spec.rb +57 -0
  134. data/spec/tagcrumbs/builders/json_builder_spec.rb +47 -0
  135. data/spec/tagcrumbs/builders/xml_builder_spec.rb +34 -0
  136. data/spec/tagcrumbs/exceptions_spec.rb +16 -0
  137. data/spec/tagcrumbs/node_spec.rb +42 -0
  138. data/spec/tagcrumbs/parsers/json_parser_spec.rb +117 -0
  139. data/spec/tagcrumbs/parsers/parser_spec.rb +25 -0
  140. data/spec/tagcrumbs/parsers/xml_parser_spec.rb +117 -0
  141. data/spec/tagcrumbs/proxy_spec.rb +48 -0
  142. data/spec/tagcrumbs/requestor_spec.rb +62 -0
  143. data/spec/tagcrumbs/resources/array_spec.rb +62 -0
  144. data/spec/tagcrumbs/resources/models/accessors_spec.rb +123 -0
  145. data/spec/tagcrumbs/resources/models/activity_spec.rb +33 -0
  146. data/spec/tagcrumbs/resources/models/address_spec.rb +24 -0
  147. data/spec/tagcrumbs/resources/models/city_spec.rb +33 -0
  148. data/spec/tagcrumbs/resources/models/comment_spec.rb +23 -0
  149. data/spec/tagcrumbs/resources/models/country_spec.rb +26 -0
  150. data/spec/tagcrumbs/resources/models/fanship_spec.rb +28 -0
  151. data/spec/tagcrumbs/resources/models/favorite_spec.rb +28 -0
  152. data/spec/tagcrumbs/resources/models/filter_spec.rb +43 -0
  153. data/spec/tagcrumbs/resources/models/flag_spec.rb +5 -0
  154. data/spec/tagcrumbs/resources/models/friendship_spec.rb +28 -0
  155. data/spec/tagcrumbs/resources/models/geoname_spec.rb +18 -0
  156. data/spec/tagcrumbs/resources/models/link_spec.rb +24 -0
  157. data/spec/tagcrumbs/resources/models/location_spec.rb +28 -0
  158. data/spec/tagcrumbs/resources/models/model_spec.rb +27 -0
  159. data/spec/tagcrumbs/resources/models/picture_spec.rb +42 -0
  160. data/spec/tagcrumbs/resources/models/place_spec.rb +12 -0
  161. data/spec/tagcrumbs/resources/models/placemark_spec.rb +61 -0
  162. data/spec/tagcrumbs/resources/models/profile_link_spec.rb +29 -0
  163. data/spec/tagcrumbs/resources/models/profile_spec.rb +43 -0
  164. data/spec/tagcrumbs/resources/models/root_spec.rb +57 -0
  165. data/spec/tagcrumbs/resources/models/settings_spec.rb +32 -0
  166. data/spec/tagcrumbs/resources/models/state_spec.rb +30 -0
  167. data/spec/tagcrumbs/resources/models/subscription_spec.rb +28 -0
  168. data/spec/tagcrumbs/resources/models/suggestions_spec.rb +33 -0
  169. data/spec/tagcrumbs/resources/models/tag_spec.rb +16 -0
  170. data/spec/tagcrumbs/resources/models/tagcrumb_spec.rb +21 -0
  171. data/spec/tagcrumbs/resources/models/user_recommendation_spec.rb +35 -0
  172. data/spec/tagcrumbs/resources/models/user_spec.rb +128 -0
  173. data/spec/tagcrumbs/resources/resource_spec.rb +62 -0
  174. data/spec/tagcrumbs/validations_spec.rb +27 -0
  175. data/spec/tagcrumbs_spec.rb +103 -0
  176. data/tagcrumbs.gemspec +59 -0
  177. data/tasks/rspec.rake +21 -0
  178. metadata +327 -0
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby -rubygems
2
+
3
+ require File.dirname(__FILE__) + '/request_authorization'
4
+
5
+ placemark = Tagcrumbs.current_user.placemarks.first
6
+ friend = Tagcrumbs.current_user.friends.first
7
+
8
+ recommendation = Tagcrumbs::UserRecommendation.new(:placemark => placemark, :recommended_user => friend, :message => 'very interesting place, check it out!')
9
+
10
+ if recommendation.save
11
+ puts "You sent a recommendation to #{friend}!"
12
+ else
13
+ puts "Could not sent a recommendation to #{friend}!"
14
+ puts recommendation.errors.full_messages.join("\n")
15
+ end
@@ -0,0 +1,45 @@
1
+ module Tagcrumbs
2
+ class Builder
3
+ attr_accessor :model
4
+
5
+ def initialize(model, options={})
6
+ self.model = model
7
+ end
8
+
9
+ def build
10
+ ''
11
+ end
12
+
13
+ # also include the name as we might have some exceptions based on name someday
14
+ def typecast(name, value)
15
+ if value.class == Time
16
+ value.iso8601
17
+ elsif value.is_a?(Tagcrumbs::Model)
18
+ value.resource_url
19
+ else
20
+ value
21
+ end
22
+ end
23
+
24
+ # also include the name as we might have some exceptions based on name someday
25
+ def determine_type(name, value)
26
+ type = {}
27
+ type.merge!(:nil => true) if value.nil?
28
+
29
+ if value.class == Time
30
+ type.merge!(:type => :timestamp)
31
+ elsif value.class == TrueClass || value.class == FalseClass
32
+ type.merge!(:type => :boolean)
33
+ elsif value.class == Float
34
+ type.merge!(:type => :float)
35
+ elsif value.class == Fixnum
36
+ type.merge!(:type => :integer)
37
+ elsif value.is_a?(Tagcrumbs::Model)
38
+ type.merge!(:type => value.class_name)
39
+ end
40
+
41
+ type
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,38 @@
1
+ module Tagcrumbs
2
+ class JsonBuilder < Builder
3
+ def build(options={})
4
+ options.reverse_merge!(:to_json => true)
5
+
6
+ attributes = {}
7
+
8
+ if options[:badgerfish] # build badgerfish style json
9
+ model.attributes(options).each do |key, value|
10
+ attributes[key] = {'$' => typecast(key, value)}.merge(badgerfish_attributes(determine_type(key, value)))
11
+ end
12
+
13
+ hash = {model.class_name.underscore => attributes.merge(badgerfish_attributes(:type => model.class_name))}
14
+ else # build plain json
15
+ model.attributes(options).each do |key, value|
16
+ attributes[key] = typecast(key, value)
17
+ end
18
+
19
+ hash = {model.class_name.underscore => attributes}
20
+ end
21
+
22
+ options[:to_json] ? hash.to_json : hash # makes testing easier
23
+ end
24
+
25
+
26
+ private
27
+
28
+ def badgerfish_attributes(attributes)
29
+ hash = {}
30
+
31
+ attributes.each do |k, v|
32
+ hash["@#{k}"] = v
33
+ end
34
+
35
+ hash
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,30 @@
1
+ module Tagcrumbs
2
+ class XmlBuilder < Builder
3
+ def build(options={})
4
+ result = ''
5
+
6
+ xml = ::Builder::XmlMarkup.new(:indent => 1, :target => result)
7
+ xml.instruct! :xml, :version => "1.0"
8
+
9
+ xml.tag!(model.class_name.underscore, :type => model.class_name, 'xmlns:xlink' => "http://www.w3.org/1999/xlink") do
10
+ attributes = model.attributes(options)
11
+ attribute_keys = attributes.keys
12
+ attribute_keys.sort!{|a, b| a.to_s <=> b.to_s} if options[:sorted_attributes] # for testing, otherwise output order is random
13
+
14
+ attribute_keys.each do |key|
15
+ if attributes[key].is_a?(Hash) # nested attributes like the address attributes on creation of a placemark
16
+ xml.tag!(key) do
17
+ attributes[key].each do |k, v|
18
+ xml.tag!(k, typecast(k, v), determine_type(k, v))
19
+ end
20
+ end
21
+ else
22
+ xml.tag!(key, typecast(key, attributes[key]), determine_type(key, attributes[key]))
23
+ end
24
+ end
25
+ end
26
+
27
+ result
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,268 @@
1
+ require 'optparse'
2
+ require 'ostruct'
3
+ require File.dirname(__FILE__) + '/../tagcrumbs'
4
+
5
+ module Tagcrumbs
6
+ class CLI
7
+ attr_reader :options, :stdout, :stdin
8
+
9
+ def self.execute(stdout, stdin, stderr, arguments = [])
10
+ self.new.execute(stdout, stdin, stderr, arguments)
11
+ end
12
+
13
+ def initialize
14
+ @options = {}
15
+
16
+ trap(:INT) { exit } # no backtrace on ^C
17
+ end
18
+
19
+ def execute(stdout, stdin, stderr, arguments = [])
20
+ @stdout = stdout
21
+ @stdin = stdin
22
+ @stderr = stderr
23
+
24
+ @config_store = Tagcrumbs::ConfigStore.load(ENV['HOME'] + '/.tagcrumbs')
25
+ Tagcrumbs.configure(@config_store.options)
26
+
27
+ if Tagcrumbs.valid_credentials?
28
+ if arguments.present?
29
+ option_parser.parse!(arguments)
30
+ execute_new_placemark
31
+ else
32
+ execute_interactive_mode
33
+ end
34
+ else
35
+ execute_authorize
36
+ end
37
+ end
38
+
39
+ protected
40
+
41
+ def option_parser
42
+ option_parser = OptionParser.new do |opts|
43
+ opts.banner = "Usage: #{$0} [options]"
44
+
45
+ @options[:placemark] = {}
46
+ @options[:placemark][:address_attributes] = {}
47
+
48
+ opts.on('-v', '--version') do
49
+ output_version
50
+ exit 0
51
+ end
52
+
53
+ # GENERAL OPTIONS
54
+ opts.on('-h', '--help') do
55
+ output_help
56
+ exit 0
57
+ end
58
+
59
+ opts.on('-V', '--verbose') do
60
+ @options[:verbose] = true
61
+ end
62
+
63
+ # PLACEMARK OPTIONS
64
+ opts.on("-n", "--name NAME", "Give your tagcrumb a name") do |name|
65
+ @options[:placemark][:name] = name
66
+ end
67
+
68
+ opts.on("-N", "--notes NOTES", "Add some notes") do |notes|
69
+ @options[:placemark][:notes] = notes
70
+ end
71
+
72
+ opts.on("-t", "--tags tag1,tag2,tagX", "Add tags") do |tags|
73
+ @options[:placemark][:tags] = tags
74
+ end
75
+
76
+ opts.on("-y", "--latitude LATITUDE", Float, "Set the latitude") do |latitude|
77
+ @options[:placemark][:latitude] = latitude
78
+ end
79
+
80
+ opts.on("-x", "--longitude LATITUDE", Float, "Set the longitude") do |longitude|
81
+ @options[:placemark][:longitude] = longitude
82
+ end
83
+
84
+ opts.on("-p", "--private", "Make it private") do
85
+ @options[:placemark][:private] = true
86
+ end
87
+
88
+ opts.on("-g", "--guess-city", "Make a guess on the correct city") do
89
+ @options[:placemark][:guess_city] = true
90
+ end
91
+
92
+ opts.on("-w", "--websites url1,url2,urlX", ::Array, "List of websites") do |links|
93
+ @options[:placemark][:links] = links
94
+ end
95
+
96
+
97
+ opts.on("--address-street STREET", "The street address") do |street|
98
+ @options[:placemark][:address_attributes][:street] = street
99
+ end
100
+
101
+ opts.on("--address-postal-code ZIP", "The postal code") do |postal_code|
102
+ @options[:placemark][:address_attributes][:postal_code] = postal_code
103
+ end
104
+
105
+ opts.on("--address-city CITY", "The city name as it will be put in the address") do |city|
106
+ @options[:placemark][:address_attributes][:city] = city
107
+ end
108
+
109
+ opts.on("--address-state STATE", "The state name as it will be put in the address") do |state|
110
+ @options[:placemark][:address_attributes][:state] = state
111
+ end
112
+
113
+ opts.on("--address-country COUNTRY", "The country name as it will be put in the address") do |country|
114
+ @options[:placemark][:address_attributes][:country] = country
115
+ end
116
+
117
+ opts.separator ""
118
+ opts.separator "Create a new placemark with command line arguments:"
119
+ opts.separator "#{$0} -n 'name of the tagcrumb' -x 9.012 -y 49.9"
120
+ opts.separator ""
121
+ opts.separator "Create a new placemark in interactive mode:"
122
+ opts.separator "#{$0}"
123
+ end
124
+ end
125
+
126
+ def output_version
127
+ @stdout.puts "#{$0} version #{Tagcrumbs::VERSION}"
128
+ end
129
+
130
+ def output_help
131
+ output_version
132
+ @stdout.puts
133
+ @stdout.puts option_parser.help
134
+ end
135
+
136
+ def verbose_puts(msg)
137
+ @stdout.puts msg if @options[:verbose]
138
+ end
139
+
140
+ def execute_authorize
141
+ @stdout.puts "Connecting to Tagcrumbs..."
142
+
143
+ authorize_url = Tagcrumbs.authorize_url
144
+ %x(open #{authorize_url})
145
+
146
+ @stdout.print "Please authorize the application in your browser: #{authorize_url}.\nPress Enter after you authorized the application to continue..."
147
+ @stdin.gets
148
+
149
+ if Tagcrumbs.exchange_request_token!
150
+ @stdout.puts "The application is now authorized. You won't have to do this again the next time you run the application."
151
+ @config_store.update(Tagcrumbs.options.slice(:access_token, :access_secret))
152
+ else
153
+ @stdout.puts "The application could not be authorized."
154
+ end
155
+ end
156
+
157
+ def execute_new_placemark
158
+ links = @options[:placemark].delete(:links)
159
+
160
+ placemark = Tagcrumbs::Placemark.new(@options[:placemark])
161
+
162
+ if placemark.save
163
+ verbose_puts "Saved: " + placemark.resource_url
164
+
165
+ if links.present?
166
+ links.each do |url|
167
+ link = Tagcrumbs::Link.new(:url => url, :placemark => placemark)
168
+
169
+ if link.save
170
+ verbose_puts "Saved Link: " + link.resource_url
171
+ else
172
+ @stderr.puts "** Link Errors: \n"
173
+ @stderr.puts link.errors.full_messages.join("\n")
174
+ end
175
+ end
176
+ end
177
+ else
178
+ @stderr.puts "** Placemark could not be created!"
179
+
180
+ @stderr.puts "** Placemark Errors: \n"
181
+ @stderr.puts placemark.errors.full_messages.join("\n")
182
+ end # end placemark.save
183
+ end
184
+
185
+ def execute_interactive_mode
186
+ @stdout.puts "== Create a new Placemark on tagcrumbs.com"
187
+ @stdout.puts "You can also use arguments to create a placemark, type #{$0} -h to find out how.\n"
188
+
189
+ begin
190
+ placemark = Tagcrumbs::Placemark.new
191
+
192
+ @stdout.print "Latitude: "
193
+ placemark.latitude = @stdin.gets.chomp
194
+
195
+ @stdout.print "Longitude: "
196
+ placemark.longitude = @stdin.gets.chomp
197
+
198
+ @stdout.print "\n== Loading suggestions... \n"
199
+ suggestions = placemark.suggestions
200
+
201
+ if suggestions.names.present?
202
+ @stdout.puts "\n== Select a name"
203
+
204
+ suggestions.names.each_with_index do |value, index|
205
+ @stdout.puts " #{index+1}. #{value}"
206
+ end
207
+
208
+ @stdout.puts " 0. Enter your own name"
209
+
210
+ @stdout.print "Select a number: "
211
+ number = @stdin.gets.chomp
212
+
213
+ if number.to_i == 0
214
+ @stdout.print "Enter a name: "
215
+ placemark.name = @stdin.gets.chomp
216
+ else
217
+ placemark.name = suggestions.names[number.to_i-1]
218
+ end
219
+ else
220
+ @stdout.puts "No suggestions found."
221
+ @stdout.print "Enter a name: "
222
+ placemark.name = @stdin.gets.chomp
223
+ end
224
+
225
+ @stdout.puts "\n== Enter tags (comma separated)"
226
+
227
+ @stdout.puts "Popular tags: #{suggestions.popular_tags.map{|t| t.name}.join(', ')}" if suggestions.popular_tags.present?
228
+ @stdout.puts "Recommended tags: #{suggestions.recommended_tags.map{|t| t.name}.join(', ')}" if suggestions.recommended_tags.present?
229
+ @stdout.print "Enter tags: "
230
+
231
+ placemark.tag_list = @stdin.gets.chomp
232
+
233
+ @stdout.print "\n== Enter notes: "
234
+ placemark.notes = @stdin.gets.chomp
235
+
236
+
237
+ if suggestions.cities.present?
238
+ @stdout.puts "\n== Which city?"
239
+
240
+ suggestions.cities.each_with_index do |city, index|
241
+ @stdout.puts " #{index+1}. #{city.name}"
242
+ end
243
+
244
+ @stdout.puts " 0. Middle of Nowhere"
245
+
246
+ @stdout.print "Select a city: "
247
+ number = @stdin.gets.chomp
248
+ placemark.city = (number == '0') ? nil : suggestions.cities[number.to_i-1]
249
+ end
250
+
251
+ @stdout.print "== Save the placemark? (y/n): "
252
+ save = @stdin.gets.chomp
253
+
254
+ if placemark.save
255
+ @stdout.puts "Saved (#{placemark.resource_url})"
256
+ else
257
+ @stdout.puts "Could not be saved:"
258
+ @stderr.puts placemark.errors.full_messages.join("\n")
259
+ end
260
+
261
+ @stdout.print "== Create another placemark? (y/n): "
262
+ start_over = @stdin.gets.chomp
263
+ end while(start_over == 'y')
264
+
265
+ end
266
+
267
+ end
268
+ end
@@ -0,0 +1,35 @@
1
+ module Tagcrumbs
2
+ # Use the ConfigStore to save configuration options to a yaml file (tokens)
3
+ class ConfigStore
4
+ attr_accessor :filename, :options
5
+
6
+ def initialize(filename, options ={})
7
+ @filename = filename
8
+ @options = options
9
+ end
10
+
11
+ # Load a filename and read it with YAML
12
+ def self.load(filename)
13
+ config = File.exist?(filename) ? YAML::load(open(filename)) : {}
14
+ new(filename, config)
15
+ end
16
+
17
+ # Update the current options and save the file
18
+ def update(c={})
19
+ @options.merge!(c)
20
+ save
21
+ self
22
+ end
23
+
24
+ # Save the current options into the file in YAML format
25
+ def save
26
+ File.open(filename, 'w') { |f| f.write(YAML.dump(@options)) }
27
+ self
28
+ end
29
+ end
30
+
31
+ end
32
+
33
+
34
+
35
+
@@ -0,0 +1,13 @@
1
+ module Tagcrumbs
2
+ class UnsupportedFormat < StandardError; end
3
+
4
+ class CreateURLMissing < StandardError; end
5
+ class ResourceURLMissing < StandardError; end
6
+
7
+ class CreateImpossible < StandardError; end
8
+ class UpdateImpossible < StandardError; end
9
+ class DestroyImpossible < StandardError; end
10
+
11
+ class InvalidRequest < StandardError; end
12
+
13
+ end
@@ -0,0 +1,43 @@
1
+ module Tagcrumbs
2
+ class Node
3
+ attr_accessor :name, :value
4
+ attr_writer :properties
5
+
6
+ def initialize(name, value, properties={})
7
+ self.name = name
8
+ self.properties = properties
9
+ self.value = typecast_value(value)
10
+ end
11
+
12
+ # The XML/JSON properties of a node, they are used to typecast the value and kept around
13
+ # so that it is also possible to look at them later
14
+ def properties
15
+ @properties ||= {}
16
+ end
17
+
18
+ private
19
+ # typecast value depending on the properties that are set and read from the webservice
20
+ def typecast_value(value)
21
+ return nil if value.nil?
22
+
23
+ if value.is_a?(String)
24
+ if properties['nil'] == 'true'
25
+ nil
26
+ else
27
+ case properties['type']
28
+ when 'timestamp': value.to_time
29
+ when 'float': value.to_f
30
+ when 'integer': value.to_i
31
+ when 'boolean': value == 'true' ? true : false
32
+ else
33
+ coder = HTMLEntities.new
34
+ coder.decode(value)
35
+ end
36
+ end
37
+ else
38
+ value
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,79 @@
1
+ module Tagcrumbs
2
+ # A parser for JSON
3
+ class JsonParser < Parser
4
+ attr_accessor :_name # save the name of the json hash
5
+
6
+ def initialize(parser_or_document)
7
+ if parser_or_document.is_a?(String)
8
+ tmp = JSON.parse(parser_or_document)
9
+ self._name = tmp.keys.first
10
+ self.document = tmp[self._name]
11
+ elsif(parser_or_document.is_a?(Tagcrumbs::JsonParser))
12
+ self.document = parser_or_document.document
13
+ else
14
+ self.document = parser_or_document
15
+ end
16
+
17
+ self.format = :json
18
+ end
19
+
20
+ # Get a node with a specific name from the document
21
+ def get_node(name)
22
+ self.document[name.to_s]
23
+ end
24
+
25
+ # Is this a stub and therefore only a "link" in the webservice or is a resource loaded?
26
+ def node_loaded?
27
+ badgerfish_json_loaded?
28
+ end
29
+
30
+ # Get all child nodes with a specific name
31
+ def nodes_with_name(name)
32
+ self.document[name] || []
33
+ end
34
+
35
+ # Get all attributes from the badgerfish JSON that (all keys starting with '@')
36
+ def get_attributes
37
+ attributes_from_badgerfish_json
38
+ end
39
+
40
+ # Get one attribute from the JSON
41
+ def get_attribute(attribute)
42
+ self.document["@#{attribute}"]
43
+ end
44
+
45
+ # Get the value of an element ('$' key in badgerfish JSON)
46
+ def get_value
47
+ self.document['$']
48
+ end
49
+
50
+ # Get the name of the current element
51
+ def name
52
+ _name || self.document.keys.select{|k| k.first != '@'}.first
53
+ end
54
+
55
+ private
56
+
57
+ def attributes_from_badgerfish_json
58
+ hash = {}
59
+
60
+ self.document.each do |k, v|
61
+ if k.first == '@'
62
+ hash[k[1..-1]] = v
63
+ end
64
+ end
65
+
66
+ hash
67
+ end
68
+
69
+ # is there anything else besides badgerfish style attributes?
70
+ def badgerfish_json_loaded?
71
+ self.document.each do |k, v|
72
+ return true if k.first != '@'
73
+ end
74
+
75
+ false
76
+ end
77
+
78
+ end
79
+ end
@@ -0,0 +1,24 @@
1
+ module Tagcrumbs
2
+ # Parser is an abstract class that provides common functions for all parsers and that helps to get a correct parser for
3
+ # a specific format
4
+ class Parser
5
+ attr_accessor :document, :format
6
+
7
+ # Create a parser for a specific format (xml or json)
8
+ # +document_or_parser+ can either be a Parser or a document, if it is a Parser it will be returned as it is,
9
+ # otherwise a parser will be created out of the document
10
+ def self.new_for_format(document_or_parser, format)
11
+ if document_or_parser.is_a?(Tagcrumbs::Parser) # allow to also pass a document right into the method
12
+ document_or_parser
13
+ else
14
+ case format
15
+ when :xml: XmlParser.new(document_or_parser)
16
+ when :json: JsonParser.new(document_or_parser)
17
+ else
18
+ raise UnsupportedFormat
19
+ end
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,54 @@
1
+ module Tagcrumbs
2
+ class XmlParser < Parser
3
+
4
+ def initialize(parser_or_document)
5
+ if parser_or_document.is_a?(String)
6
+ xml = Hpricot.XML(parser_or_document)
7
+ self.document = xml.root ? xml.root : xml
8
+ elsif(parser_or_document.is_a?(Tagcrumbs::XmlParser))
9
+ self.document = parser_or_document.document
10
+ else
11
+ self.document = parser_or_document
12
+ end
13
+
14
+ self.format = :xml
15
+ end
16
+
17
+ # Get a node with a specific name from the document
18
+ def get_node(name)
19
+ self.document.children_of_type(name.to_s).first
20
+ end
21
+
22
+ # Does the document contain a resource or is it just a "link" in the webservice?
23
+ def node_loaded?
24
+ #self.document.innerText.present?
25
+ !self.document.empty?
26
+ end
27
+
28
+ # Get all children with name
29
+ def nodes_with_name(name)
30
+ self.document.children_of_type(name)
31
+ end
32
+
33
+ # Get all attributes
34
+ def get_attributes
35
+ self.document.attributes
36
+ end
37
+
38
+ # Get one specific attribute
39
+ def get_attribute(attribute)
40
+ self.document.get_attribute(attribute)
41
+ end
42
+
43
+ # Get the value of the tag
44
+ def get_value
45
+ self.document.innerHTML
46
+ end
47
+
48
+ # Get the name of the document
49
+ def name
50
+ self.document.name
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,32 @@
1
+ module Tagcrumbs
2
+ # The Proxy class is used in resource associations to delay loading of a resource for as long as possible.
3
+ # Some method calls are directly forwarded to the resource, for others the resource is loaded first from the webservice
4
+ # and afterwards the call is made.
5
+ # The Proxy tries to be as transparent as possible so that you basically never know that you have a proxy object instead of a real resource.
6
+ class Proxy
7
+ UNPROXIED_METHODS = [:reload, :resource_url, :properties, :loaded?, :class] # TODO: figure out what other methods might be needed?
8
+
9
+ instance_methods.each { |m| undef_method m unless m =~ /^__/ } # http://onestepback.org/index.cgi/Tech/Ruby/BlankSlate.rdoc
10
+
11
+ def initialize(object)
12
+ @object = object
13
+ end
14
+
15
+
16
+ # Forward all method calls to the proxied object, reload if necessary
17
+ def method_missing(method_name, *args, &block)
18
+ #puts "** Proxying #{method_name}. Loaded: #{@object.loaded?}"
19
+
20
+ if @object.loaded? || UNPROXIED_METHODS.include?(method_name) #|| !@object.respond_to?(method_name) # having this doesn't allow first etc on arrays
21
+ @object.send(method_name, *args, &block)
22
+ else
23
+ #puts "** Reloading"
24
+ @object.reload if @object.resource_url.present?
25
+ @object.send(method_name, *args, &block)
26
+ end
27
+
28
+
29
+ end
30
+ end
31
+
32
+ end