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.
- data/History.txt +9 -0
- data/License.txt +22 -0
- data/Manifest.txt +177 -0
- data/PostInstall.txt +2 -0
- data/README.rdoc +45 -0
- data/Rakefile +35 -0
- data/TODO.txt +17 -0
- data/bin/tagcrumbs +5 -0
- data/examples/address_update.rb +15 -0
- data/examples/comment_new.rb +19 -0
- data/examples/favorite_new.rb +17 -0
- data/examples/filter_new.rb +15 -0
- data/examples/friendship_create.rb +19 -0
- data/examples/link_new.rb +16 -0
- data/examples/location_update.rb +16 -0
- data/examples/picture_update.rb +16 -0
- data/examples/placemark_new_simple.rb +24 -0
- data/examples/placemark_new_suggestions.rb +24 -0
- data/examples/profile_link_new.rb +14 -0
- data/examples/profile_update.rb +12 -0
- data/examples/request_authorization.rb +25 -0
- data/examples/settings_update.rb +12 -0
- data/examples/subscription_new.rb +15 -0
- data/examples/tagcrumbs_find.rb +25 -0
- data/examples/tagcrumbs_search.rb +7 -0
- data/examples/user.rb +28 -0
- data/examples/user_recommendation_new.rb +15 -0
- data/lib/tagcrumbs/builders/builder.rb +45 -0
- data/lib/tagcrumbs/builders/json_builder.rb +38 -0
- data/lib/tagcrumbs/builders/xml_builder.rb +30 -0
- data/lib/tagcrumbs/cli.rb +268 -0
- data/lib/tagcrumbs/config_store.rb +35 -0
- data/lib/tagcrumbs/exceptions.rb +13 -0
- data/lib/tagcrumbs/node.rb +43 -0
- data/lib/tagcrumbs/parsers/json_parser.rb +79 -0
- data/lib/tagcrumbs/parsers/parser.rb +24 -0
- data/lib/tagcrumbs/parsers/xml_parser.rb +54 -0
- data/lib/tagcrumbs/proxy.rb +32 -0
- data/lib/tagcrumbs/requestor.rb +140 -0
- data/lib/tagcrumbs/resources/array.rb +108 -0
- data/lib/tagcrumbs/resources/models/accessors.rb +252 -0
- data/lib/tagcrumbs/resources/models/activity.rb +19 -0
- data/lib/tagcrumbs/resources/models/address.rb +16 -0
- data/lib/tagcrumbs/resources/models/city.rb +13 -0
- data/lib/tagcrumbs/resources/models/comment.rb +12 -0
- data/lib/tagcrumbs/resources/models/country.rb +15 -0
- data/lib/tagcrumbs/resources/models/fanship.rb +12 -0
- data/lib/tagcrumbs/resources/models/favorite.rb +22 -0
- data/lib/tagcrumbs/resources/models/filter.rb +32 -0
- data/lib/tagcrumbs/resources/models/flag.rb +10 -0
- data/lib/tagcrumbs/resources/models/friendship.rb +12 -0
- data/lib/tagcrumbs/resources/models/geoname.rb +8 -0
- data/lib/tagcrumbs/resources/models/link.rb +13 -0
- data/lib/tagcrumbs/resources/models/location.rb +13 -0
- data/lib/tagcrumbs/resources/models/model.rb +148 -0
- data/lib/tagcrumbs/resources/models/picture.rb +28 -0
- data/lib/tagcrumbs/resources/models/place.rb +17 -0
- data/lib/tagcrumbs/resources/models/placemark.rb +30 -0
- data/lib/tagcrumbs/resources/models/profile.rb +16 -0
- data/lib/tagcrumbs/resources/models/profile_link.rb +12 -0
- data/lib/tagcrumbs/resources/models/root.rb +19 -0
- data/lib/tagcrumbs/resources/models/settings.rb +14 -0
- data/lib/tagcrumbs/resources/models/state.rb +14 -0
- data/lib/tagcrumbs/resources/models/subscription.rb +13 -0
- data/lib/tagcrumbs/resources/models/suggestions.rb +11 -0
- data/lib/tagcrumbs/resources/models/tag.rb +6 -0
- data/lib/tagcrumbs/resources/models/tagcrumb.rb +58 -0
- data/lib/tagcrumbs/resources/models/user.rb +101 -0
- data/lib/tagcrumbs/resources/models/user_recommendation.rb +18 -0
- data/lib/tagcrumbs/resources/resource.rb +76 -0
- data/lib/tagcrumbs/validations.rb +301 -0
- data/lib/tagcrumbs.rb +232 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/fixtures/activity.json +210 -0
- data/spec/fixtures/activity.xml +66 -0
- data/spec/fixtures/address.json +183 -0
- data/spec/fixtures/address.xml +58 -0
- data/spec/fixtures/city.json +88 -0
- data/spec/fixtures/city.xml +34 -0
- data/spec/fixtures/comment.json +286 -0
- data/spec/fixtures/comment.xml +87 -0
- data/spec/fixtures/country.json +32 -0
- data/spec/fixtures/country.xml +13 -0
- data/spec/fixtures/fanship.json +180 -0
- data/spec/fixtures/fanship.xml +54 -0
- data/spec/fixtures/favorite.json +332 -0
- data/spec/fixtures/favorite.xml +109 -0
- data/spec/fixtures/filter.json +243 -0
- data/spec/fixtures/filter.xml +80 -0
- data/spec/fixtures/friendship.json +180 -0
- data/spec/fixtures/friendship.xml +54 -0
- data/spec/fixtures/geoname.json +18 -0
- data/spec/fixtures/geoname.xml +6 -0
- data/spec/fixtures/json_parser.json +25 -0
- data/spec/fixtures/link.json +288 -0
- data/spec/fixtures/link.xml +88 -0
- data/spec/fixtures/location.json +204 -0
- data/spec/fixtures/location.xml +71 -0
- data/spec/fixtures/picture.json +154 -0
- data/spec/fixtures/picture.png +0 -0
- data/spec/fixtures/picture.xml +49 -0
- data/spec/fixtures/placemark.json +357 -0
- data/spec/fixtures/placemark.xml +58 -0
- data/spec/fixtures/placemarks.json +887 -0
- data/spec/fixtures/placemarks.xml +348 -0
- data/spec/fixtures/profile.json +282 -0
- data/spec/fixtures/profile.xml +100 -0
- data/spec/fixtures/profile_link.json +156 -0
- data/spec/fixtures/profile_link.xml +49 -0
- data/spec/fixtures/root.json +46 -0
- data/spec/fixtures/root.xml +14 -0
- data/spec/fixtures/settings.json +147 -0
- data/spec/fixtures/settings.xml +45 -0
- data/spec/fixtures/state.json +62 -0
- data/spec/fixtures/state.xml +23 -0
- data/spec/fixtures/subscription.json +283 -0
- data/spec/fixtures/subscription.xml +86 -0
- data/spec/fixtures/suggestions.json +1877 -0
- data/spec/fixtures/suggestions.xml +724 -0
- data/spec/fixtures/tag.json +9 -0
- data/spec/fixtures/tag.xml +4 -0
- data/spec/fixtures/user.json +105 -0
- data/spec/fixtures/user.xml +31 -0
- data/spec/fixtures/user_recommendation.json +349 -0
- data/spec/fixtures/user_recommendation.xml +106 -0
- data/spec/fixtures/validation_errors.json +5 -0
- data/spec/fixtures/validation_errors.xml +5 -0
- data/spec/fixtures/xml_parser.xml +13 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +75 -0
- data/spec/tagcrumbs/builders/builder_spec.rb +57 -0
- data/spec/tagcrumbs/builders/json_builder_spec.rb +47 -0
- data/spec/tagcrumbs/builders/xml_builder_spec.rb +34 -0
- data/spec/tagcrumbs/exceptions_spec.rb +16 -0
- data/spec/tagcrumbs/node_spec.rb +42 -0
- data/spec/tagcrumbs/parsers/json_parser_spec.rb +117 -0
- data/spec/tagcrumbs/parsers/parser_spec.rb +25 -0
- data/spec/tagcrumbs/parsers/xml_parser_spec.rb +117 -0
- data/spec/tagcrumbs/proxy_spec.rb +48 -0
- data/spec/tagcrumbs/requestor_spec.rb +62 -0
- data/spec/tagcrumbs/resources/array_spec.rb +62 -0
- data/spec/tagcrumbs/resources/models/accessors_spec.rb +123 -0
- data/spec/tagcrumbs/resources/models/activity_spec.rb +33 -0
- data/spec/tagcrumbs/resources/models/address_spec.rb +24 -0
- data/spec/tagcrumbs/resources/models/city_spec.rb +33 -0
- data/spec/tagcrumbs/resources/models/comment_spec.rb +23 -0
- data/spec/tagcrumbs/resources/models/country_spec.rb +26 -0
- data/spec/tagcrumbs/resources/models/fanship_spec.rb +28 -0
- data/spec/tagcrumbs/resources/models/favorite_spec.rb +28 -0
- data/spec/tagcrumbs/resources/models/filter_spec.rb +43 -0
- data/spec/tagcrumbs/resources/models/flag_spec.rb +5 -0
- data/spec/tagcrumbs/resources/models/friendship_spec.rb +28 -0
- data/spec/tagcrumbs/resources/models/geoname_spec.rb +18 -0
- data/spec/tagcrumbs/resources/models/link_spec.rb +24 -0
- data/spec/tagcrumbs/resources/models/location_spec.rb +28 -0
- data/spec/tagcrumbs/resources/models/model_spec.rb +27 -0
- data/spec/tagcrumbs/resources/models/picture_spec.rb +42 -0
- data/spec/tagcrumbs/resources/models/place_spec.rb +12 -0
- data/spec/tagcrumbs/resources/models/placemark_spec.rb +61 -0
- data/spec/tagcrumbs/resources/models/profile_link_spec.rb +29 -0
- data/spec/tagcrumbs/resources/models/profile_spec.rb +43 -0
- data/spec/tagcrumbs/resources/models/root_spec.rb +57 -0
- data/spec/tagcrumbs/resources/models/settings_spec.rb +32 -0
- data/spec/tagcrumbs/resources/models/state_spec.rb +30 -0
- data/spec/tagcrumbs/resources/models/subscription_spec.rb +28 -0
- data/spec/tagcrumbs/resources/models/suggestions_spec.rb +33 -0
- data/spec/tagcrumbs/resources/models/tag_spec.rb +16 -0
- data/spec/tagcrumbs/resources/models/tagcrumb_spec.rb +21 -0
- data/spec/tagcrumbs/resources/models/user_recommendation_spec.rb +35 -0
- data/spec/tagcrumbs/resources/models/user_spec.rb +128 -0
- data/spec/tagcrumbs/resources/resource_spec.rb +62 -0
- data/spec/tagcrumbs/validations_spec.rb +27 -0
- data/spec/tagcrumbs_spec.rb +103 -0
- data/tagcrumbs.gemspec +59 -0
- data/tasks/rspec.rake +21 -0
- 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
|