stapeluberlauf 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 54382d58bda064e906f460acc85dcb69cd170a9e
4
+ data.tar.gz: 0536c70cda40779c6afad7d5c641ae31f0275513
5
+ SHA512:
6
+ metadata.gz: 56b7cf3260c7f15a00800e3f620a0bb3c3b588cedb813f3659d27ebd5450b6fad8650688e4140dc1f8e04d62649ce9cef84ba128146013a882107fee4f225a96
7
+ data.tar.gz: 809ea489f06cc2edf5e5d05461d3977e6464877012e55458c39cdc77a4cbdb1da2c646e792f37b4495f95a3ac2971bc23f819fd82a9f50681c8aa385bf76c4fd
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+
5
+ before_install:
6
+ - gem install bundler -v 1.10.6
7
+
8
+ install:
9
+ - bundle install --without=documentation
10
+
11
+ script: bundle exec rspec
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in stapeluberlauf.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'rspec', '~> 3.4.0'
8
+ gem 'vcr', '~> 3.0.1'
9
+ gem 'simplecov', require: false
10
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Marius Rackwitz
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ stapeluberlauf
2
+ ==============
3
+ ### /ˈʃtaːpl̩ ˈyːbɐˌlaʊ̯f/ _noun_, german word for stack overflow
4
+
5
+ [![Build Status](http://img.shields.io/travis/mrackwitz/stapeluberlauf/master.svg?style=flat)](https://travis-ci.org/mrackwitz/stapeluberlauf)
6
+ [![Gem Version](http://img.shields.io/gem/v/stapeluberlauf.svg?style=flat)](http://badge.fury.io/rb/stapeluberlauf)
7
+ [![Code Climate](http://img.shields.io/codeclimate/github/mrackwitz/stapeluberlauf.svg?style=flat)](https://codeclimate.com/github/mrackwitz/stapeluberlauf)
8
+
9
+ This gem provides a light-weight API wrapper to access the StackExchange API a little more conveniently.
10
+
11
+ It consists mainly out of simple DSL, which allows to describe requests and abstracts the implementation of basic
12
+ concepts like filters and paging.
13
+
14
+ It supports authenticated requests and is aware when a request requires authorization, so that it can fail preemptive
15
+ if this is not provided without a round-trip to the API.
16
+
17
+ It does provide a simple model for the response wrapper. But this covers just a primitive mapping of the JSON attributes
18
+ used for further communication negotiation. These include error indication, paging and rate limiting (quota).
19
+
20
+ It doesn't provide wrappers for the retrieved model classes yet. These have to be accessed through the envelope as raw
21
+ hashes.
22
+
23
+ ## Usage
24
+
25
+ This example fetches the first 10 unanswered questions tagged as `ruby`.
26
+
27
+ ```ruby
28
+ stackoverflow = Stapeluberlauf.site('stackoverflow').use_filter('withbody')
29
+ questions = stackoverflow.questions(tagged: 'ruby').unanswered.execute
30
+ ```
31
+
32
+ ## Installation
33
+
34
+ Add this line to your application's Gemfile:
35
+
36
+ ```ruby
37
+ gem 'stapeluberlauf'
38
+ ```
39
+
40
+ And then execute:
41
+
42
+ $ bundle
43
+
44
+ Or install it yourself as:
45
+
46
+ $ gem install stapeluberlauf
47
+
48
+ ## Disclaimer
49
+
50
+ This is not an official gem by StackExchange / StackOverflow.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "stackexchange"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'bundler/setup'
4
+ require 'stapeluberlauf'
5
+
6
+ if ARGV.count != 1
7
+ puts "Usage: #{File.basename($0)} tagged"
8
+ exit 1
9
+ end
10
+ tagged = ARGV.shift
11
+
12
+ stackoverflow = Stapeluberlauf.site('stackoverflow').use_filter('withbody')
13
+ questions = stackoverflow.questions(tagged: tagged).unanswered
14
+ questions.page_size = 10
15
+
16
+ result = questions.execute
17
+ result.items.each do |question|
18
+ puts "❓ #{question['title']}"
19
+ puts "🔗 #{question['link']}"
20
+ puts question['body']
21
+ puts "-- @#{question['owner']['display_name']}"
22
+ comments = stackoverflow.question(question['question_id']).comments.execute.items
23
+ next if comments.nil?
24
+ puts '-' * 10
25
+ comments.each do |comment|
26
+ puts " #{comment['body']}"
27
+ puts " -- @#{comment['owner']['display_name']}"
28
+ end
29
+ puts '*' * 80
30
+ end
@@ -0,0 +1,39 @@
1
+ module Stapeluberlauf
2
+ module Authorizable
3
+ # @return [String]
4
+ # The secret key of the OAuth app which allows to authorize to increased throttle
5
+ # quota and privileged access to user data.
6
+ # Must be set in combination with an #acess_token.
7
+ # If you don't have a request key you can obtain one by [registering your
8
+ # application on Stack Apps](http://stackapps.com/apps/oauth/register).
9
+ attr_accessor :key
10
+
11
+ # @return [String]
12
+ # The OAuth access token which allows to authorize to increased throttle
13
+ # quota and privileged access to user data.
14
+ # Must be set in combination with a #key.
15
+ #
16
+ attr_accessor :access_token
17
+
18
+ # Authorize a request with an access token and key.
19
+ #
20
+ # @param [String] key @see #key
21
+ #
22
+ # @param [String] access_token @see #access_token
23
+ #
24
+ # @return [Self] returns the receiver to support a floating API
25
+ #
26
+ def authorize(key, access_token)
27
+ self.key = key
28
+ self.access_token = access_token
29
+ return self
30
+ end
31
+
32
+ # Returns whether all credentials are sufficiently set
33
+ # to execute authorized requests.
34
+ #
35
+ def authorized?
36
+ !key.nil? && !access_token.nil?
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ module Stapeluberlauf
2
+ class Error < StandardError
3
+ # @return [Request] the request which resulted in the error
4
+ attr_accessor :request
5
+
6
+ # @return [Response] the erroneous response
7
+ attr_accessor :response
8
+
9
+ # Initialize a new error from a request and its response
10
+ #
11
+ # @param [Request] request @see #request
12
+ #
13
+ # @param [Response] response @see #response
14
+ #
15
+ def initialize(request, response)
16
+ super(response.error_message)
17
+ self.request = request
18
+ self.response = response
19
+ end
20
+
21
+ # Refers to an error type returned by the API
22
+ #
23
+ # @return [Int]
24
+ def id
25
+ response.error_id
26
+ end
27
+
28
+ # Returns the name of the error
29
+ #
30
+ # @return [String]
31
+ def name
32
+ response.error_name
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,20 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ class Answer < Request
4
+ include Behavior::Acceptable
5
+ include Behavior::Commentable
6
+ include Behavior::Editable
7
+ include Behavior::Flagable
8
+ include Behavior::Upvotable
9
+ include Behavior::Downvotable
10
+
11
+ # Gets all questions the identified answers are on.
12
+ #
13
+ # @return [Request]
14
+ #
15
+ def questions
16
+ request('questions')
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,13 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ autoload :Acceptable, 'stapeluberlauf/request/behaviors/acceptable.rb'
5
+ autoload :Commentable, 'stapeluberlauf/request/behaviors/commentable.rb'
6
+ autoload :Downvotable, 'stapeluberlauf/request/behaviors/downvotable.rb'
7
+ autoload :Editable, 'stapeluberlauf/request/behaviors/editable.rb'
8
+ autoload :Favoritable, 'stapeluberlauf/request/behaviors/favoritable.rb'
9
+ autoload :Flagable, 'stapeluberlauf/request/behaviors/flagable.rb'
10
+ autoload :Upvotable, 'stapeluberlauf/request/behaviors/upvotable.rb'
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Acceptable
5
+ # Casts an accept vote.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def accept
12
+ request('accept').auth_required!
13
+ end
14
+
15
+ # Undoes an accept vote.
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def undo_accept
22
+ request('accept/undo').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Commentable
5
+ # Get the comments.
6
+ #
7
+ # @return [Request]
8
+ #
9
+ def comments
10
+ request('comments')
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Downvotable
5
+ # Casts a downvote on the given resource.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def downvote
12
+ request('downvote').auth_required!
13
+ end
14
+
15
+ # Undoes a downvote on the given resource
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def undo_downvote
22
+ request('downvote/undo').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Editable
5
+ # Edits the given resource.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def edit
12
+ request('edit').auth_required!
13
+ end
14
+
15
+ # Deletes the given resource.
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def delete
22
+ request('delete').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Favoritable
5
+ # Favorites the given resource.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def favorite
12
+ request('favorite').auth_required!
13
+ end
14
+
15
+ # Undoes favoriting the given resouce.
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def undo_favorite
22
+ request('favorite/undo').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Flagable
5
+ # Casts a flag on the given resource.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def add_flag
12
+ request('flags/add').auth_required!
13
+ end
14
+
15
+ # Returns valid flag options for the given resource.
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def flag_options
22
+ request('flags/options').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,27 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ module Behavior
4
+ module Upvotable
5
+ # Casts an upvote on the given resource.
6
+ #
7
+ # @note auth required
8
+ #
9
+ # @return [Request]
10
+ #
11
+ def upvote
12
+ request('upvote').auth_required!
13
+ end
14
+
15
+ # Undoes an upvote on the given resource
16
+ #
17
+ # @note auth required
18
+ #
19
+ # @return [Request]
20
+ #
21
+ def undo_upvote
22
+ request('upvote/undo').auth_required!
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ class Comment < Request
4
+ include Behavior::Editable
5
+ include Behavior::Flagable
6
+ include Behavior::Upvotable
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,124 @@
1
+ module Stapeluberlauf
2
+ class Request
3
+ class Question < Site
4
+ include Behavior::Commentable
5
+ include Behavior::Editable
6
+ include Behavior::Favoritable
7
+ include Behavior::Flagable
8
+ include Behavior::Upvotable
9
+ include Behavior::Downvotable
10
+
11
+ # Renders a hypothetical question.
12
+ #
13
+ # @note auth required
14
+ #
15
+ # @return [Request]
16
+ #
17
+ def render
18
+ request('render').auth_required!
19
+ end
20
+
21
+ # Creates a new question.
22
+ #
23
+ # @note auth required
24
+ #
25
+ # @return [Request]
26
+ #
27
+ def add
28
+ request('add').auth_required!
29
+ end
30
+
31
+ # Get the answers to the questions
32
+ #
33
+ # @return [Request]
34
+ #
35
+ def answers
36
+ request('answers')
37
+ end
38
+
39
+ # Creates an answer on the given question. auth required
40
+ #
41
+ # @return [Request]
42
+ #
43
+ def add_answer
44
+ request('answers/add')
45
+ end
46
+
47
+ # Renders a hypothetical answer to a question.
48
+ #
49
+ # @return [Request]
50
+ #
51
+ def render_answer
52
+ request('answers/render')
53
+ end
54
+
55
+ # Returns valid flag options which are also close reasons for the given question.
56
+ #
57
+ # @note auth required
58
+ #
59
+ # @return [Request]
60
+ #
61
+ def close_options
62
+ request('close/options').auth_required!
63
+ end
64
+
65
+ # Get the questions that link to the questions.
66
+ #
67
+ # @return [Request]
68
+ #
69
+ def linked
70
+ request('linked')
71
+ end
72
+
73
+ # Get the questions that are related to the questions.
74
+ #
75
+ # @return [Request]
76
+ #
77
+ def related
78
+ request('related')
79
+ end
80
+
81
+ # Get the timelines of the questions.
82
+ #
83
+ # @return [Request]
84
+ #
85
+ def timeline
86
+ request('timeline')
87
+ end
88
+
89
+ # Get all questions on the site with active bounties.
90
+ #
91
+ # @return [Request]
92
+ #
93
+ def featured
94
+ request('featured')
95
+ end
96
+
97
+ # Get all questions on the site with *no* answers.
98
+ #
99
+ # @return [Request]
100
+ #
101
+ def no_answers
102
+ request('no-answers')
103
+ end
104
+
105
+ # Get all questions the site considers unanswered.
106
+ #
107
+ # @return [Request]
108
+ #
109
+ def unanswered
110
+ request('unanswered')
111
+ end
112
+
113
+ # Get questions the site considers unanswered within a user's favorite or interesting tags.
114
+ #
115
+ # @note auth required
116
+ #
117
+ # @return [Request]
118
+ #
119
+ def unanswered_with_my_tags
120
+ request('unanswered/my-tags').auth_required!
121
+ end
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,45 @@
1
+ require 'stapeluberlauf/authorizable.rb'
2
+ require 'stapeluberlauf/request.rb'
3
+
4
+ module Stapeluberlauf
5
+ class Request
6
+ class Site < Request
7
+ # Gets questions on the site.
8
+ #
9
+ # @param [String|Array] ids one or multiple ids
10
+ #
11
+ # @param [Hash] params the parameter
12
+ #
13
+ # @option params [String] tagged one or multiple tags
14
+ #
15
+ # @return [Request]
16
+ #
17
+ def questions(ids=nil, **params)
18
+ resource_request(Question, ids, 'questions', params)
19
+ end
20
+ alias_method :question, :questions
21
+
22
+ # Gets answers on the site.
23
+ #
24
+ # @param [String|Array] ids one or multiple ids
25
+ #
26
+ # @return [Request]
27
+ #
28
+ def answers(ids=nil, **params)
29
+ resource_request(Answer, ids, 'answers', params)
30
+ end
31
+ alias_method :answer, :answers
32
+
33
+ # Gets comments on the site.
34
+ #
35
+ # @param [String|Array] ids one or multiple ids
36
+ #
37
+ # @return [Request]
38
+ #
39
+ def comments(ids=nil, **params)
40
+ resource_request(Comment, ids, 'comments', params)
41
+ end
42
+ alias_method :comment, :comments
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,300 @@
1
+ require 'json'
2
+ require 'date'
3
+
4
+ require 'stapeluberlauf/authorizable.rb'
5
+
6
+ module Stapeluberlauf
7
+ class Request
8
+ autoload :Answer, 'stapeluberlauf/request/answer'
9
+ autoload :Behavior, 'stapeluberlauf/request/behavior'
10
+ autoload :Comment, 'stapeluberlauf/request/comment'
11
+ autoload :Question, 'stapeluberlauf/request/question'
12
+ autoload :Site, 'stapeluberlauf/request/site'
13
+
14
+ include Stapeluberlauf::Authorizable
15
+
16
+ # @return [Int] the maxium number of items on a page
17
+ MAX_PAGE_SIZE = 100.freeze
18
+
19
+ # @return [Net::HTTP] a HTTP client
20
+ attr_accessor :client
21
+
22
+ # @return [String] the path of the endpoint
23
+ attr_accessor :endpoint
24
+
25
+ # @return [Hash<Symbol, String>] the parameter
26
+ attr_accessor :params
27
+
28
+ # @return [Site] the site
29
+ attr_accessor :site
30
+
31
+ # @return [Int] the specific page to fetch, starts at and defaults to 1
32
+ attr_accessor :page
33
+
34
+ # @return [Int] any value between 0 and 100, defaults to 30 on API-site,
35
+ # initialized by default to 100
36
+ attr_accessor :page_size
37
+
38
+ # Initialize a new instance
39
+ #
40
+ # @param [Net::HTTP] client @see #client
41
+ #
42
+ # @param [Site] site @see #site
43
+ #
44
+ # @param [String] endpoint @see #endpoint
45
+ #
46
+ # @param [Hash<Symbol, String>] params @see #params
47
+ #
48
+ def initialize(client=nil, site=nil, endpoint='', params={})
49
+ @client = client
50
+ @site = site
51
+ @endpoint = endpoint
52
+ @params = params
53
+ @page = 1
54
+ @page_size = MAX_PAGE_SIZE
55
+ end
56
+
57
+ # Create a new request based on another instance.
58
+ #
59
+ # @param [Request] request
60
+ # takes over all values from the given request
61
+ #
62
+ def self.based_on(base_request)
63
+ new.tap do |instance|
64
+ base_request.instance_variables.each do |var|
65
+ value = base_request.instance_variable_get(var)
66
+ method_sym = "#{var.to_s[1..-1]}="
67
+ instance.send(method_sym, value.is_a?(Fixnum) ? value : value.dup)
68
+ end
69
+ end
70
+ end
71
+
72
+ # Request for items filtered by their creation time.
73
+ #
74
+ # @param [#to_time] after
75
+ # Define a lower limit for the range of `creation_date`.
76
+ #
77
+ # @param [#to_time] before
78
+ # Define an upper limit for the range of `creation_date`.
79
+ #
80
+ # @return [Request] returns the receiver to support a floating API
81
+ #
82
+ def created(after: nil, before: nil)
83
+ @params.merge!({
84
+ fromdate: from != nil ? before.to_time.to_i : nil,
85
+ todate: to != nil ? after.to_time.to_i : nil,
86
+ })
87
+ self
88
+ end
89
+
90
+ # Request for sorted results by a sort criteria.
91
+ #
92
+ # Calling this method multiple times on the same request instance
93
+ # will not add a further level of sorting, but override the previous
94
+ # defined sort order.
95
+ #
96
+ # @param [Symbol] sort_by the sort order
97
+ # Valid for posts (questions, answers) and comments:
98
+ # - :creation: creation_date
99
+ # - :votes: score
100
+ #
101
+ # Valid for posts (questions, answers):
102
+ # - :activity: last_activity_date
103
+ #
104
+ # Valid for questions (without prefilter as e.g. featured, unanswered, etc.):
105
+ # - :hot: by the formula ordering the hot tab
106
+ # - :week: by the formula ordering the week tab
107
+ # - :month: by the formula ordering the month tab
108
+ #
109
+ # Valid for badges:
110
+ # - :rank: by rank (default)
111
+ # - :name: by name
112
+ # - :type: by type
113
+ #
114
+ # @param [String, nil] min
115
+ # Define a lower limit for the range of the sort criteria.
116
+ #
117
+ # @param [String, nil] max
118
+ # Define an upper limit for the range of the sort criteria.
119
+ #
120
+ # @return [Request] returns the receiver to support a floating API
121
+ #
122
+ def sort_by(sort_by, min: nil, max: nil)
123
+ @params.merge!({
124
+ sort: sort_by.to_s,
125
+ min: min,
126
+ max: max,
127
+ })
128
+ self
129
+ end
130
+
131
+ # Select a filter, which enables certain includes and excludes so that the specific
132
+ # subset of data that is necessary for the application use are returned at once.
133
+ #
134
+ # @param [String] identifier
135
+ # This might be either the id of a filter retrieved after creating it
136
+ # or one of the pre-defined filters.
137
+ # - `default` returns the documented default fields
138
+ # - `withbody` returns the default plus the *.body fields
139
+ # - `none` is empty
140
+ # - `total` includes just .total
141
+ #
142
+ # @return [Request] returns the receiver to support a floating API
143
+ #
144
+ def use_filter(identifier)
145
+ @params.merge!({
146
+ filter: identifier
147
+ })
148
+ self
149
+ end
150
+
151
+ # Execute the request.
152
+ #
153
+ # @return [Result]
154
+ #
155
+ def execute
156
+ if is_auth_required? && !authorized?
157
+ raise "Request to #{self.endpoint} is not authorized, but authentication is required!"
158
+ end
159
+ http_res = client.get(absolute_uri)
160
+ json = JSON.parse(http_res.body)
161
+ res = Response.new
162
+ json.map do |key, value|
163
+ res.send("#{key}=", value)
164
+ end
165
+ unless res.error_name.nil?
166
+ raise Stapeluberlauf::Error.new(self, res)
167
+ end
168
+ res
169
+ end
170
+
171
+ # Allows to paginate
172
+ #
173
+ # @return [Enumerator]
174
+ #
175
+ def pages
176
+ Enumerator.new do |yielder|
177
+ response = self.execute
178
+ yielder << response
179
+ page_request = self.dup
180
+ while response.has_more
181
+ page_request.page += 1
182
+ response = page_request.execute
183
+ yielder << response
184
+ end
185
+ end
186
+ end
187
+
188
+ # Returns all items
189
+ #
190
+ # @return [Array<Hash>]
191
+ #
192
+ def all_items
193
+ pages.flat_map(&:items)
194
+ end
195
+
196
+ # Checks whether authentication is required.
197
+ #
198
+ # @return [Bool]
199
+ #
200
+ def is_auth_required?
201
+ @auth_required
202
+ end
203
+
204
+ # Returns the composed request URI with encoded parameters
205
+ #
206
+ # @return [URI]
207
+ #
208
+ def absolute_uri
209
+ URI.join(BASE_URI, relative_uri)
210
+ end
211
+
212
+ # Returns the composed request URI with encoded parameters
213
+ #
214
+ # @return [URI]
215
+ #
216
+ def relative_uri
217
+ uri = URI(endpoint)
218
+ uri.query = URI.encode_www_form(merged_params)
219
+ uri
220
+ end
221
+
222
+ protected
223
+
224
+ # Define that authentication is required for this endpoint.
225
+ #
226
+ # @return [Result]
227
+ #
228
+ def auth_required!
229
+ @auth_required = true
230
+ self
231
+ end
232
+
233
+ # Create a new request based on the current instance.
234
+ #
235
+ # @param [String] endpoint @see #endpoint
236
+ #
237
+ # @param [Hash<Symbol, String>] params @see #params
238
+ #
239
+ # @return [Request]
240
+ #
241
+ def request(endpoint, params={})
242
+ Request.based_on(self).tap do |instance|
243
+ instance.endpoint += "/#{endpoint}"
244
+ instance.params.merge! params
245
+ end
246
+ end
247
+
248
+ # Create a new request for a collection based on the current instance.
249
+ #
250
+ # @param [Class] request_class the class of the request, if {ids} is not nil
251
+ #
252
+ # @param [String|Array] ids one or multiple ids
253
+ #
254
+ # @param [String] endpoint @see #endpoint
255
+ #
256
+ # @param [Hash<Symbol, String>] params @see #params
257
+ #
258
+ # @return [Request]
259
+ #
260
+ def resource_request(request_class, ids, endpoint, params={})
261
+ request_class ||= self.class
262
+ request_class.based_on(self).tap do |instance|
263
+ instance.endpoint += "/#{endpoint}"
264
+ instance.params.merge! params
265
+ unless ids.nil?
266
+ instance.endpoint += "/#{process_ids(ids)}"
267
+ end
268
+ end
269
+ end
270
+
271
+ # Serialize a list of ids if necessary
272
+ #
273
+ # @param [String|Array] ids the ids of one or multiple resources
274
+ #
275
+ # @return [String]
276
+ #
277
+ def process_ids(ids)
278
+ if ids.is_a? Array
279
+ raise "Not more than 100 ids are allowed!" if ids.length > 100
280
+ ids.join(';')
281
+ else
282
+ ids
283
+ end
284
+ end
285
+
286
+ # Returns the merged parameter from different attributes
287
+ #
288
+ # @return [Hash<Symbol, String>]
289
+ #
290
+ def merged_params
291
+ params.merge({
292
+ site: site,
293
+ page: page != 1 ? page : nil,
294
+ pagesize: page_size != MAX_PAGE_SIZE ? page_size : nil,
295
+ key: key,
296
+ access_token: access_token,
297
+ }).reject { |_,v| v.nil? }
298
+ end
299
+ end
300
+ end
@@ -0,0 +1,44 @@
1
+ module Stapeluberlauf
2
+ class Response
3
+ # @return [Int, nil] is only set when the API detects the request took an unusually long time to run.
4
+ # When it is set an application must wait that number of seconds before calling that method again.
5
+ attr_accessor :backoff
6
+
7
+ # @return [Int, nil] refers to an error type returned by the API
8
+ attr_accessor :error_id
9
+
10
+ # @return [String, nil] message of the error referred by #error_id
11
+ attr_accessor :error_message
12
+
13
+ # @return [String, nil] name of the error referred by #error_name
14
+ attr_accessor :error_name
15
+
16
+ # @return [Boolean] whether there are more pages
17
+ attr_accessor :has_more
18
+
19
+ # @return [Array] an array of the type found in #type
20
+ attr_accessor :items
21
+
22
+ # @return [Int, nil] the requested page
23
+ # @note Not included in the `default` filter
24
+ attr_accessor :page
25
+
26
+ # @return [Int, nil] the size of the requested page
27
+ # @note Not included in the `default` filter
28
+ attr_accessor :page_size
29
+
30
+ # @return [Int] the maximum allowed quota bound to the credentials, if the requested was authorized
31
+ attr_accessor :quota_max
32
+
33
+ # @return [Int] the remaining quota bound to the credentials, if the requested was authorized
34
+ attr_accessor :quota_remaining
35
+
36
+ # @return [Int, nil] the total number of items
37
+ # @note Not included in the `default` filter
38
+ attr_accessor :total
39
+
40
+ # @return [String] the name of the entities returned in #items
41
+ # @note Not included in the `default` filter
42
+ attr_accessor :type
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ module Stapeluberlauf
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,33 @@
1
+ require 'uri'
2
+ require 'faraday'
3
+
4
+ module Stapeluberlauf
5
+ require 'stapeluberlauf/version'
6
+ autoload :Error, 'stapeluberlauf/error'
7
+ autoload :Request, 'stapeluberlauf/request'
8
+ autoload :Response, 'stapeluberlauf/response'
9
+
10
+ BASE_URI = URI.parse('https://api.stackexchange.com/2.2').freeze
11
+
12
+ class << self
13
+ # @return [Net::HTTP|Faraday]
14
+ #
15
+ attr_accessor :default_client
16
+
17
+ def default_client
18
+ @default_client ||= Faraday.new(url: BASE_URI.dup) do |builder|
19
+ builder.adapter Faraday.default_adapter
20
+ end
21
+ end
22
+ end
23
+
24
+ # Get access to a specific site by its name
25
+ #
26
+ # @param [String] name @see Site#name
27
+ #
28
+ # @return [Site]
29
+ #
30
+ def self.site(name)
31
+ Stapeluberlauf::Request::Site.new(default_client, name)
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'stapeluberlauf/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "stapeluberlauf"
8
+ spec.version = Stapeluberlauf::VERSION
9
+ spec.authors = ["Marius Rackwitz"]
10
+ spec.email = ["git@mariusrackwitz.de"]
11
+
12
+ spec.summary = %q{API wrapper to access the StackExchange API conveniently.}
13
+ spec.homepage = "https://github.com/mrackwitz/stapeluberlauf"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.require_paths = ["lib"]
17
+
18
+ spec.add_dependency "faraday", "~> 0.9.2"
19
+
20
+ spec.add_development_dependency "bundler", "~> 1.10"
21
+ spec.add_development_dependency "rake", "~> 10.0"
22
+ end
metadata ADDED
@@ -0,0 +1,113 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stapeluberlauf
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Marius Rackwitz
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-23 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: faraday
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.9.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.9.2
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.10'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.10'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ description:
56
+ email:
57
+ - git@mariusrackwitz.de
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - ".travis.yml"
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - bin/console
69
+ - bin/last_unanswered_questions
70
+ - lib/stapeluberlauf.rb
71
+ - lib/stapeluberlauf/authorizable.rb
72
+ - lib/stapeluberlauf/error.rb
73
+ - lib/stapeluberlauf/request.rb
74
+ - lib/stapeluberlauf/request/answer.rb
75
+ - lib/stapeluberlauf/request/behavior.rb
76
+ - lib/stapeluberlauf/request/behaviors/acceptable.rb
77
+ - lib/stapeluberlauf/request/behaviors/commentable.rb
78
+ - lib/stapeluberlauf/request/behaviors/downvotable.rb
79
+ - lib/stapeluberlauf/request/behaviors/editable.rb
80
+ - lib/stapeluberlauf/request/behaviors/favoritable.rb
81
+ - lib/stapeluberlauf/request/behaviors/flagable.rb
82
+ - lib/stapeluberlauf/request/behaviors/upvotable.rb
83
+ - lib/stapeluberlauf/request/comment.rb
84
+ - lib/stapeluberlauf/request/question.rb
85
+ - lib/stapeluberlauf/request/site.rb
86
+ - lib/stapeluberlauf/response.rb
87
+ - lib/stapeluberlauf/version.rb
88
+ - stapeluberlauf.gemspec
89
+ homepage: https://github.com/mrackwitz/stapeluberlauf
90
+ licenses: []
91
+ metadata: {}
92
+ post_install_message:
93
+ rdoc_options: []
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ required_rubygems_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ requirements: []
107
+ rubyforge_project:
108
+ rubygems_version: 2.4.5
109
+ signing_key:
110
+ specification_version: 4
111
+ summary: API wrapper to access the StackExchange API conveniently.
112
+ test_files: []
113
+ has_rdoc: