socialcastr 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. data/.gitignore +1 -0
  2. data/README.markdown +29 -6
  3. data/examples/parse_messages.rb +16 -0
  4. data/examples/post_comment.rb +11 -0
  5. data/examples/post_message.rb +15 -0
  6. data/examples/search.rb +19 -0
  7. data/lib/socialcastr/api.rb +70 -78
  8. data/lib/socialcastr/base.rb +151 -0
  9. data/lib/socialcastr/collection.rb +30 -0
  10. data/lib/socialcastr/comment.rb +2 -2
  11. data/lib/socialcastr/exceptions.rb +75 -0
  12. data/lib/socialcastr/external_resource.rb +2 -2
  13. data/lib/socialcastr/flag.rb +5 -0
  14. data/lib/socialcastr/like.rb +1 -1
  15. data/lib/socialcastr/message.rb +45 -11
  16. data/lib/socialcastr/version.rb +2 -2
  17. data/lib/socialcastr.rb +19 -11
  18. data/socialcastr.gemspec +2 -0
  19. data/spec/api_spec.rb +167 -0
  20. data/spec/base_spec.rb +235 -0
  21. data/spec/comment_spec.rb +4 -0
  22. data/spec/configuration_spec.rb +97 -0
  23. data/spec/fixtures/demo_config.yml +3 -0
  24. data/spec/fixtures/message.xml +69 -0
  25. data/spec/fixtures/messages.xml +3338 -0
  26. data/spec/message_spec.rb +173 -0
  27. data/spec/socialcastr_spec.rb +58 -0
  28. data/spec/spec_helper.rb +54 -0
  29. metadata +62 -19
  30. data/lib/socialcastr/attachment_list.rb +0 -5
  31. data/lib/socialcastr/comment_list.rb +0 -5
  32. data/lib/socialcastr/external_resource_list.rb +0 -5
  33. data/lib/socialcastr/group_list.rb +0 -5
  34. data/lib/socialcastr/group_membership_list.rb +0 -5
  35. data/lib/socialcastr/like_list.rb +0 -5
  36. data/lib/socialcastr/media_file_list.rb +0 -5
  37. data/lib/socialcastr/message_list.rb +0 -5
  38. data/lib/socialcastr/recipient_list.rb +0 -6
  39. data/lib/socialcastr/stream_list.rb +0 -5
  40. data/lib/socialcastr/tag_list.rb +0 -5
data/.gitignore CHANGED
@@ -9,3 +9,4 @@ coverage/*
9
9
  doc/*
10
10
  log/*
11
11
  pkg/*
12
+ examples/socialcast.yml
data/README.markdown CHANGED
@@ -4,31 +4,54 @@ SocialCast gem is a ruby interface to the SocialCast REST API
4
4
 
5
5
  ## INSTALLATION
6
6
 
7
- gem install socialcast4r
7
+ gem install socialcastr
8
8
 
9
9
  ## Usage
10
10
 
11
+ # configure the connection
11
12
  Socialcastr.configuration do |socialcast|
12
13
  socialcast.username = "user@example.com"
13
14
  socialcast.password = "password"
14
15
  socialcast.domain = "demo.socialcast.com"
15
16
  end
16
17
 
18
+ # obtain an instance of the API (useful to directly issue get, put, post, delete commands)
17
19
  api = Socialcastr.api
18
20
 
19
- messages = api.messages
21
+ # find all messages (currently returns just one page - 20 elements)
22
+ messages = Socialcastr::Message.find(:all)
23
+
24
+ # build a new message object
25
+ message = Socialcastr::Message.new(
26
+ :title => "hallo world!",
27
+ "body" => "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."
28
+ )
20
29
 
21
- message_params = { "message[title]" => "hallo world!", "message[body]" => "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua." }
22
- reply = api.add_message(message_params)
23
- message = Socialcastr::Message.parse(reply)
30
+ message.new? # => true
31
+
32
+ # persist the message to Socialcast
33
+ message.save
34
+
35
+ # comment a message
36
+ message.comment! :text => "Hallo world"
37
+
38
+ # search for messages
39
+ messages = Socialcastr::Message.search(:q => "test")
40
+
41
+
24
42
 
25
43
 
26
44
  ## Status
27
45
 
28
46
  This is just the first draft of the wrapper. It can be improved in many, many ways.
29
- The API is not completely covered either: a lot of interesing stuff, like (un)liking and attachments have been left out.
47
+ The API is not completely covered either: some of interesing stuff, like message and comments attachments have been left out.
30
48
  Feel free to help (see Contributing below)
31
49
 
50
+ ## TODO
51
+
52
+ * Base
53
+ * CRUD for nested objects (comments, likes, attachments)
54
+
32
55
  ## Contributing to the code (a.k.a. submitting a pull request)
33
56
 
34
57
  1. Fork the project.
@@ -0,0 +1,16 @@
1
+ $:.unshift("./lib")
2
+ require 'rubygems'
3
+ require 'socialcastr'
4
+
5
+ Socialcastr.configuration do |config|
6
+ config.username = "username"
7
+ config.password = "password"
8
+ config.domain = "domain"
9
+ end
10
+
11
+ xml = File.read("/Users/bru/Desktop/socialcast.xml")
12
+ start_time = Time.new
13
+ messages = Socialcastr::Message.parse(xml)
14
+ end_time = Time.new
15
+
16
+ puts "Found #{messages.count} messages in #{end_time - start_time} seconds"
@@ -0,0 +1,11 @@
1
+ $:.unshift('./lib')
2
+ require 'rubygems'
3
+ require 'socialcastr'
4
+
5
+ Socialcastr.configuration do |c|
6
+ c.config_file = File.join(File.dirname(__FILE__), "socialcast.yml")
7
+ end
8
+
9
+ message = Socialcastr::Message.last
10
+
11
+ message.comment! :text => "hallo from Socialcastr"
@@ -0,0 +1,15 @@
1
+ $:.unshift('./lib')
2
+ require 'rubygems'
3
+ require 'socialcastr'
4
+
5
+ Socialcastr.configuration do |c|
6
+ c.config_file = File.join(File.dirname(__FILE__), "socialcast.yml")
7
+ end
8
+
9
+ message = Socialcastr::Message.new( :title => "hello world", :body => "yet another message")
10
+
11
+ puts "message created: #{message.to_params.inspect}"
12
+
13
+ message.save
14
+
15
+ puts "message now is #{ message.new? ? "still new" : "persisted"} with id #{message.id}"
@@ -0,0 +1,19 @@
1
+ $:.unshift('./lib')
2
+ require 'rubygems'
3
+ require 'socialcastr'
4
+
5
+ NEEDLE="welcome"
6
+ Socialcastr.configuration do |c|
7
+ c.config_file = File.join(File.dirname(__FILE__), 'socialcast.yml')
8
+ end
9
+
10
+
11
+ puts "Searching for #{NEEDLE}..."
12
+ messages = Socialcastr::Message.search(:q => NEEDLE)
13
+ puts "found #{messages.size} results:"
14
+
15
+ messages.each do |message|
16
+ puts "#{message.user.name}:\n\t#{message.title}\n\t#{message.body}"
17
+ end
18
+
19
+
@@ -5,8 +5,6 @@ require 'uri'
5
5
  require 'digest/md5'
6
6
  require 'cgi'
7
7
 
8
-
9
-
10
8
  module Socialcastr
11
9
  class API
12
10
  attr_accessor :debug
@@ -19,102 +17,86 @@ module Socialcastr
19
17
  @endpoint = "https://#{domain}/api/"
20
18
  return self
21
19
  end
20
+
21
+ def get(path, args={})
22
+ https_request(:get, path, args)
23
+ end
22
24
 
23
- def messages(stream=nil, query={})
24
- method="messages"
25
- method.insert(0, "streams/#{stream.id}/") if stream
26
- xml = api_get(method, query)
27
- return Socialcastr::MessageList.parse(xml).messages
28
- end
29
-
30
- def search(query)
31
- method="messages/search"
32
- xml = api_get(method, query)
33
- return Socialcastr::MessageList.parse(xml).messages
34
- end
35
-
36
- def groups
37
- method = "group_memberships"
38
- xml = api_get(method)
39
- return Socialcastr::GroupMembershipList.parse(xml).group_memberships
40
- end
41
-
42
- def streams
43
- method = "streams"
44
- xml = api_get(method)
45
- return Socialcastr::StreamList.parse(xml).streams
46
- end
47
-
48
- def add_message(message)
49
- xml = api_post("messages", message)
50
- return xml
51
- end
52
-
53
- def add_comment(message_id, comment)
54
- xml = api_post("messages/#{message_id}/comments", comment)
55
- return xml
25
+ def put(path, args={})
26
+ https_request(:put, path, args)
56
27
  end
57
-
58
- def like_comment(message_id,comment_id)
59
- xml = api_post("messages/#{message_id.to_s}/comments/#{comment_id.to_s}/likes")
60
- return xml
28
+
29
+ def post(path, args={})
30
+ https_request(:post, path, args)
61
31
  end
62
32
 
63
- def unlike_comment(message_id,comment_id,like_id)
64
- xml = api_delete("messages/#{message_id}/comments/#{comment_id}/likes/#{like_id}")
65
- return xml
33
+ def delete(path, args={})
34
+ https_request(:delete, path, args)
66
35
  end
67
-
36
+
68
37
  def https_request(method, path, args)
69
38
  https = setup_https
70
- response = ""
71
-
72
- # HACK
73
- # if path == "messages/search"
74
- # path = "messages"
75
- # end
76
- # data = File.read(File.join('/tmp','fixtures','xml', "#{path}.#{@format}"))
77
- # return data
78
- # # /HACK
79
39
 
80
40
  case method
81
- when 'get'
82
- request_class = Net::HTTP::Get
83
- query=args
84
- when 'post'
85
- request_class = Net::HTTP::Post
86
- form_data = args
87
- when 'put'
88
- request_class = Net::HTTP::Put
89
- form_data = args
90
- when 'delete'
91
- request_class = Net::HTTP::Delete
41
+ when :get
42
+ request_class = Net::HTTP::Get
43
+ query=args
44
+ when :post
45
+ request_class = Net::HTTP::Post
46
+ form_data = args
47
+ when :put
48
+ request_class = Net::HTTP::Put
49
+ form_data = args
50
+ when :delete
51
+ request_class = Net::HTTP::Delete
52
+ else
53
+ raise InvalidMethod
92
54
  end
55
+ response = nil
93
56
  https.start do |session|
94
- query_string = "/api/#{path}.#{@format}"
95
- query_string += "?" + (query.collect { |k,v| "#{k}=#{CGI::escape(v.to_s)}" }.join('&')) unless query.nil?
57
+ query_string = build_query_string(path, query)
96
58
  req = request_class.new(query_string)
97
59
  req.basic_auth @username, @password
98
60
  if form_data
99
61
  req.set_form_data(args, ';')
100
62
  end
101
- response = session.request(req).body
63
+ response = session.request(req)
102
64
  end
103
65
 
104
- response
105
- end
106
-
107
- def api_get(path, args={})
108
- https_request('get', path, args)
109
- end
110
-
111
-
112
- def api_post(path, args={})
113
- https_request('post', path, args)
66
+ return handle_response(response).body
114
67
  end
115
68
 
116
- def api_delete(path, args={})
117
- https_request('delete', path, args)
69
+
70
+ # Handles response and error codes from the remote service.
71
+ def handle_response(response)
72
+ case response.code.to_i
73
+ when 301,302
74
+ raise(Redirection.new(response))
75
+ when 200...400
76
+ response
77
+ when 400
78
+ raise(BadRequest.new(response))
79
+ when 401
80
+ raise(UnauthorizedAccess.new(response))
81
+ when 403
82
+ raise(ForbiddenAccess.new(response))
83
+ when 404
84
+ raise(ResourceNotFound.new(response))
85
+ when 405
86
+ raise(MethodNotAllowed.new(response))
87
+ when 409
88
+ raise(ResourceConflict.new(response))
89
+ when 410
90
+ raise(ResourceGone.new(response))
91
+ when 422
92
+ raise(ResourceInvalid.new(response))
93
+ when 401...500
94
+ raise(ClientError.new(response))
95
+ when 500...600
96
+ raise(ServerError.new(response))
97
+ else
98
+ raise(ConnectionError.new(response, "Unknown response code: #{response.code}"))
99
+ end
118
100
  end
119
101
 
120
102
  def setup_https
@@ -124,5 +106,15 @@ module Socialcastr
124
106
  https.use_ssl = true
125
107
  return https
126
108
  end
109
+
110
+ def build_query_string(path, query=nil)
111
+ params = []
112
+ unless query.nil?
113
+ params = query.collect do |k,v|
114
+ "#{k.to_s}=#{CGI::escape(v.to_s)}"
115
+ end
116
+ end
117
+ "/api#{path.to_s =~ /\// ? path.to_s : "/" + path.to_s }.#{@format}" + (params.any? ? "?" + params.join('&') : "")
118
+ end
127
119
  end
128
120
  end
@@ -1,6 +1,157 @@
1
1
  require 'sax-machine'
2
+
2
3
  module Socialcastr
3
4
  class Base
4
5
  include SAXMachine
6
+
7
+ def initialize(arguments={})
8
+ arguments.map do |k,v|
9
+ self.send((k.to_s + "=").to_sym, v)
10
+ end
11
+ end
12
+
13
+ def id
14
+ begin
15
+ tmp_id = send self.class.id_attribute
16
+ tmp_id.to_i unless tmp_id.nil?
17
+ rescue
18
+ nil
19
+ end
20
+ end
21
+
22
+ def save
23
+ new? ? create : update
24
+ end
25
+
26
+ def create
27
+ api.post(collection_path, to_params).tap do |xml|
28
+ copy_attributes_from_object(self.class.parse(xml))
29
+ end
30
+ end
31
+
32
+ def update
33
+ api.put(element_path, to_params).tap do |xml|
34
+ copy_attributes_from_object(self.class.parse(xml))
35
+ end
36
+ end
37
+
38
+ def copy_attributes_from_object(object=nil)
39
+ object.instance_variables.each do |v|
40
+ instance_variable_set(v, object.instance_variable_get(v))
41
+ end
42
+ end
43
+
44
+ def new?
45
+ id.nil?
46
+ end
47
+
48
+ def api
49
+ self.class.api
50
+ end
51
+
52
+ def element_path
53
+ self.class.element_path(self.id)
54
+ end
55
+
56
+ def collection_path
57
+ self.class.collection_path
58
+ end
59
+
60
+ def to_params
61
+ params = {}
62
+ instance_variables.each do |variable|
63
+ params[param_name(variable)] = instance_variable_get(variable)
64
+ end
65
+ params
66
+ end
67
+
68
+ def param_name(variable_name)
69
+ "#{self.class.model_name.downcase}[#{variable_name.to_s.gsub /@/,''}]"
70
+ end
71
+
72
+ class << self
73
+ def api
74
+ @api ||= Socialcastr.api
75
+ end
76
+
77
+ def find(*arguments)
78
+ scope = arguments.slice!(0)
79
+ options = arguments.slice!(0) || {}
80
+ case scope
81
+ when :all then find_every(options)
82
+ when :first then find_every(options).first
83
+ when :last then find_every(options).to_a.last
84
+ #when :one then find_one(options)
85
+ else find_single(scope, options)
86
+ end
87
+ end
88
+
89
+ def find_single(id, options)
90
+ path = element_path(id, options)
91
+ parse(api.get(path))
92
+ end
93
+
94
+ def find_every(options)
95
+ path = collection_path(options)
96
+ parse_collection(api.get(path))
97
+ end
98
+
99
+ def all(*arguments)
100
+ find(:all, *arguments)
101
+ end
102
+
103
+ def first(*arguments)
104
+ find(:first, *arguments)
105
+ end
106
+
107
+ def last(*arguments)
108
+ find(:last, *arguments)
109
+ end
110
+
111
+ def element_path(id, prefix_options = {})
112
+ "#{prefix(prefix_options)}#{collection_name}/#{URI.escape id.to_s}"
113
+ end
114
+
115
+ def collection_path(options = {})
116
+ "#{prefix(options)}#{collection_name}"
117
+ end
118
+
119
+ def prefix(options)
120
+ options.map { |k,v| k.to_s.gsub("_id", 's') + "/" + v.to_s }.join("/") + "/"
121
+ end
122
+
123
+ def model_name
124
+ self.to_s.gsub(/^.*::/, '')
125
+ end
126
+
127
+ def collection_name
128
+ model_name.downcase + "s"
129
+ end
130
+
131
+ def parse_collection(data)
132
+ collection_class.parse(data)
133
+ end
134
+
135
+ def id_attribute
136
+ model_name.downcase + "_id"
137
+ end
138
+
139
+ def collection_class
140
+ return @collection_class if @collection_class
141
+ class_name = model_name + "List"
142
+ model_class = self
143
+ c_element = model_name.downcase.to_sym
144
+ c_name = collection_name.to_sym
145
+ klass = Object.const_set(class_name,Class.new(Socialcastr::Collection))
146
+ klass.class_eval do
147
+ collection_of c_element, :as => c_name, :class => model_class
148
+ end
149
+ return @collection_class = klass
150
+ end
151
+
152
+ def id_element(name=:id)
153
+ element name, :as => id_attribute.to_sym
154
+ end
155
+ end
5
156
  end
6
157
  end
@@ -0,0 +1,30 @@
1
+ module Socialcastr
2
+ class Collection < Base
3
+ include Enumerable
4
+
5
+ def each &block
6
+ members.each{|member| block.call(member)}
7
+ end
8
+
9
+ def size
10
+ members.size
11
+ end
12
+
13
+ def members
14
+ send self.class.members_method
15
+ end
16
+
17
+ def self.collection_of(name, options={})
18
+ if options[:as]
19
+ @members_method = options[:as]
20
+ else
21
+ @members_method = name
22
+ end
23
+ elements name, options
24
+ end
25
+
26
+ def self.members_method
27
+ @members_method
28
+ end
29
+ end
30
+ end
@@ -1,10 +1,10 @@
1
1
  module Socialcastr
2
2
  class Comment < Base
3
3
  element :editable
4
- element :attachments, :as => :attachment_list, :class => Socialcastr::AttachmentList
4
+ elements :attachment, :as => :attachments, :class => Socialcastr::Attachment
5
5
  element :likable
6
6
  element :deletable
7
- element :likes, :as => :like_list, :class => Socialcastr::LikeList
7
+ elements :like, :as => :likes, :class => Socialcastr::Like
8
8
  element :permalink_url
9
9
  element :text
10
10
  element :user, :class => Socialcastr::User
@@ -0,0 +1,75 @@
1
+ module Socialcastr
2
+ class InvalidMethod < StandardError
3
+ end
4
+ class ConnectionError < StandardError # :nodoc:
5
+ attr_reader :response
6
+
7
+ def initialize(response, message = nil)
8
+ @response = response
9
+ @message = message
10
+ end
11
+
12
+ def to_s
13
+ message = "Failed."
14
+ message << " Response code = #{response.code}." if response.respond_to?(:code)
15
+ message << " Response message = #{response.message}." if response.respond_to?(:message)
16
+ message
17
+ end
18
+ end
19
+
20
+ # Raised when a Timeout::Error occurs.
21
+ class TimeoutError < ConnectionError
22
+ def initialize(message)
23
+ @message = message
24
+ end
25
+ def to_s; @message ;end
26
+ end
27
+
28
+ # Raised when a OpenSSL::SSL::SSLError occurs.
29
+ class SSLError < ConnectionError
30
+ def initialize(message)
31
+ @message = message
32
+ end
33
+ def to_s; @message ;end
34
+ end
35
+
36
+ # 3xx Redirection
37
+ class Redirection < ConnectionError # :nodoc:
38
+ def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
39
+ end
40
+
41
+ # 4xx Client Error
42
+ class ClientError < ConnectionError; end # :nodoc:
43
+
44
+ # 400 Bad Request
45
+ class BadRequest < ClientError; end # :nodoc
46
+
47
+ # 401 Unauthorized
48
+ class UnauthorizedAccess < ClientError; end # :nodoc
49
+
50
+ # 403 Forbidden
51
+ class ForbiddenAccess < ClientError; end # :nodoc
52
+
53
+ # 404 Not Found
54
+ class ResourceNotFound < ClientError; end # :nodoc:
55
+
56
+ # 409 Conflict
57
+ class ResourceConflict < ClientError; end # :nodoc:
58
+
59
+ # 410 Gone
60
+ class ResourceGone < ClientError; end # :nodoc:
61
+
62
+ # 422 Invalid
63
+ class ResourceInvalid < ClientError; end # :nodoc:
64
+
65
+
66
+ # 5xx Server Error
67
+ class ServerError < ConnectionError; end # :nodoc:
68
+
69
+ # 405 Method Not Allowed
70
+ class MethodNotAllowed < ClientError # :nodoc:
71
+ def allowed_methods
72
+ @response['Allow'].split(',').map { |verb| verb.strip.downcase.to_sym }
73
+ end
74
+ end
75
+ end
@@ -1,10 +1,10 @@
1
1
  module Socialcastr
2
2
  class ExternalResource < Base
3
3
  element :type
4
- element :tags, :as => :tag_list, :class => Socialcastr::TagList
4
+ elements :tag, :as => :tags, :class => Socialcastr::Tag
5
5
  element :canonical_hashtag
6
6
  element :source, :class => Socialcastr::Source
7
- element :media_files, :as => :media_file_list, :class => Socialcastr::MediaFileList
7
+ elements :media_file, :as => :media_files, :class => Socialcastr::MediaFile
8
8
  element :url
9
9
  element :title
10
10
  element :description
@@ -0,0 +1,5 @@
1
+ module Socialcastr
2
+ class Flag < Base
3
+ id_element
4
+ end
5
+ end
@@ -1,9 +1,9 @@
1
1
  module Socialcastr
2
2
  class Like < Base
3
+ id_element
3
4
  element :unlikable
4
5
  element :user, :class => Socialcastr::User
5
6
  element :created_at
6
- element :id
7
7
  def unlikable_by?(api_id)
8
8
  @unlikable && api_id == self.user_id ? true : false
9
9
  end