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,301 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
class ResourceInvalid# < ClientError #:nodoc:
|
|
3
|
+
end
|
|
4
|
+
|
|
5
|
+
# == Borrowed from ActiveResource
|
|
6
|
+
|
|
7
|
+
# Active Resource validation is reported to and from this object, which is used by Base#save
|
|
8
|
+
# to determine whether the object in a valid state to be saved. See usage example in Validations.
|
|
9
|
+
class Errors
|
|
10
|
+
include Enumerable
|
|
11
|
+
attr_reader :errors
|
|
12
|
+
|
|
13
|
+
delegate :empty?, :to => :errors
|
|
14
|
+
|
|
15
|
+
def initialize(base) # :nodoc:
|
|
16
|
+
@base, @errors = base, {}
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Add an error to the base Active Resource object rather than an attribute.
|
|
20
|
+
#
|
|
21
|
+
# ==== Examples
|
|
22
|
+
# my_folder = Folder.find(1)
|
|
23
|
+
# my_folder.errors.add_to_base("You can't edit an existing folder")
|
|
24
|
+
# my_folder.errors.on_base
|
|
25
|
+
# # => "You can't edit an existing folder"
|
|
26
|
+
#
|
|
27
|
+
# my_folder.errors.add_to_base("This folder has been tagged as frozen")
|
|
28
|
+
# my_folder.valid?
|
|
29
|
+
# # => false
|
|
30
|
+
# my_folder.errors.on_base
|
|
31
|
+
# # => ["You can't edit an existing folder", "This folder has been tagged as frozen"]
|
|
32
|
+
#
|
|
33
|
+
def add_to_base(msg)
|
|
34
|
+
add(:base, msg)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Adds an error to an Active Resource object's attribute (named for the +attribute+ parameter)
|
|
38
|
+
# with the error message in +msg+.
|
|
39
|
+
#
|
|
40
|
+
# ==== Examples
|
|
41
|
+
# my_resource = Node.find(1)
|
|
42
|
+
# my_resource.errors.add('name', 'can not be "base"') if my_resource.name == 'base'
|
|
43
|
+
# my_resource.errors.on('name')
|
|
44
|
+
# # => 'can not be "base"!'
|
|
45
|
+
#
|
|
46
|
+
# my_resource.errors.add('desc', 'can not be blank') if my_resource.desc == ''
|
|
47
|
+
# my_resource.valid?
|
|
48
|
+
# # => false
|
|
49
|
+
# my_resource.errors.on('desc')
|
|
50
|
+
# # => 'can not be blank!'
|
|
51
|
+
#
|
|
52
|
+
def add(attribute, msg)
|
|
53
|
+
@errors[attribute.to_s] = [] if @errors[attribute.to_s].nil?
|
|
54
|
+
@errors[attribute.to_s] << msg
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Returns true if the specified +attribute+ has errors associated with it.
|
|
58
|
+
#
|
|
59
|
+
# ==== Examples
|
|
60
|
+
# my_resource = Disk.find(1)
|
|
61
|
+
# my_resource.errors.add('location', 'must be Main') unless my_resource.location == 'Main'
|
|
62
|
+
# my_resource.errors.on('location')
|
|
63
|
+
# # => 'must be Main!'
|
|
64
|
+
#
|
|
65
|
+
# my_resource.errors.invalid?('location')
|
|
66
|
+
# # => true
|
|
67
|
+
# my_resource.errors.invalid?('name')
|
|
68
|
+
# # => false
|
|
69
|
+
def invalid?(attribute)
|
|
70
|
+
!@errors[attribute.to_s].nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
# A method to return the errors associated with +attribute+, which returns nil, if no errors are
|
|
74
|
+
# associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
|
|
75
|
+
# or an array of error messages if more than one error is associated with the specified +attribute+.
|
|
76
|
+
#
|
|
77
|
+
# ==== Examples
|
|
78
|
+
# my_person = Person.new(params[:person])
|
|
79
|
+
# my_person.errors.on('login')
|
|
80
|
+
# # => nil
|
|
81
|
+
#
|
|
82
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
83
|
+
# my_person.errors.on('login')
|
|
84
|
+
# # => 'can not be empty'
|
|
85
|
+
#
|
|
86
|
+
# my_person.errors.add('login', 'can not be longer than 10 characters') if my_person.login.length > 10
|
|
87
|
+
# my_person.errors.on('login')
|
|
88
|
+
# # => ['can not be empty', 'can not be longer than 10 characters']
|
|
89
|
+
def on(attribute)
|
|
90
|
+
errors = @errors[attribute.to_s]
|
|
91
|
+
return nil if errors.nil?
|
|
92
|
+
errors.size == 1 ? errors.first : errors
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
alias :[] :on
|
|
96
|
+
|
|
97
|
+
# A method to return errors assigned to +base+ object through add_to_base, which returns nil, if no errors are
|
|
98
|
+
# associated with the specified +attribute+, the error message if one error is associated with the specified +attribute+,
|
|
99
|
+
# or an array of error messages if more than one error is associated with the specified +attribute+.
|
|
100
|
+
#
|
|
101
|
+
# ==== Examples
|
|
102
|
+
# my_account = Account.find(1)
|
|
103
|
+
# my_account.errors.on_base
|
|
104
|
+
# # => nil
|
|
105
|
+
#
|
|
106
|
+
# my_account.errors.add_to_base("This account is frozen")
|
|
107
|
+
# my_account.errors.on_base
|
|
108
|
+
# # => "This account is frozen"
|
|
109
|
+
#
|
|
110
|
+
# my_account.errors.add_to_base("This account has been closed")
|
|
111
|
+
# my_account.errors.on_base
|
|
112
|
+
# # => ["This account is frozen", "This account has been closed"]
|
|
113
|
+
#
|
|
114
|
+
def on_base
|
|
115
|
+
on(:base)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Yields each attribute and associated message per error added.
|
|
119
|
+
#
|
|
120
|
+
# ==== Examples
|
|
121
|
+
# my_person = Person.new(params[:person])
|
|
122
|
+
#
|
|
123
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
124
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
|
125
|
+
# messages = ''
|
|
126
|
+
# my_person.errors.each {|attr, msg| messages += attr.to_s.humanize + " " + msg + "<br />"}
|
|
127
|
+
# messages
|
|
128
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
|
129
|
+
#
|
|
130
|
+
def each
|
|
131
|
+
@errors.each_key { |attr| @errors[attr].each { |msg| yield attr, msg } }
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Yields each full error message added. So Person.errors.add("first_name", "can't be empty") will be returned
|
|
135
|
+
# through iteration as "First name can't be empty".
|
|
136
|
+
#
|
|
137
|
+
# ==== Examples
|
|
138
|
+
# my_person = Person.new(params[:person])
|
|
139
|
+
#
|
|
140
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
141
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
|
142
|
+
# messages = ''
|
|
143
|
+
# my_person.errors.each_full {|msg| messages += msg + "<br/>"}
|
|
144
|
+
# messages
|
|
145
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
|
146
|
+
#
|
|
147
|
+
def each_full
|
|
148
|
+
full_messages.each { |msg| yield msg }
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Returns all the full error messages in an array.
|
|
152
|
+
#
|
|
153
|
+
# ==== Examples
|
|
154
|
+
# my_person = Person.new(params[:person])
|
|
155
|
+
#
|
|
156
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
157
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
|
158
|
+
# messages = ''
|
|
159
|
+
# my_person.errors.full_messages.each {|msg| messages += msg + "<br/>"}
|
|
160
|
+
# messages
|
|
161
|
+
# # => "Login can not be empty<br />Password can not be empty<br />"
|
|
162
|
+
#
|
|
163
|
+
def full_messages
|
|
164
|
+
full_messages = []
|
|
165
|
+
|
|
166
|
+
@errors.each_key do |attr|
|
|
167
|
+
@errors[attr].each do |msg|
|
|
168
|
+
next if msg.nil?
|
|
169
|
+
|
|
170
|
+
if attr == "base"
|
|
171
|
+
full_messages << msg
|
|
172
|
+
else
|
|
173
|
+
full_messages << [attr.to_s.humanize, msg].join(' ')
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
end
|
|
177
|
+
full_messages
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def clear
|
|
181
|
+
@errors = {}
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Returns the total number of errors added. Two errors added to the same attribute will be counted as such
|
|
185
|
+
# with this as well.
|
|
186
|
+
#
|
|
187
|
+
# ==== Examples
|
|
188
|
+
# my_person = Person.new(params[:person])
|
|
189
|
+
# my_person.errors.size
|
|
190
|
+
# # => 0
|
|
191
|
+
#
|
|
192
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
193
|
+
# my_person.errors.add('password', 'can not be empty') if my_person.password == ''
|
|
194
|
+
# my_person.error.size
|
|
195
|
+
# # => 2
|
|
196
|
+
#
|
|
197
|
+
def size
|
|
198
|
+
@errors.values.inject(0) { |error_count, attribute| error_count + attribute.size }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
alias_method :count, :size
|
|
202
|
+
alias_method :length, :size
|
|
203
|
+
|
|
204
|
+
def from_format(document, format)
|
|
205
|
+
case format
|
|
206
|
+
when :xml: from_xml(document)
|
|
207
|
+
when :json: from_json(document)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
# Grabs errors from the XML response.
|
|
212
|
+
def from_xml(xml)
|
|
213
|
+
clear
|
|
214
|
+
humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.to_s.humanize => attr_name) }
|
|
215
|
+
messages = ::Array.wrap(Hash.from_xml(xml)['errors']['error']) rescue []
|
|
216
|
+
messages.each do |message|
|
|
217
|
+
attr_message = humanized_attributes.keys.detect do |attr_name|
|
|
218
|
+
if message[0, attr_name.size + 1] == "#{attr_name} "
|
|
219
|
+
add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
add_to_base message if attr_message.nil?
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Grabs errors from the JSON response.
|
|
228
|
+
# Added for Tagcrumbs
|
|
229
|
+
def from_json(json)
|
|
230
|
+
clear
|
|
231
|
+
humanized_attributes = @base.attributes.keys.inject({}) { |h, attr_name| h.update(attr_name.to_s.humanize => attr_name) }
|
|
232
|
+
|
|
233
|
+
messages = JSON.parse(json)['errors']['error'] rescue []
|
|
234
|
+
messages.each do |message|
|
|
235
|
+
attr_message = humanized_attributes.keys.detect do |attr_name|
|
|
236
|
+
if message[0, attr_name.size + 1] == "#{attr_name} "
|
|
237
|
+
add humanized_attributes[attr_name], message[(attr_name.size + 1)..-1]
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
add_to_base message if attr_message.nil?
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
# Module to support validation and errors with Active Resource objects. The module overrides
|
|
247
|
+
# Base#save to rescue ActiveResource::ResourceInvalid exceptions and parse the errors returned
|
|
248
|
+
# in the web service response. The module also adds an +errors+ collection that mimics the interface
|
|
249
|
+
# of the errors provided by ActiveRecord::Errors.
|
|
250
|
+
#
|
|
251
|
+
# ==== Example
|
|
252
|
+
#
|
|
253
|
+
# Consider a Person resource on the server requiring both a +first_name+ and a +last_name+ with a
|
|
254
|
+
# <tt>validates_presence_of :first_name, :last_name</tt> declaration in the model:
|
|
255
|
+
#
|
|
256
|
+
# person = Person.new(:first_name => "Jim", :last_name => "")
|
|
257
|
+
# person.save # => false (server returns an HTTP 422 status code and errors)
|
|
258
|
+
# person.valid? # => false
|
|
259
|
+
# person.errors.empty? # => false
|
|
260
|
+
# person.errors.count # => 1
|
|
261
|
+
# person.errors.full_messages # => ["Last name can't be empty"]
|
|
262
|
+
# person.errors.on(:last_name) # => "can't be empty"
|
|
263
|
+
# person.last_name = "Halpert"
|
|
264
|
+
# person.save # => true (and person is now saved to the remote service)
|
|
265
|
+
#
|
|
266
|
+
module Validations
|
|
267
|
+
# def self.included(base) # :nodoc:
|
|
268
|
+
# base.class_eval do
|
|
269
|
+
# alias_method_chain :save, :validation
|
|
270
|
+
# end
|
|
271
|
+
# end
|
|
272
|
+
#
|
|
273
|
+
# # Validate a resource and save (POST) it to the remote web service.
|
|
274
|
+
# def save_with_validation
|
|
275
|
+
# save_without_validation
|
|
276
|
+
# true
|
|
277
|
+
# rescue ResourceInvalid => error
|
|
278
|
+
# errors.from_xml(error.response.body)
|
|
279
|
+
# false
|
|
280
|
+
# end
|
|
281
|
+
|
|
282
|
+
# Checks for errors on an object (i.e., is resource.errors empty?).
|
|
283
|
+
#
|
|
284
|
+
# ==== Examples
|
|
285
|
+
# my_person = Person.create(params[:person])
|
|
286
|
+
# my_person.valid?
|
|
287
|
+
# # => true
|
|
288
|
+
#
|
|
289
|
+
# my_person.errors.add('login', 'can not be empty') if my_person.login == ''
|
|
290
|
+
# my_person.valid?
|
|
291
|
+
# # => false
|
|
292
|
+
def valid?
|
|
293
|
+
errors.empty?
|
|
294
|
+
end
|
|
295
|
+
|
|
296
|
+
# Returns the Errors object that holds all information about attribute error messages.
|
|
297
|
+
def errors
|
|
298
|
+
@errors ||= Errors.new(self)
|
|
299
|
+
end
|
|
300
|
+
end
|
|
301
|
+
end
|
data/lib/tagcrumbs.rb
ADDED
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
|
2
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
3
|
+
|
|
4
|
+
require 'rubygems'
|
|
5
|
+
#require 'ruby-debug'
|
|
6
|
+
|
|
7
|
+
gem 'activesupport', '2.3.2'
|
|
8
|
+
require 'active_support'
|
|
9
|
+
|
|
10
|
+
gem 'hpricot', '0.8.1'
|
|
11
|
+
require 'hpricot'
|
|
12
|
+
|
|
13
|
+
gem 'htmlentities', '4.0.0'
|
|
14
|
+
require 'htmlentities'
|
|
15
|
+
|
|
16
|
+
gem 'json', '1.1.6'
|
|
17
|
+
require 'JSON'
|
|
18
|
+
|
|
19
|
+
gem 'oauth', '0.3.4'
|
|
20
|
+
require 'oauth'
|
|
21
|
+
|
|
22
|
+
gem 'mime-types', '1.16'
|
|
23
|
+
require 'mime/types'
|
|
24
|
+
|
|
25
|
+
gem 'builder', '2.1.2'
|
|
26
|
+
require 'builder'
|
|
27
|
+
|
|
28
|
+
# Inflections must be available before classes are loaded, needed to automatically find the correct classes
|
|
29
|
+
ActiveSupport::Inflector.inflections do |inflect|
|
|
30
|
+
inflect.irregular 'address', 'address'
|
|
31
|
+
inflect.uncountable "settings"
|
|
32
|
+
inflect.uncountable "suggestions"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
require 'tagcrumbs/validations'
|
|
36
|
+
require 'tagcrumbs/config_store'
|
|
37
|
+
require 'tagcrumbs/exceptions'
|
|
38
|
+
|
|
39
|
+
# parsers
|
|
40
|
+
require 'tagcrumbs/parsers/parser'
|
|
41
|
+
require 'tagcrumbs/parsers/json_parser'
|
|
42
|
+
require 'tagcrumbs/parsers/xml_parser'
|
|
43
|
+
|
|
44
|
+
# builders
|
|
45
|
+
require 'tagcrumbs/builders/builder'
|
|
46
|
+
require 'tagcrumbs/builders/json_builder'
|
|
47
|
+
require 'tagcrumbs/builders/xml_builder'
|
|
48
|
+
|
|
49
|
+
# general
|
|
50
|
+
require 'tagcrumbs/node'
|
|
51
|
+
require 'tagcrumbs/requestor'
|
|
52
|
+
require 'tagcrumbs/proxy'
|
|
53
|
+
|
|
54
|
+
# resources
|
|
55
|
+
require 'tagcrumbs/resources/resource'
|
|
56
|
+
require 'tagcrumbs/resources/array'
|
|
57
|
+
|
|
58
|
+
# models
|
|
59
|
+
require 'tagcrumbs/resources/models/accessors'
|
|
60
|
+
require 'tagcrumbs/resources/models/model'
|
|
61
|
+
require 'tagcrumbs/resources/models/tagcrumb'
|
|
62
|
+
require 'tagcrumbs/resources/models/place'
|
|
63
|
+
|
|
64
|
+
require 'tagcrumbs/resources/models/address'
|
|
65
|
+
require 'tagcrumbs/resources/models/activity'
|
|
66
|
+
require 'tagcrumbs/resources/models/city'
|
|
67
|
+
require 'tagcrumbs/resources/models/comment'
|
|
68
|
+
require 'tagcrumbs/resources/models/country'
|
|
69
|
+
require 'tagcrumbs/resources/models/fanship'
|
|
70
|
+
require 'tagcrumbs/resources/models/favorite'
|
|
71
|
+
require 'tagcrumbs/resources/models/filter'
|
|
72
|
+
require 'tagcrumbs/resources/models/flag'
|
|
73
|
+
require 'tagcrumbs/resources/models/friendship'
|
|
74
|
+
require 'tagcrumbs/resources/models/geoname'
|
|
75
|
+
require 'tagcrumbs/resources/models/link'
|
|
76
|
+
require 'tagcrumbs/resources/models/location'
|
|
77
|
+
require 'tagcrumbs/resources/models/picture'
|
|
78
|
+
require 'tagcrumbs/resources/models/placemark'
|
|
79
|
+
require 'tagcrumbs/resources/models/profile'
|
|
80
|
+
require 'tagcrumbs/resources/models/profile_link'
|
|
81
|
+
require 'tagcrumbs/resources/models/root'
|
|
82
|
+
require 'tagcrumbs/resources/models/settings'
|
|
83
|
+
require 'tagcrumbs/resources/models/state'
|
|
84
|
+
require 'tagcrumbs/resources/models/subscription'
|
|
85
|
+
require 'tagcrumbs/resources/models/suggestions'
|
|
86
|
+
require 'tagcrumbs/resources/models/tag'
|
|
87
|
+
require 'tagcrumbs/resources/models/user'
|
|
88
|
+
require 'tagcrumbs/resources/models/user_recommendation'
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
module Tagcrumbs
|
|
92
|
+
VERSION = '0.3.1'
|
|
93
|
+
|
|
94
|
+
class << self
|
|
95
|
+
# Returns the default configuration options of the gem. You can overwrite the options with configure
|
|
96
|
+
def default_options
|
|
97
|
+
{
|
|
98
|
+
:consumer_token => 'ZQAR3nW5D3S5JgU8EAKEfA',
|
|
99
|
+
:consumer_secret => 'vMc7NA77eraJc9TwyImshEEMVgrEy4C8YURVlwoyrHE',
|
|
100
|
+
:format => :xml,
|
|
101
|
+
:consumer_options =>
|
|
102
|
+
{
|
|
103
|
+
:site => 'http://www.tagcrumbs.com'
|
|
104
|
+
#:site => 'http://localhost:2999'
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
# Returns the current configuration options
|
|
110
|
+
def options
|
|
111
|
+
@@options ||= default_options.with_indifferent_access
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
# Resets the configuration options to the default_options
|
|
115
|
+
def reset_options!
|
|
116
|
+
@@options = default_options.with_indifferent_access
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# Overwrite the default_options with your own options. You can specify:
|
|
120
|
+
#
|
|
121
|
+
# consumer_secret:: The OAuth consumer secret of your client that you registered on www.tagcrumbs.com
|
|
122
|
+
# consumer_token:: The OAuth consumer token of your client that you registered on www.tagcrumbs.com
|
|
123
|
+
# consumer_options:: Further Options that will be passed to the OAuth Gem
|
|
124
|
+
# request_token:: The OAuth request token that you received that you can exchange against an access token
|
|
125
|
+
# request_secret:: The OAuth request secret that you received that you can exchange against an access token
|
|
126
|
+
# format:: The Tagcrumbs webservice supports XML and JSON. You can specify the format of your requests here
|
|
127
|
+
def configure(new_options={})
|
|
128
|
+
new_options.symbolize_keys!
|
|
129
|
+
new_options.assert_valid_keys(:consumer_secret, :consumer_token, :consumer_options,
|
|
130
|
+
:access_token, :access_secret,
|
|
131
|
+
:request_token, :request_secret,
|
|
132
|
+
:format)
|
|
133
|
+
|
|
134
|
+
self.options.merge!(new_options)
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
# A request_token that you can let the user authorize, alternatively you can directly use the authorize_url
|
|
139
|
+
def request_token
|
|
140
|
+
return @@request_token if defined?(@@request_token)
|
|
141
|
+
@@request_token = Requestor.new.request_token
|
|
142
|
+
|
|
143
|
+
options[:request_token] = @@request_token.token
|
|
144
|
+
options[:request_secret] = @@request_token.secret
|
|
145
|
+
|
|
146
|
+
@@request_token
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# An URL that you should redirect the user to or that you should open in a browser on which the user has to approve access
|
|
150
|
+
# for your application
|
|
151
|
+
def authorize_url
|
|
152
|
+
request_token.authorize_url
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# After you redirected the user to the authorize_url and after the user authorized your application you can exchange
|
|
156
|
+
# the request_token against an access_token that you can use to make all further signed requests.
|
|
157
|
+
# You should use configure to pass in your access_token and access_secret for all further web service calls.
|
|
158
|
+
def exchange_request_token!
|
|
159
|
+
return @@access_token if defined?(@@access_token)
|
|
160
|
+
@@access_token = request_token.get_access_token
|
|
161
|
+
|
|
162
|
+
options[:access_token] = @@access_token.token
|
|
163
|
+
options[:access_secret] = @@access_token.secret
|
|
164
|
+
|
|
165
|
+
@@access_token
|
|
166
|
+
rescue OAuth::Unauthorized
|
|
167
|
+
nil
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Returns the current user that approved the application and owns the current access_token
|
|
171
|
+
# This is also used internally to automatically request all the URLs to POST new resources to.
|
|
172
|
+
def current_user
|
|
173
|
+
@@current_user ||= User.load('/you')
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Reads the index.(xml|json) file and automatically useses the URLs for further other queries
|
|
177
|
+
def root
|
|
178
|
+
@@root ||= Root.load('/index')
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Constants that are used to validate query parameters in find and search queries
|
|
182
|
+
def constants
|
|
183
|
+
@@constants ||= {
|
|
184
|
+
:per_page => [1, 5, 7, 10, 25, 50, 100],
|
|
185
|
+
:from => [:you, :friends, :fans, :recommendations, :everybody, :user],
|
|
186
|
+
:tagcrumb_type => ['Placemark', 'Favorite'],
|
|
187
|
+
:within => [0.015, 0.15, 0.55],
|
|
188
|
+
:sort => [:date, :distance, :name, :favorites, :comments],
|
|
189
|
+
:order => [:asc, :desc],
|
|
190
|
+
:w => [:unknown, :country, :state, :city, :zip, :street, :intersection, :address, :premise, :nearby, :everywhere]
|
|
191
|
+
}.with_indifferent_access
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
# Validate that key has a valid value. Returns an error if it does not. The constants method returns the valid values.
|
|
195
|
+
def validate(key, value)
|
|
196
|
+
if constants[key]
|
|
197
|
+
raise ArgumentError.new("#{key} must be one of the following: #{constants[key].join(', ')}") unless constants[key].include?(value)
|
|
198
|
+
true
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
# Returns a Tagcrumbs Class by name
|
|
203
|
+
def class_by_type(class_name)
|
|
204
|
+
"Tagcrumbs::#{class_name.classify}".constantize
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# The supported formats of the Gem and their MIME types
|
|
208
|
+
def supported_formats
|
|
209
|
+
{
|
|
210
|
+
:xml => ['application/xml', 'text/xml'],
|
|
211
|
+
:json => ['application/json', 'text/json']
|
|
212
|
+
}
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# Checks whether there is a valid access_token and access_secret that can be used to make all further calls to the webservice.
|
|
216
|
+
def valid_credentials?
|
|
217
|
+
if options[:access_token] && options[:access_secret]
|
|
218
|
+
requestor = Requestor.new
|
|
219
|
+
requestor.get('/you')
|
|
220
|
+
|
|
221
|
+
true
|
|
222
|
+
else
|
|
223
|
+
false
|
|
224
|
+
end
|
|
225
|
+
rescue Net::HTTPServerException => error
|
|
226
|
+
requestor.response.code == '401' ? false : (raise error) # unauthorized
|
|
227
|
+
end
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
|
data/script/console
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# File: script/console
|
|
3
|
+
irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
|
|
4
|
+
|
|
5
|
+
libs = " -r irb/completion"
|
|
6
|
+
# Perhaps use a console_lib to store any extra methods I may want available in the cosole
|
|
7
|
+
# libs << " -r #{File.dirname(__FILE__) + '/../lib/console_lib/console_logger.rb'}"
|
|
8
|
+
libs << " -r #{File.dirname(__FILE__) + '/../lib/tagcrumbs.rb'}"
|
|
9
|
+
puts "Loading tagcrumbs gem"
|
|
10
|
+
exec "#{irb} #{libs} --simple-prompt"
|
data/script/destroy
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/destroy'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Destroy.new.run(ARGV)
|
data/script/generate
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
|
3
|
+
|
|
4
|
+
begin
|
|
5
|
+
require 'rubigen'
|
|
6
|
+
rescue LoadError
|
|
7
|
+
require 'rubygems'
|
|
8
|
+
require 'rubigen'
|
|
9
|
+
end
|
|
10
|
+
require 'rubigen/scripts/generate'
|
|
11
|
+
|
|
12
|
+
ARGV.shift if ['--help', '-h'].include?(ARGV[0])
|
|
13
|
+
RubiGen::Base.use_component_sources! [:rubygems, :newgem, :newgem_theme, :test_unit]
|
|
14
|
+
RubiGen::Scripts::Generate.new.run(ARGV)
|