stapeluberlauf 0.1.0

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.
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: