tagcrumbs 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (178) hide show
  1. data/History.txt +9 -0
  2. data/License.txt +22 -0
  3. data/Manifest.txt +177 -0
  4. data/PostInstall.txt +2 -0
  5. data/README.rdoc +45 -0
  6. data/Rakefile +35 -0
  7. data/TODO.txt +17 -0
  8. data/bin/tagcrumbs +5 -0
  9. data/examples/address_update.rb +15 -0
  10. data/examples/comment_new.rb +19 -0
  11. data/examples/favorite_new.rb +17 -0
  12. data/examples/filter_new.rb +15 -0
  13. data/examples/friendship_create.rb +19 -0
  14. data/examples/link_new.rb +16 -0
  15. data/examples/location_update.rb +16 -0
  16. data/examples/picture_update.rb +16 -0
  17. data/examples/placemark_new_simple.rb +24 -0
  18. data/examples/placemark_new_suggestions.rb +24 -0
  19. data/examples/profile_link_new.rb +14 -0
  20. data/examples/profile_update.rb +12 -0
  21. data/examples/request_authorization.rb +25 -0
  22. data/examples/settings_update.rb +12 -0
  23. data/examples/subscription_new.rb +15 -0
  24. data/examples/tagcrumbs_find.rb +25 -0
  25. data/examples/tagcrumbs_search.rb +7 -0
  26. data/examples/user.rb +28 -0
  27. data/examples/user_recommendation_new.rb +15 -0
  28. data/lib/tagcrumbs/builders/builder.rb +45 -0
  29. data/lib/tagcrumbs/builders/json_builder.rb +38 -0
  30. data/lib/tagcrumbs/builders/xml_builder.rb +30 -0
  31. data/lib/tagcrumbs/cli.rb +268 -0
  32. data/lib/tagcrumbs/config_store.rb +35 -0
  33. data/lib/tagcrumbs/exceptions.rb +13 -0
  34. data/lib/tagcrumbs/node.rb +43 -0
  35. data/lib/tagcrumbs/parsers/json_parser.rb +79 -0
  36. data/lib/tagcrumbs/parsers/parser.rb +24 -0
  37. data/lib/tagcrumbs/parsers/xml_parser.rb +54 -0
  38. data/lib/tagcrumbs/proxy.rb +32 -0
  39. data/lib/tagcrumbs/requestor.rb +140 -0
  40. data/lib/tagcrumbs/resources/array.rb +108 -0
  41. data/lib/tagcrumbs/resources/models/accessors.rb +252 -0
  42. data/lib/tagcrumbs/resources/models/activity.rb +19 -0
  43. data/lib/tagcrumbs/resources/models/address.rb +16 -0
  44. data/lib/tagcrumbs/resources/models/city.rb +13 -0
  45. data/lib/tagcrumbs/resources/models/comment.rb +12 -0
  46. data/lib/tagcrumbs/resources/models/country.rb +15 -0
  47. data/lib/tagcrumbs/resources/models/fanship.rb +12 -0
  48. data/lib/tagcrumbs/resources/models/favorite.rb +22 -0
  49. data/lib/tagcrumbs/resources/models/filter.rb +32 -0
  50. data/lib/tagcrumbs/resources/models/flag.rb +10 -0
  51. data/lib/tagcrumbs/resources/models/friendship.rb +12 -0
  52. data/lib/tagcrumbs/resources/models/geoname.rb +8 -0
  53. data/lib/tagcrumbs/resources/models/link.rb +13 -0
  54. data/lib/tagcrumbs/resources/models/location.rb +13 -0
  55. data/lib/tagcrumbs/resources/models/model.rb +148 -0
  56. data/lib/tagcrumbs/resources/models/picture.rb +28 -0
  57. data/lib/tagcrumbs/resources/models/place.rb +17 -0
  58. data/lib/tagcrumbs/resources/models/placemark.rb +30 -0
  59. data/lib/tagcrumbs/resources/models/profile.rb +16 -0
  60. data/lib/tagcrumbs/resources/models/profile_link.rb +12 -0
  61. data/lib/tagcrumbs/resources/models/root.rb +19 -0
  62. data/lib/tagcrumbs/resources/models/settings.rb +14 -0
  63. data/lib/tagcrumbs/resources/models/state.rb +14 -0
  64. data/lib/tagcrumbs/resources/models/subscription.rb +13 -0
  65. data/lib/tagcrumbs/resources/models/suggestions.rb +11 -0
  66. data/lib/tagcrumbs/resources/models/tag.rb +6 -0
  67. data/lib/tagcrumbs/resources/models/tagcrumb.rb +58 -0
  68. data/lib/tagcrumbs/resources/models/user.rb +101 -0
  69. data/lib/tagcrumbs/resources/models/user_recommendation.rb +18 -0
  70. data/lib/tagcrumbs/resources/resource.rb +76 -0
  71. data/lib/tagcrumbs/validations.rb +301 -0
  72. data/lib/tagcrumbs.rb +232 -0
  73. data/script/console +10 -0
  74. data/script/destroy +14 -0
  75. data/script/generate +14 -0
  76. data/spec/fixtures/activity.json +210 -0
  77. data/spec/fixtures/activity.xml +66 -0
  78. data/spec/fixtures/address.json +183 -0
  79. data/spec/fixtures/address.xml +58 -0
  80. data/spec/fixtures/city.json +88 -0
  81. data/spec/fixtures/city.xml +34 -0
  82. data/spec/fixtures/comment.json +286 -0
  83. data/spec/fixtures/comment.xml +87 -0
  84. data/spec/fixtures/country.json +32 -0
  85. data/spec/fixtures/country.xml +13 -0
  86. data/spec/fixtures/fanship.json +180 -0
  87. data/spec/fixtures/fanship.xml +54 -0
  88. data/spec/fixtures/favorite.json +332 -0
  89. data/spec/fixtures/favorite.xml +109 -0
  90. data/spec/fixtures/filter.json +243 -0
  91. data/spec/fixtures/filter.xml +80 -0
  92. data/spec/fixtures/friendship.json +180 -0
  93. data/spec/fixtures/friendship.xml +54 -0
  94. data/spec/fixtures/geoname.json +18 -0
  95. data/spec/fixtures/geoname.xml +6 -0
  96. data/spec/fixtures/json_parser.json +25 -0
  97. data/spec/fixtures/link.json +288 -0
  98. data/spec/fixtures/link.xml +88 -0
  99. data/spec/fixtures/location.json +204 -0
  100. data/spec/fixtures/location.xml +71 -0
  101. data/spec/fixtures/picture.json +154 -0
  102. data/spec/fixtures/picture.png +0 -0
  103. data/spec/fixtures/picture.xml +49 -0
  104. data/spec/fixtures/placemark.json +357 -0
  105. data/spec/fixtures/placemark.xml +58 -0
  106. data/spec/fixtures/placemarks.json +887 -0
  107. data/spec/fixtures/placemarks.xml +348 -0
  108. data/spec/fixtures/profile.json +282 -0
  109. data/spec/fixtures/profile.xml +100 -0
  110. data/spec/fixtures/profile_link.json +156 -0
  111. data/spec/fixtures/profile_link.xml +49 -0
  112. data/spec/fixtures/root.json +46 -0
  113. data/spec/fixtures/root.xml +14 -0
  114. data/spec/fixtures/settings.json +147 -0
  115. data/spec/fixtures/settings.xml +45 -0
  116. data/spec/fixtures/state.json +62 -0
  117. data/spec/fixtures/state.xml +23 -0
  118. data/spec/fixtures/subscription.json +283 -0
  119. data/spec/fixtures/subscription.xml +86 -0
  120. data/spec/fixtures/suggestions.json +1877 -0
  121. data/spec/fixtures/suggestions.xml +724 -0
  122. data/spec/fixtures/tag.json +9 -0
  123. data/spec/fixtures/tag.xml +4 -0
  124. data/spec/fixtures/user.json +105 -0
  125. data/spec/fixtures/user.xml +31 -0
  126. data/spec/fixtures/user_recommendation.json +349 -0
  127. data/spec/fixtures/user_recommendation.xml +106 -0
  128. data/spec/fixtures/validation_errors.json +5 -0
  129. data/spec/fixtures/validation_errors.xml +5 -0
  130. data/spec/fixtures/xml_parser.xml +13 -0
  131. data/spec/spec.opts +1 -0
  132. data/spec/spec_helper.rb +75 -0
  133. data/spec/tagcrumbs/builders/builder_spec.rb +57 -0
  134. data/spec/tagcrumbs/builders/json_builder_spec.rb +47 -0
  135. data/spec/tagcrumbs/builders/xml_builder_spec.rb +34 -0
  136. data/spec/tagcrumbs/exceptions_spec.rb +16 -0
  137. data/spec/tagcrumbs/node_spec.rb +42 -0
  138. data/spec/tagcrumbs/parsers/json_parser_spec.rb +117 -0
  139. data/spec/tagcrumbs/parsers/parser_spec.rb +25 -0
  140. data/spec/tagcrumbs/parsers/xml_parser_spec.rb +117 -0
  141. data/spec/tagcrumbs/proxy_spec.rb +48 -0
  142. data/spec/tagcrumbs/requestor_spec.rb +62 -0
  143. data/spec/tagcrumbs/resources/array_spec.rb +62 -0
  144. data/spec/tagcrumbs/resources/models/accessors_spec.rb +123 -0
  145. data/spec/tagcrumbs/resources/models/activity_spec.rb +33 -0
  146. data/spec/tagcrumbs/resources/models/address_spec.rb +24 -0
  147. data/spec/tagcrumbs/resources/models/city_spec.rb +33 -0
  148. data/spec/tagcrumbs/resources/models/comment_spec.rb +23 -0
  149. data/spec/tagcrumbs/resources/models/country_spec.rb +26 -0
  150. data/spec/tagcrumbs/resources/models/fanship_spec.rb +28 -0
  151. data/spec/tagcrumbs/resources/models/favorite_spec.rb +28 -0
  152. data/spec/tagcrumbs/resources/models/filter_spec.rb +43 -0
  153. data/spec/tagcrumbs/resources/models/flag_spec.rb +5 -0
  154. data/spec/tagcrumbs/resources/models/friendship_spec.rb +28 -0
  155. data/spec/tagcrumbs/resources/models/geoname_spec.rb +18 -0
  156. data/spec/tagcrumbs/resources/models/link_spec.rb +24 -0
  157. data/spec/tagcrumbs/resources/models/location_spec.rb +28 -0
  158. data/spec/tagcrumbs/resources/models/model_spec.rb +27 -0
  159. data/spec/tagcrumbs/resources/models/picture_spec.rb +42 -0
  160. data/spec/tagcrumbs/resources/models/place_spec.rb +12 -0
  161. data/spec/tagcrumbs/resources/models/placemark_spec.rb +61 -0
  162. data/spec/tagcrumbs/resources/models/profile_link_spec.rb +29 -0
  163. data/spec/tagcrumbs/resources/models/profile_spec.rb +43 -0
  164. data/spec/tagcrumbs/resources/models/root_spec.rb +57 -0
  165. data/spec/tagcrumbs/resources/models/settings_spec.rb +32 -0
  166. data/spec/tagcrumbs/resources/models/state_spec.rb +30 -0
  167. data/spec/tagcrumbs/resources/models/subscription_spec.rb +28 -0
  168. data/spec/tagcrumbs/resources/models/suggestions_spec.rb +33 -0
  169. data/spec/tagcrumbs/resources/models/tag_spec.rb +16 -0
  170. data/spec/tagcrumbs/resources/models/tagcrumb_spec.rb +21 -0
  171. data/spec/tagcrumbs/resources/models/user_recommendation_spec.rb +35 -0
  172. data/spec/tagcrumbs/resources/models/user_spec.rb +128 -0
  173. data/spec/tagcrumbs/resources/resource_spec.rb +62 -0
  174. data/spec/tagcrumbs/validations_spec.rb +27 -0
  175. data/spec/tagcrumbs_spec.rb +103 -0
  176. data/tagcrumbs.gemspec +59 -0
  177. data/tasks/rspec.rake +21 -0
  178. metadata +327 -0
@@ -0,0 +1,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,10 @@
1
+ module Tagcrumbs
2
+ # A Flag is used to mark a Placemark to be against the Terms of Service. It can only be created and not read, destroyed or
3
+ # updated afterwards.
4
+ class Flag < Model
5
+ can_be :created
6
+
7
+ writeable_attributes :reason
8
+
9
+ end
10
+ 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