Class: FMyLife::Account

Inherits:
Object
  • Object
show all
Includes:
CanParse
Defined in:
lib/fmylife.rb

Overview

Account

An account/connection to FMyLife.com. This class provides access to all of the features of the FML api.

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from CanParse

#xml_attribute, #xml_content, #xml_doc, #xpath

Constructor Details

#initialize(key = 'readonly', language = 'en') ⇒ Account

Creates an FMyLife with the given developer key. The language can also be ‘fr’ for French. Sandbox mode defaults to being off.



93
94
95
96
# File 'lib/fmylife.rb', line 93

def initialize(key = 'readonly', language='en')
  @api_key, @language = key, language
  @sandbox_mode = false
end

Instance Attribute Details

#api_keyObject

Returns the value of attribute api_key.



88
89
90
# File 'lib/fmylife.rb', line 88

def api_key
  @api_key
end

#auth_tokenObject

Returns the value of attribute auth_token.



87
88
89
# File 'lib/fmylife.rb', line 87

def auth_token
  @auth_token
end

#sandbox_modeObject

Returns the value of attribute sandbox_mode.



89
90
91
# File 'lib/fmylife.rb', line 89

def sandbox_mode
  @sandbox_mode
end

Instance Method Details

#all_unmoderatedObject

Returns all unmoderated stories.



305
306
307
308
309
310
311
312
313
# File 'lib/fmylife.rb', line 305

def all_unmoderated
  path = "/mod/view"
  doc = http_get(path)
  results = []
  xpath(doc,"//items/item").each do |item|
    results << xml_content(item).to_i
  end
  results
end

#authenticate(username, password) ⇒ Object

Logs the connection in with the provided username and password



99
100
101
102
103
104
105
106
107
108
109
110
# File 'lib/fmylife.rb', line 99

def authenticate(username, password)
  path = "/account/login/#{username}/#{Digest::MD5.hexdigest(password)}"
  doc = http_post(path, '')
  
  case code(doc)
  when 0
    raise FMyLife::AuthenticationError.new("Error logging into FMyLife: #{error(doc)}")
  when 1
    @auth_token = xml_content xpath(doc,'//token').first
  end
  self
end

#category(category, page = 0) ⇒ Object

Returns a page of stories with the provided category. You may abbreviate “miscellaneous” as “misc”.



170
171
172
173
174
175
176
177
# File 'lib/fmylife.rb', line 170

def category(category, page=0)
  category = :miscellaneous if category.to_s =~ /misc/ # poor spellers unite!
  unless FMyLife::AVAILABLE_CATEGORIES.include?(category.to_sym)
    raise InvalidCategoryError.new("You provided an invalid category: #{category}")  
  end
  path = "/view/#{category}/#{page}"
  retrieve_stories(path)
end

#code(doc) ⇒ Object

Helper method that finds the error code from an XML document from fmylife



382
383
384
# File 'lib/fmylife.rb', line 382

def code(doc)
  xml_content(xpath(doc,'//code').first).to_i
end

#comment(story, arg) ⇒ Object

Submits a Comment to fmylife.com. You must be logged in (with authenticate). The story parameter can be either an FMyLife::Story, or an ID number. arg can be an FMyLife::Comment or a hash with the following values:

:text

The text of the story. Maximum 300 characters.

:url

The url for the currently logged-in user.



276
277
278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/fmylife.rb', line 276

def comment(story, arg)
  raise FMyLife::AuthenticationError.new("You aren't logged in.") if @auth_token.nil?
  params = (arg.is_a? FMyLife::Comment) ? {:text => arg.text, :url => arg.author_url} : arg
  params[:id] = (story.is_a? FMyLife::Story) ? story.id : story
  params[:token] = @auth_token
  path = "/comment"
  doc = http_get(path, params)
  case code(doc)
  when 0
    raise VotingError.new("Error while voting on story ##{params[:id]}: #{error(doc)}")
  when 1
    true
  end
end

#developer_infoObject

Returns an FMyLife::Developer object with information on the current developer (determined by the id provided when the FMyLife was instantiated)



293
294
295
296
297
298
299
300
301
302
# File 'lib/fmylife.rb', line 293

def developer_info
  path = "/dev"
  doc = http_get(path)
  case code(doc)
  when 0
    raise RetrievalError.new("Error retrieving developer info: #{error(doc)}")
  when 1
    Developer.new(:xml => doc)
  end
end

#error(doc) ⇒ Object

Helper method that finds the error string from an XML document from fmylife



387
388
389
390
391
392
393
# File 'lib/fmylife.rb', line 387

def error(doc)
  begin
    xml_content xpath(doc,'//error').first
  rescue
    xml_content xpath(doc,'//errors').first
  end
end

#favoritesObject

Gets the currently logged-in user’s favorite stories.



201
202
203
204
# File 'lib/fmylife.rb', line 201

def favorites
  path = "/view/favorites"
  retrieve_stories(path,true)
end

#flop(interval = nil, page = 0) ⇒ Object

Returns a page of the “flop” (lowest rated) stories. interval can be “day”, “week”, or “month”, to change how far back to look. Paginated to 15 entries, and page starts at 0.



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/fmylife.rb', line 152

def flop(interval = nil, page=0)
  param = case interval.to_sym
          when nil
            "flop"
          when :day
            "flop_day"
          when :week
            "flop_week"
          when :month
            "flop_month"
          else
            raise ArgumentError.new("Invalid flop-fml interval: #{interval}")
          end
  path = "/view/#{param}/#{page}"
  retrieve_stories(path)
end

#http_get(path, query_params = {}) ⇒ Object

Helper method for retrieving URLs via GET while escaping parameters and including API-specific parameters



361
362
363
364
365
366
367
# File 'lib/fmylife.rb', line 361

def http_get(path, query_params = {})
  query_params.merge!(:key => @api_key, :language => @language)
  http = Net::HTTP.new((@sandbox_mode) ? SANDBOX_ROOT : API_ROOT)
  path = path + "?" + URI.escape(query_params.map {|k,v| "#{k}=#{v}"}.join("&"), /[^-_!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]/n)
  resp = http.get(path)
  xml_doc(resp.body)
end

#http_post(path, data, query_params = {}) ⇒ Object

Helper method for retrieving URLs via POST while escaping parameters and including API-specific parameters



371
372
373
374
375
376
377
# File 'lib/fmylife.rb', line 371

def http_post(path, data, query_params = {})
  query_params.merge!(:key => @api_key, :language => @language)
  http = Net::HTTP.new((@sandbox_mode) ? SANDBOX_ROOT : API_ROOT)
  path = path + "?" + URI.escape(query_params.map {|k,v| "#{k}=#{v}"}.join("&"), /[^-_!~*'()a-zA-Z\d;\/?:@&=+$,\[\]]/n)
  resp = http.post(path, data)
  xml_doc(resp.body)
end

#last_moderatedObject

Returns the last story to be moderated.



322
323
324
325
# File 'lib/fmylife.rb', line 322

def last_moderated
  path = "/mod/last"
  retrieve_stories(path, true)
end

#latest(page = 0) ⇒ Object

Returns the most recent stories, paginated (starting at 0). Maximum return of 15 stories.



126
127
128
129
# File 'lib/fmylife.rb', line 126

def latest(page=0)
  path = "/view/last/#{page}"
  retrieve_stories(path)
end

#logoutObject

Logs the user out of fmylife.com.



113
114
115
116
117
118
119
120
121
122
123
# File 'lib/fmylife.rb', line 113

def logout
  raise FMyLife::AuthenticationError.new("You aren't logged in.") if @auth_token.nil?
  path = "/account/logout/#{@auth_token}"
  doc = http_get(path)
  case code(doc)
  when 0
    raise FMyLife::AuthenticationError.new("Error logging out: #{error(doc)}")
  when 1
    @auth_token = nil
  end
end

#moderate(story, type) ⇒ Object

Moderates a given story with a “yes” or “no” answer. ID can be either an FMyLife::Story object or an ID number. type should be :yes or :no .

Raises:



329
330
331
332
333
334
335
336
337
338
339
340
# File 'lib/fmylife.rb', line 329

def moderate(story, type)
  raise VotingTypeError.new("Your vote must be either 'yes' or 'no'.") unless [:yes, :no].include? type.to_sym
  id = (story.is_a? Story) ? story.id : story
  path = "/mod/#{type}/#{id}"
  doc = http_get(path, :token => @auth_token)
  case code(doc)
  when 0
    raise VotingError.new("Error while moderating story ##{story.id}: #{error(doc)}")
  when 1
    true
  end
end

#newObject

Gets the stories that the currently logged-in user has not seen.



195
196
197
198
# File 'lib/fmylife.rb', line 195

def new
  path = "/view/new"
  retrieve_stories(path,true)
end

#random(get_comments = true) ⇒ Object

Gets a random FML story, with or without comments.



180
181
182
183
# File 'lib/fmylife.rb', line 180

def random(get_comments = true)
  path = "/view/random"
  retrieve_story(path,get_comments)
end

#retrieve_stories(path, pass_token = false, params = {}) ⇒ Object

Helper method for retreiving multiple stories, without comments. Internal Use Only.



343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
# File 'lib/fmylife.rb', line 343

def retrieve_stories(path, pass_token = false, params = {})
  params.merge!({:token => @auth_token}) if pass_token
  doc = http_get(path, params)
  case code(doc)
  when 0
    raise FMyLife::RetrievalError.new("Error retrieving stories: #{error(doc)}")
  when 1
    stories = []
    xpath(doc,'//items/item').each do |entry|
      story = FMyLife::Story.new(:xml => entry)
      stories << story
    end
    stories
  end
end

#retrieve_story(path, get_comments = true) ⇒ Object

Helper method for retrieving a single story with comments. Internal use only.



212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# File 'lib/fmylife.rb', line 212

def retrieve_story(path, get_comments = true)
  doc = http_get(path)
  
  case code(doc)
  when 0
    raise FMyLife::RetrievalError.new("Error retrieving story ##{number}: #{error(doc)}")
  when 1
    story = FMyLife::Story.new(:xml => xpath(doc,'//items/item').first)
    if get_comments
      comments = []
      xpath(doc,'//comments/comment').each do |entry|
        comments << FMyLife::Comment.new(:xml => entry)
      end
      story.comments = comments
    end
    story
  end
end

#search(term) ⇒ Object



206
207
208
209
# File 'lib/fmylife.rb', line 206

def search(term)
  path = "/view/search/"
  retrieve_stories(path,false,{:search => term})
end

#story(_story, get_comments = true) ⇒ Object

Gets a specific story, with or without comments. The story parameter may be either a FMyLife::Story object, or an ID number.



187
188
189
190
191
192
# File 'lib/fmylife.rb', line 187

def story(_story, get_comments = true)
  number = (_story.is_a? FMyLife::Story) ? _story.id : _story
  path = "/view/#{number}"
  path = path + "/nocomment" unless get_comments
  retrieve_story(path,get_comments)
end

#submit(story) ⇒ Object

Submits a Story to fmylife.com. You must be logged in (with authenticate). The story parameter can be either an FMyLife::Story, or a hash with the following values:

:author

The name of the story’s author

:text

The text of the story. Maximum 300 characters.

:cat

The category to use. Must be one of AVAILABLE_CATEGORIES.



238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
# File 'lib/fmylife.rb', line 238

def submit(story)
  raise FMyLife::AuthenticationError.new("You aren't logged in.") if @auth_token.nil?
  params = (story.is_a? FMyLife::Story) ? {:author => story.author, :text => story.text, :cat => story.category} : story
  params[:cat] = params.delete :category if params[:category]
  params[:cat] = :miscellaneous if params[:cat].to_s =~ /misc/
  raise FMyLife::StoryTooLongError.new("Stories can be no longer than 300 characters.") if params[:text].size > 300
  params[:token] = @auth_token
  path = "/submit"
  doc = http_get(path, params)
  case code(doc)
  when 0
    raise VotingError.new("Error while submitting story with text \"#{params[:text]}\": #{error(doc)}")
  when 1
    true
  end
end

#top(interval = nil, page = 0) ⇒ Object

Returns a page of the top stories. interval can be “day”, “week”, or “month”, to change how far back to look. Paginated to 15 entries, and page starts at 0.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/fmylife.rb', line 133

def top(interval = nil, page=0)
  param = case interval
          when nil
            "top"
          when :day
            "top_day"
          when :week
            "top_week"
          when :month
            "top_month"
          else
            raise ArgumentError.new("Invalid top-fml interval: #{interval}")
          end
  path = "/view/#{param}/#{page}"
  retrieve_stories(path)
end

#unmoderated(id) ⇒ Object

Returns a single unmoderated story, based on its ID number.



316
317
318
319
# File 'lib/fmylife.rb', line 316

def unmoderated(id)
  path = "/mod/view/#{id}"
  retrieve_stories(path, true)
end

#vote(story, type) ⇒ Object

Votes for a story. type must be :agree or :deserved . Story can be either a FMyLife::Story or an ID number.

Raises:



256
257
258
259
260
261
262
263
264
265
266
267
# File 'lib/fmylife.rb', line 256

def vote(story, type)
  raise VotingTypeError.new("Your vote must be either 'agree' or 'deserved'.") unless [:agree, :deserved].include? type.to_sym
  id = (story.is_a? FMyLife::Story) ? story.id : story
  path = "/vote/#{id}/#{type}"
  doc = http_get(path)
  case code(doc)
  when 0
    raise VotingError.new("Error while voting on story ##{story.id}: #{error(doc)}")
  when 1
    true
  end
end