Fork me on GitHub

Ruby, meet Stack Exchange.

Serel is Stack Exchange RElational Algebra, a fully featured Ruby library for the Stack Exchange API.

View on RubyGems View on GitHub RDocs

Installation is easy, provided you already have Ruby installed. If not, you're probably in the wrong place.

gem install serel

Configuration

To start, drop Serel into your Ruby file of choice, and configure it with your API key and the site you are looking to query.

require 'serel'
Serel::Base.config(:stackoverflow, 'api_key')

I can has API Key?

Don't have an API key? Sign up for one on Stack Apps

Stop logging things

By defaut, Serel will log the URL of every HTTP request made to STDOUT. If you don't want this, you can set the level of the logger to be higher: Serel::Base.logger.level = Logger::WARN

Tour

We'll now go on a whirlwind tour of the basics of Serel. Open up irb and configure Serel as above.

Stack Exchange is all about the questions, right? Lets start there. We'll retrieve a list of the 3 highest voted questions of all time on Stack Overflow, get the first one, find it's owner, and get a relation object with the tags he is active in sorted by name. Completely pointles in a practical sense? Yes, but you'll learn something.

See the little help box to the right for more information on the Relation object.

questions = Serel::Question.pagesize(3).sort('votes').get
=> [#<Serel::Question:70103133675940 @question_id=194812, @answer_count=112, ...>]
question = questions.first
 => #<Serel::Question:70103133675940 @question_id=194812, @answer_count=112, ...>
user = question.owner
 => #<Serel::User:70103133674960 @user_id=11110, @user_type=registered, ...>
relation = user.tags.sort('name')
=> #<Serel::Relation:0x007f8450c09f28 @type="tag", @klass=Serel::Tag, @scope={:api_key=>"0p65aJUHxHo0G19*YF272A((", :site=>:stackoverflow, :url=>"users/11110/tags", :sort=>"name"}, @qty=:plural> 

Here, take this with you

Despite using a library, the Stack Exchange API documentation will be invaluable. It provides all sorts of useful information, particularly with regards to what values are acceptable for which parameters, since this will change across methods.

Ooh, what's this relation object here?

The Serel::Relation class stores information about the current scope of the request. It's what we use to create the awesome chainable interface. You can pass these around if you like, and have reason to. Most people won't deal with Relation objects, but they're frequently used within the library.

Working with access tokens

We have a handy helper class to get from access tokens to a user or to their inbox.

token = Serel::AccessToken.new('token')
=> <Serel::AccessToken @token='token'>
user = token.user
=> <Serel::User>
inbox = token.inbox.get
==> [<Serel::Inbox>,...]

Once you have retrieved the user associated with the access token it's simple to access any of the instance methods on the user - see the User documentation for more information

Use the data in this table along with the API documentation to find which methods a particular route accepts. Serel has no way of knowing if a particular scoping method is valid on a particular route, so make sure your scopes are valid.

API ParameterSerel
filterfilter(String)
fromdatefromdate(DateTime/Integer/Time)
innameinname(String)
intitleintitle(String)
maxmax(Various)
minmin(Various)
nottaggednottagged(String)
orderorder(String)
pagepage(Integer)
pagesizepagesize(Integer)
sincesince(DateTime/Integer/Time)
sortsort(Various)
taggedtagged(String)
titletitle(String)
urlurl(String)
todatetodate(DateTime/Integer/Time)

Most methods return an instance of Serel::Response. This is a thin wrapper around the Array class that allows easy access to the additional attributes in the common wrapper.

> answers = Serel::Answer.get
[INFO][2012-03-31 10:44:35] Making request to /2.0/answers?site=stackoverflow&key=0p65aJUHxHo0G19%2AYF272A%28%28
=> [#<Serel::Answer:70199501159940...>]
> answers.backoff
=> nil
> answers.has_more
=> true
> answers.quota_remaining
=> 99997

The methods used to access the information in the response wrapper are named exactly the same as the fields in the wrapper object.

Some methods don't return a Serel::Response, instead returning a singular class. This is only done where it is obvious that singular responses will be recieved, e.g Answer#question and Info.get_info. The RDocs should indicate which object a method returns.

A complete list of all the API routes, mapped to their Serel equivalent. Note that we also have some additional methods, discussed below the table.

Heads up! All uses of Serel::Class.with_ids can be replaced by an instance of that class. The instance is the more likely use case but we wanted to show off the ability to use vectorized routes.

Per site methods

API RouteSarel Method
Answers
/answers Serel::Answer.get
/answers/{ids} Serel::Answer.find(ids)
/answers/{ids}/comments Serel::Answer.with_ids(1,2,3).comments.get
Badges
/badges Serel::Badge.get
/badges/{ids} Serel::Badge.find(1,2,3)
/badges/name Serel::Badge.named.get
/badges/recipients Serel::Badge.recipients.get
/badges/{ids}/recipients Serel::Badge.recipients(1,2,3).get
/badges/tags Serel::Badge.tag_based.get
Comments
/comments Serel::Comment.get
/comments/{ids} Serel::Comment.find(1,2,3)
Events
/events Serel::Event.access_token('token').get
Info
/info Serel::Info.get_info
Posts
/posts Serel::Post.get
/posts/{ids} Serel::Post.find(1,2,3)
/posts/{ids}/comments Serel::Post.with_ids(1,2,3).comments.get
/posts/{ids}/revisions Serel::Post.with_ids(1,2,3).revisions.get
/posts/{ids}/suggested-edits Serel::Post.with_ids(1,2,3).suggested_edits.get
Privileges
/privileges Serel::Privileges.all
Questions
/questions Serel::Question.get
/questions/{ids} Serel::Question.find(1,2,3)
/questions/{ids}/answers Serel::Question.with_ids(1,2,3).answers.get
/questions/{ids}/comments Serel::Question.with_ids(1,2,3).comments.get
/questions/{ids}/linked Serel::Question.with_ids(1,2,3).linked.get
/questions/{ids}/related Serel::Question.with_ids(1,2,3).related.get
/questions/{ids}/timeline Serel::Question.with_ids(1,2,3).timeline.get
/questions/featured Serel::Question.featured.get
/questions/unanswered Serel::Question.unanswered.get
/questions/no-answers Serel::Question.no_answers.get
Revisions
/revisions/{ids} Serel::Revision.find(1,2,3)
Search
/search Serel::Question.search
/similar Serel::Question.similar
Suggested Edits
/suggested-edits Serel::SuggestedEdit.get
/suggested-edits/{ids} Serel::SuggestedEdit.find(1,2,3)
Tags
/tags Serel::Tag.get
/tags/{name}/info Serel::Tag.find_by_name('tag')
/tags/moderator-only Serel::Tag.moderator_only.get
/tags/required Serel::Tag.required.get
/tags/synonyms Serel::Tag.synonyms.get
/tags/{name}/faq Serel::Tag.find_by_name('tag').faq.get
/tags/{name}/related Serel::Tag.find_by_name('tag').related.get
/tags/{name}/synonyms Serel::Tag.find_by_name('tag').synonyms.get
/tags/{name}/top-answerers/{period} Serel::Tag.find_by_name('tag').top_answerers(:all_time/:month).get
/tags/{name}/top-askers/{period} Serel::Tag.find_by_name('tag').top_askers(:all_time/:month).get
/tags/{name}/wikis Serel::Tag.find_by_name('tag').wiki.get
Users
/users Serel::User.get
/users/{ids} Serel::User.find(1,2,3)
/users/{ids}/answers Serel::User.with_ids(1,2,3).answers.get
/users/{ids}/badges Serel::User.with_ids(1,2,3).badges.get
/users/{ids}/comments Serel::User.with_ids(1,2,3).comments.get
/users/{ids}/comments/{toid} Serel::User.with_ids(1,2,3).comments(4).get
/users/{ids}/favorites Serel::User.with_ids(1,2,3).favorites.get
/users/{ids}/mentioned Serel::User.with_ids(1,2,3).mentioned.get
/users/{ids}/privileges user.privileges.get
/users/{ids}/questions Serel::User.with_ids(1,2,3).questions.get
/users/{ids}/questions/featured Serel::User.with_ids(1,2,3).questions_featured.get
/users/{ids}/questions/no-answers Serel::User.with_ids(1,2,3).questions_no_answers.get
/users/{ids}/questions/unaccepted Serel::User.with_ids(1,2,3).questions_unaccepted.get
/users/{ids}/questions/unanswered Serel::User.with_ids(1,2,3).questions_unanswered.get
/users/{ids}/reputation Serel::User.with_ids(1,2,3).reputation.get
/users/{ids}/suggested_edits Serel::User.with_ids(1,2,3).suggested_edits.get
/users/{ids}/tags Serel::User.with_ids(1,2,3).tags.get
/users/{ids}/tags/{tags}/top-answers Serel::User.with_ids(1,2,3).top_answers_on('tag1', 'tag2').get
/users/{ids}/tags/{tags}/top-questions Serel::User.with_ids(1,2,3).top_questions_on('tag1', 'tag2').get
/users/{ids}/timeline Serel::User.with_ids(1,2,3).timeline.get
/users/{ids}/top-answer-tags Serel::User.with_ids(1,2,3).top_answer_tags.get
/users/{ids}/top-question-tags Serel::User.with_ids(1,2,3).top_question_tags.get
/users/moderators Serel::User.moderators.get
/users/moderators/elected Serel::User.elected_moderators.get
/me/inbox Serel::AccessToken.new('token').inbox.get
/me/inbox/unread Serel::AccessToken.new('token').unread_inbox.get

1.0.0

  • Change the behaviour of #get to only change the URL scope if the URL is not already set
  • Fixed issue with filters that don't return any values
  • Improved handling of filters and responses requiring time values

1.0.0.rc1/2

  • Released Serel