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,140 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# The Requestor class is used to make the actual requests to the Tagcrumbs web service
|
|
3
|
+
# It uses the OAuth gem to make the requests.
|
|
4
|
+
|
|
5
|
+
class Requestor
|
|
6
|
+
attr_accessor :options, :response
|
|
7
|
+
|
|
8
|
+
# A Requestor can have custom options. If no custom options are specified the general Tagcrumbs.options are used.
|
|
9
|
+
# You can use this to have a Requestor with different consumer tokens for example.
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
options.reverse_merge!(Tagcrumbs.options)
|
|
12
|
+
self.options = options.with_indifferent_access
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# ================
|
|
16
|
+
# = OAuth Tokens =
|
|
17
|
+
# ================
|
|
18
|
+
|
|
19
|
+
# Returns the OAuth consumer token
|
|
20
|
+
def consumer
|
|
21
|
+
@consumer ||= ::OAuth::Consumer.new(options[:consumer_token], options[:consumer_secret], options[:consumer_options])
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
# Requests a Request Token from the web service or builds it from the options
|
|
25
|
+
def request_token
|
|
26
|
+
return @request_token if defined?(@request_token)
|
|
27
|
+
|
|
28
|
+
if options[:request_token] && options[:request_secret]
|
|
29
|
+
@request_token = ::OAuth::RequestToken.new(consumer, options[:request_token], options[:request_secret])
|
|
30
|
+
else
|
|
31
|
+
@request_token = consumer.get_request_token
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Exchanges a Request Token against an Access Token that has to be saved and used for all further calls to the webservice.
|
|
36
|
+
def exchange_request_token!
|
|
37
|
+
@access_token = request_token.get_access_token
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Returns the configured access token. Calls should be made with the get, post, put and delete methods, direct access to this
|
|
41
|
+
# should usually not be needed.
|
|
42
|
+
def access_token
|
|
43
|
+
return @access_token if defined?(@access_token)
|
|
44
|
+
|
|
45
|
+
if options[:access_token] && options[:access_secret]
|
|
46
|
+
@access_token = ::OAuth::AccessToken.new(consumer, options[:access_token], options[:access_secret])
|
|
47
|
+
else
|
|
48
|
+
nil
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# ========
|
|
53
|
+
# = HTTP =
|
|
54
|
+
# ========
|
|
55
|
+
|
|
56
|
+
# Create a custom request to the webservice
|
|
57
|
+
# If an access token is configured it will be used to make webservice calls (3-legged oauth)
|
|
58
|
+
# If a consumer is configured and it is a get request only it will make 2-legged oauth calls
|
|
59
|
+
def request(http_method, path, query_string = {}, body = '', headers = {})
|
|
60
|
+
url = path_with_query_string(path, query_string)
|
|
61
|
+
arguments = body.present? ? [body, default_headers.merge(headers)] : [default_headers.merge(headers)]
|
|
62
|
+
|
|
63
|
+
if access_token # 3-legged request with access token
|
|
64
|
+
response = self.access_token.request(http_method, url, *arguments)
|
|
65
|
+
elsif http_method == :get && consumer # 2-legged consumer only request
|
|
66
|
+
response = self.consumer.request(:get, url, nil, {}, *arguments)
|
|
67
|
+
else
|
|
68
|
+
raise InvalidRequest.new("Setup the Access Token first or only make get requests with a valid consumer.")
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
handle_response(response)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Create a get request on path, optionally pass a hash of query parameters
|
|
75
|
+
# Can also be used with 2-legged oauth to request public resources
|
|
76
|
+
def get(path, query_string = {})
|
|
77
|
+
request(:get, path, query_string)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Post body to path, access_token needed
|
|
81
|
+
def post(path, body='')
|
|
82
|
+
request(:post, path, {}, body)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Put body to path, access_token needed
|
|
86
|
+
def put(path, body='')
|
|
87
|
+
request(:put, path, {}, body)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Delete a resource, access_token needed
|
|
91
|
+
def delete(path)
|
|
92
|
+
request(:delete, path)
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
# Return the format of the response object
|
|
96
|
+
def response_format
|
|
97
|
+
raise 'Request a resource first' unless response
|
|
98
|
+
|
|
99
|
+
Tagcrumbs.supported_formats.each do |format, content_types|
|
|
100
|
+
return format if content_types.include?(response.content_type)
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
:xml
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
# Build a URL from a path and a hash of query string parameters
|
|
108
|
+
def path_with_query_string(path, arguments = {})
|
|
109
|
+
if arguments.present?
|
|
110
|
+
path + '?' + arguments.to_param
|
|
111
|
+
else
|
|
112
|
+
path
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# Handle responses from the webservice, raise an error on unexpected HTTP response codes
|
|
117
|
+
def handle_response(response)
|
|
118
|
+
self.response = response
|
|
119
|
+
case response.code
|
|
120
|
+
when '200', '201', '422': # ok, created, invalid
|
|
121
|
+
response
|
|
122
|
+
else
|
|
123
|
+
response.error!
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Set the Content-Type and Accept HTTP Headers to the correct values depending on format
|
|
128
|
+
def default_headers(format = nil)
|
|
129
|
+
format ||= self.options[:format]
|
|
130
|
+
|
|
131
|
+
raise UnsupportedFormat unless Tagcrumbs.supported_formats[format]
|
|
132
|
+
|
|
133
|
+
{
|
|
134
|
+
"Content-Type" => Tagcrumbs.supported_formats[format].first,
|
|
135
|
+
"Accept" => Tagcrumbs.supported_formats[format].join(', ')
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
end
|
|
140
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Basically a regular Array that holds many Resources.
|
|
3
|
+
# Used to get collections from the webservice.
|
|
4
|
+
class Array < Resource
|
|
5
|
+
ATTRIBUTES = [:total_entries, :pages, :per_page, :page, :sort, :order, :next_page, :previous_page]
|
|
6
|
+
|
|
7
|
+
attr_accessor :array
|
|
8
|
+
attr_reader *ATTRIBUTES
|
|
9
|
+
|
|
10
|
+
# set instance variables and automatically convert them to real integers if they are an integer
|
|
11
|
+
ATTRIBUTES.each do |a|
|
|
12
|
+
define_method "#{a}=" do |value|
|
|
13
|
+
begin
|
|
14
|
+
typecasted_value = Integer(value)
|
|
15
|
+
rescue ArgumentError
|
|
16
|
+
typecasted_value = value
|
|
17
|
+
ensure
|
|
18
|
+
self.instance_variable_set("@#{a}", typecasted_value)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def initialize
|
|
24
|
+
reset!
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Reset the state of the Array
|
|
28
|
+
def reset!
|
|
29
|
+
super
|
|
30
|
+
@array = []
|
|
31
|
+
|
|
32
|
+
true
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
# Replace the Array with the next page of elements if there is one
|
|
36
|
+
def next_page!
|
|
37
|
+
reload(next_page) if next_page.present?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Replace the Array with the privious page of elements if there is one
|
|
41
|
+
def previous_page!
|
|
42
|
+
reload(previous_page) if previous_page.present?
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Iterate over all pages
|
|
46
|
+
def each_page(args={})
|
|
47
|
+
begin
|
|
48
|
+
yield(self)
|
|
49
|
+
end while(args[:reverse] == true ? previous_page! : next_page!)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Iterate over all items with pagination
|
|
53
|
+
def each_page_each_item(args={})
|
|
54
|
+
self.each_page(args) do |array|
|
|
55
|
+
array.each do |item|
|
|
56
|
+
yield item
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Set the properties and typecast some attributes automatically
|
|
63
|
+
def properties=(properties)
|
|
64
|
+
super
|
|
65
|
+
|
|
66
|
+
self.properties.each do |key, value|
|
|
67
|
+
send("#{key}=", value) if ATTRIBUTES.include?(key.to_sym)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# delegate unkown methods to the underlying array
|
|
72
|
+
def method_missing(name, *args, &block)
|
|
73
|
+
if @array.respond_to?(name)
|
|
74
|
+
@array.send(name, *args, &block)
|
|
75
|
+
else
|
|
76
|
+
super
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def inspect
|
|
81
|
+
@array.inspect
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Load data into the object from a document
|
|
85
|
+
def initialize_from_document(parser, format = Tagcrumbs.options[:format])
|
|
86
|
+
parser = super
|
|
87
|
+
|
|
88
|
+
if self.loaded?
|
|
89
|
+
nodes = parser.nodes_with_name(parser.name.singularize)
|
|
90
|
+
return if nodes.blank?
|
|
91
|
+
|
|
92
|
+
nodes.each do |node|
|
|
93
|
+
node_parser = Parser.new_for_format(node, parser.format)
|
|
94
|
+
node_type = node_parser.get_attribute('type')
|
|
95
|
+
|
|
96
|
+
if node_type # a nested model
|
|
97
|
+
@array << Tagcrumbs.class_by_type(node_type).new_from_document(node, parser.format)
|
|
98
|
+
else # a nested node
|
|
99
|
+
@array << node_parser.get_value
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,252 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
module Accessors
|
|
3
|
+
def self.included(base)
|
|
4
|
+
base.class_eval do
|
|
5
|
+
extend(ClassMethods)
|
|
6
|
+
|
|
7
|
+
# class variables that save the options for attributes and associations
|
|
8
|
+
class_inheritable_accessor :writeable_attributes_options
|
|
9
|
+
class_inheritable_accessor :readable_attributes_options
|
|
10
|
+
class_inheritable_accessor :has_one_associations_options
|
|
11
|
+
class_inheritable_accessor :has_many_associations_options
|
|
12
|
+
class_inheritable_accessor :can_be_options
|
|
13
|
+
|
|
14
|
+
self.can_be_options ||= []
|
|
15
|
+
self.writeable_attributes_options ||= {}
|
|
16
|
+
self.readable_attributes_options ||= {}
|
|
17
|
+
self.has_one_associations_options ||= {}
|
|
18
|
+
self.has_many_associations_options ||= {}
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# The nodes contain the values that were read from the web service
|
|
23
|
+
def nodes
|
|
24
|
+
@nodes ||= {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# nodes_updates contain the changes that were done to nodes
|
|
28
|
+
def nodes_updates
|
|
29
|
+
@nodes_updates ||= {}
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# has_one_associations contain the related objects
|
|
33
|
+
def has_one_associations
|
|
34
|
+
@has_one_associations ||= {}
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# has_one_associations_updates contain updates of the related objects
|
|
38
|
+
def has_one_associations_updates
|
|
39
|
+
@has_one_associations_updates ||= {}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# has_many_associations contains Arrays of further objects
|
|
43
|
+
def has_many_associations
|
|
44
|
+
@has_many_associations ||= {}
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# Restore back to the original state, discard any changes that were done after the object
|
|
48
|
+
# was loaded
|
|
49
|
+
def restore!
|
|
50
|
+
@nodes_updates = {}
|
|
51
|
+
@has_one_associations_updates = {}
|
|
52
|
+
|
|
53
|
+
true
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Set attributes with a hash
|
|
57
|
+
def attributes=(args={})
|
|
58
|
+
args.each do |k, v|
|
|
59
|
+
if respond_to?("#{k}=")
|
|
60
|
+
self.send("#{k}=", v)
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Return all attributes dynamically, pass :modifiable_only => true to only return
|
|
66
|
+
# modifiable arguments
|
|
67
|
+
def attributes(args={})
|
|
68
|
+
hash = {}
|
|
69
|
+
|
|
70
|
+
self.class.attributes(args).each do |k|
|
|
71
|
+
hash[k] = send(k)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
hash
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# Nice output for console testing
|
|
78
|
+
def inspect
|
|
79
|
+
attributes_for_inspect = []
|
|
80
|
+
attributes.each{|k, v| attributes_for_inspect << "#{k}: #{attribute_for_inspect(v)}"}
|
|
81
|
+
"#<#{self.class} #{attributes_for_inspect.join(', ')}>"
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def attribute_for_inspect(value)
|
|
85
|
+
if value.is_a?(String) && value.length > 50
|
|
86
|
+
"#{value[0..50]}...".inspect
|
|
87
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
|
88
|
+
%("#{value.iso8601}")
|
|
89
|
+
else
|
|
90
|
+
value.inspect
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Returns the class name without modules
|
|
95
|
+
def class_name
|
|
96
|
+
self.class.to_s.split('::').last
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
# Returns the URL to which a post request must be sent to create a new resource
|
|
100
|
+
def create_url
|
|
101
|
+
raise CreateImpossible unless can_be?(:created)
|
|
102
|
+
|
|
103
|
+
# convention over configuration, can also be overwritten in subclasses
|
|
104
|
+
has_many_association_name = class_name.tableize
|
|
105
|
+
|
|
106
|
+
if Tagcrumbs.current_user.respond_to?(has_many_association_name) && Tagcrumbs.current_user.send(has_many_association_name)
|
|
107
|
+
Tagcrumbs.current_user.send("#{has_many_association_name}_url")
|
|
108
|
+
else
|
|
109
|
+
raise CreateURLMissing
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
# Can this object be :created, :updated or :destroyed?
|
|
114
|
+
def can_be?(method)
|
|
115
|
+
self.class.can_be_options.include?(method.to_sym)
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
module ClassMethods
|
|
120
|
+
# Configure if objects of this class can be :created, :updated or :destroyed
|
|
121
|
+
def can_be(*args)
|
|
122
|
+
self.can_be_options = args
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Returns an array of all the attributes of a class, pass :modifiable_only => true to only get modifiable attributes
|
|
126
|
+
def attributes(args = {})
|
|
127
|
+
if args[:modifiable_only]
|
|
128
|
+
writeable_attributes_options.keys + modifiable_has_one_associations_options.keys
|
|
129
|
+
else
|
|
130
|
+
writeable_attributes_options.keys + readable_attributes_options.keys + modifiable_has_one_associations_options.keys
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
# Configure the writeable attributes of the class that will be sent to the server on a post or put
|
|
135
|
+
def writeable_attributes(*attributes)
|
|
136
|
+
attributes.each do |attribute|
|
|
137
|
+
define_method attribute do
|
|
138
|
+
if nodes_updates[attribute]
|
|
139
|
+
nodes_updates[attribute].value
|
|
140
|
+
elsif nodes[attribute]
|
|
141
|
+
nodes[attribute].value
|
|
142
|
+
else
|
|
143
|
+
nil
|
|
144
|
+
end
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
define_method "#{attribute}=" do |value|
|
|
148
|
+
self.nodes_updates[attribute] = Node.new(attribute, value)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
self.writeable_attributes_options[attribute] = nil
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Configure attributes that are only readable and that cannot be changed by the client
|
|
156
|
+
def readable_attributes(*attributes)
|
|
157
|
+
attributes.each do |attribute|
|
|
158
|
+
define_method attribute do
|
|
159
|
+
if nodes[attribute]
|
|
160
|
+
nodes[attribute].value
|
|
161
|
+
else
|
|
162
|
+
nil
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
self.readable_attributes_options[attribute] = nil
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Configure a has_one relationship
|
|
171
|
+
# Pass :modifiable => true if this relationship can be modified.
|
|
172
|
+
# When the resource is sent to the webservice the resource_url of the object will be sent to identify it
|
|
173
|
+
def has_one(name, options={})
|
|
174
|
+
options[:node_name] ||= name.to_s.underscore
|
|
175
|
+
|
|
176
|
+
self.has_one_associations_options[name] = options
|
|
177
|
+
|
|
178
|
+
define_method name do # define a reader that loads the association if not done already
|
|
179
|
+
if has_one_associations_updates[name]
|
|
180
|
+
has_one_associations_updates[name]
|
|
181
|
+
elsif !has_one_associations.has_key?(name)
|
|
182
|
+
nil
|
|
183
|
+
else
|
|
184
|
+
Proxy.new(has_one_associations[name]) # use a proxy to load objects as late as possible
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
define_method "#{name}=" do |value|
|
|
189
|
+
self.has_one_associations_updates[name] = value
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# define a convenience url accessor method
|
|
193
|
+
define_method "#{name}_url" do
|
|
194
|
+
if has_one_associations_updates[name]
|
|
195
|
+
has_one_associations_updates[name].resource_url
|
|
196
|
+
elsif has_one_associations[name]
|
|
197
|
+
has_one_associations[name].resource_url
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# define aliases for tagcrumb
|
|
202
|
+
if name == :tagcrumb
|
|
203
|
+
define_method :placemark do
|
|
204
|
+
self.tagcrumb
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
define_method :placemark= do |value|
|
|
208
|
+
self.tagcrumb = value
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Configure a has_many association
|
|
214
|
+
def has_many(name, options={})
|
|
215
|
+
options[:node_name] ||= name.to_s.underscore
|
|
216
|
+
|
|
217
|
+
self.has_many_associations_options[name] = options
|
|
218
|
+
|
|
219
|
+
define_method name do # define a reader that loads the association if not done already
|
|
220
|
+
if !has_many_associations.has_key?(name)
|
|
221
|
+
nil
|
|
222
|
+
else
|
|
223
|
+
Proxy.new(has_many_associations[name]) # use a proxy to load objects as late as possible
|
|
224
|
+
end
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# define a convenience url accessor method
|
|
228
|
+
define_method "#{name}_url" do
|
|
229
|
+
has_many_associations[name].resource_url if has_many_associations[name]
|
|
230
|
+
end
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
# Return the names of all modifiable has one associations of the class
|
|
234
|
+
def modifiable_has_one_associations_options
|
|
235
|
+
hash = {}
|
|
236
|
+
|
|
237
|
+
self.has_one_associations_options.each do |k, v|
|
|
238
|
+
hash[k] = v if v[:modifiable]
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
hash
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
# Return has_one and has_many options
|
|
245
|
+
def association_options
|
|
246
|
+
has_one_associations_options.merge(has_many_associations_options)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
end
|
|
252
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Activites are automatically created when other actions are performed on the website or with the webservice.
|
|
3
|
+
# They cannot be created or modified manually. However, it is possible to destroy an activity if you don't
|
|
4
|
+
# want it to be in your activity stream.
|
|
5
|
+
# The user of the activity is the actor who performed the activity, the subject is the social thing the action was performed upon.
|
|
6
|
+
# Sometimes there also is a secondary subject. The creation of a comment sets the subject to the comment and the secondary subject
|
|
7
|
+
# will be the placemark on which the comment was created.
|
|
8
|
+
# The event_type attribute shows what action happened and if a resource was modified or created.
|
|
9
|
+
class Activity < Model
|
|
10
|
+
can_be :destroyed
|
|
11
|
+
|
|
12
|
+
readable_attributes :event_type, :created_at, :updated_at
|
|
13
|
+
|
|
14
|
+
has_one :user
|
|
15
|
+
has_one :subject
|
|
16
|
+
has_one :secondary_subject
|
|
17
|
+
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Addresses are associated to Placemarks. They cannot be created or destroyed manually.
|
|
3
|
+
# They can only be updated. An address will be destroyed if the associated Placemark is
|
|
4
|
+
# destroyed.
|
|
5
|
+
#
|
|
6
|
+
# The city field contains a string that describes the city as it would stand in an address.
|
|
7
|
+
# It must not - but can - be the same as the associated city of the Placemark.
|
|
8
|
+
class Address < Model
|
|
9
|
+
can_be :updated
|
|
10
|
+
|
|
11
|
+
writeable_attributes :street_address, :postal_code, :city, :state, :country
|
|
12
|
+
|
|
13
|
+
has_one :tagcrumb
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# A City somewhere on this planet. It has a name and a geoname and you can get more information about it with the geonames web service.
|
|
3
|
+
# It belongs to a state and a state belongs to a country, so that you can find out the associated admin areas.
|
|
4
|
+
class City < Place
|
|
5
|
+
has_one :state
|
|
6
|
+
|
|
7
|
+
# Search for a city by handing a string.
|
|
8
|
+
def self.search(q, args={})
|
|
9
|
+
super(q, args.merge(:for => :city))
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Comments can be created on Placemarks. They can be destroyed for a certain timespan, afterwards it is no longer
|
|
3
|
+
# possible to destroy them.
|
|
4
|
+
class Comment < Model
|
|
5
|
+
can_be :created, :destroyed
|
|
6
|
+
|
|
7
|
+
readable_attributes :created_at, :updated_at
|
|
8
|
+
writeable_attributes :text
|
|
9
|
+
|
|
10
|
+
has_one :tagcrumb, :modifiable => true
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# A Country is a Place and has a name. It contains many states and the states contain many cities.
|
|
3
|
+
# You can filter Tagcrumbs by country. You can find out more information about the country by querying the geonames webservice.
|
|
4
|
+
|
|
5
|
+
class Country < Place
|
|
6
|
+
|
|
7
|
+
has_many :states
|
|
8
|
+
|
|
9
|
+
# Search a country by a string
|
|
10
|
+
def self.search(q, args={})
|
|
11
|
+
super(q, args.merge(:for => :country))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# A Fanship is the association between a User and a Fan.
|
|
3
|
+
# It cannot be created or destroyed, it only shows who is "following" you.
|
|
4
|
+
class Fanship < Model
|
|
5
|
+
|
|
6
|
+
readable_attributes :created_at, :updated_at
|
|
7
|
+
|
|
8
|
+
has_one :user
|
|
9
|
+
has_one :fan
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Instead of creating a brand new Placemark you can create a Favorite instead of a Placemark that is somewhere nearby
|
|
3
|
+
# and describes the place already that you wanted to describe.
|
|
4
|
+
|
|
5
|
+
# If you set subscribe to true you will automatically subscribe to updates of this Placemark.
|
|
6
|
+
class Favorite < Tagcrumb
|
|
7
|
+
can_be :created, :updated, :destroyed
|
|
8
|
+
|
|
9
|
+
writeable_attributes :tag_list, :private, :subscribe
|
|
10
|
+
readable_attributes :created_at, :updated_at
|
|
11
|
+
|
|
12
|
+
has_one :user
|
|
13
|
+
|
|
14
|
+
has_one :tagcrumb, :modifiable => true
|
|
15
|
+
|
|
16
|
+
# Find favorites and pass in additional filter options. The options are documented in Tagcrumb.
|
|
17
|
+
def self.find(args={})
|
|
18
|
+
super(args.merge(:tagcrumb_type => :favorite))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# A Filter is a saved set of "find" criterias that can be easily accessed by a user multiple times later on.
|
|
3
|
+
# == Filter criteria:
|
|
4
|
+
# private:
|
|
5
|
+
# - true: only show private Placemarks
|
|
6
|
+
# - false: only show public Placemarks
|
|
7
|
+
# - nil: show private and public Placemarks
|
|
8
|
+
# tagcrumb_type:
|
|
9
|
+
# - Placemark: only show Placemarks
|
|
10
|
+
# - Favorite: only show Favorites
|
|
11
|
+
# - nil: show Placemarks and Favorites
|
|
12
|
+
# tag_list: only show Tagcrumbs that are tagged with all of the tags in the comma-separated list
|
|
13
|
+
# since: all Tagcrumbs since this point of time
|
|
14
|
+
# until: all Tagcrumbs until this point of time (can be combined with since)
|
|
15
|
+
# from: you, everybody, fans, friends, recommendations
|
|
16
|
+
|
|
17
|
+
class Filter < Model
|
|
18
|
+
can_be :created, :updated, :destroyed
|
|
19
|
+
|
|
20
|
+
readable_attributes :created_at, :updated_at, :permalink
|
|
21
|
+
writeable_attributes :name, :private, :tagcrumb_type, :tag_list, :middle_of_nowhere, :from, :within, :since, :until
|
|
22
|
+
|
|
23
|
+
has_one :user
|
|
24
|
+
|
|
25
|
+
has_one :monitored_user, :modifiable => true
|
|
26
|
+
has_one :city, :modifiable => true
|
|
27
|
+
has_one :state, :modifiable => true
|
|
28
|
+
has_one :country, :modifiable => true
|
|
29
|
+
|
|
30
|
+
has_many :tagcrumbs
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# A Friendship can be created or destroyed and lets you "follow" other Users
|
|
3
|
+
class Friendship < Model
|
|
4
|
+
can_be :created, :destroyed
|
|
5
|
+
|
|
6
|
+
readable_attributes :created_at, :updated_at
|
|
7
|
+
|
|
8
|
+
has_one :user
|
|
9
|
+
has_one :friend, :modifiable => true
|
|
10
|
+
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
module Tagcrumbs
|
|
2
|
+
# Cities, States and Countries are connected to a Geoname. You can get more information about a place by querying the
|
|
3
|
+
# geonames webservice with the geonameid of the place.
|
|
4
|
+
class Geoname < Model
|
|
5
|
+
readable_attributes :geonameid, :latitude, :longitude
|
|
6
|
+
|
|
7
|
+
end
|
|
8
|
+
end
|