From 5975ed19470c2eac079024fcafb56e18d6ecec74 Mon Sep 17 00:00:00 2001 From: Rhenium Date: Sun, 9 Feb 2014 18:27:31 +0900 Subject: rewrite APIs with Grape and RABL --- Gemfile | 3 +- Gemfile.lock | 73 ++++++++++---- app/api/api.rb | 48 +++++++++ app/api/api_deprecated.rb | 81 +++++++++++++++ app/api/api_tweets.rb | 111 +++++++++++++++++++++ app/api/api_users.rb | 48 +++++++++ .../concerns/twitter_oauth_echo_authentication.rb | 22 ++++ app/api/templates/tweet.rabl | 6 ++ app/api/templates/tweets.rabl | 3 + .../templates/user_discovored_by_and_users.rabl | 6 ++ app/api/templates/user_stats.rabl | 5 + app/assets/javascripts/apidocs.js | 12 +++ app/controllers/apidocs_controller.rb | 30 +++++- app/controllers/application_controller.rb | 30 +++--- .../concerns/twitter_oauth_echo_authentication.rb | 22 ---- app/controllers/tweets_controller.rb | 105 +------------------ app/controllers/users_controller.rb | 47 +-------- app/helpers/apidocs_helper.rb | 5 + app/models/user.rb | 6 +- app/views/about/api.html.haml | 12 --- app/views/about/index.html.haml | 2 - app/views/apidocs/endpoint.html.haml | 34 ++++--- app/views/apidocs/index.html.haml | 30 ++++-- app/views/errors/render_error.json.jbuilder | 5 - app/views/shared/sidebar/_i.html.haml | 1 - app/views/tweets/_tweet.json.jbuilder | 9 -- app/views/tweets/_tweets.json.jbuilder | 1 - app/views/tweets/show.json.jbuilder | 1 - app/views/users/_users_list.json.jbuilder | 6 -- app/views/users/stats.json.jbuilder | 2 - config/application.rb | 9 +- config/initializers/rabl.rb | 3 + config/routes.rb | 11 +- lib/aclog/exceptions.rb | 5 + lib/apidoc.rb | 17 ---- lib/apidoc/controller_dsl.rb | 38 ------- lib/apidoc/controller_dsl/endpoint.rb | 30 ------ lib/apidoc/controller_dsl/parameters.rb | 27 ----- lib/apidoc/controller_dsl/resources.rb | 12 --- lib/apidoc/endpoint.rb | 18 ---- lib/apidoc/exceptions.rb | 17 ---- lib/apidoc/parameter.rb | 13 --- lib/apidoc/railtie.rb | 9 -- lib/apidoc/resource.rb | 12 --- 44 files changed, 514 insertions(+), 473 deletions(-) create mode 100644 app/api/api.rb create mode 100644 app/api/api_deprecated.rb create mode 100644 app/api/api_tweets.rb create mode 100644 app/api/api_users.rb create mode 100644 app/api/concerns/twitter_oauth_echo_authentication.rb create mode 100644 app/api/templates/tweet.rabl create mode 100644 app/api/templates/tweets.rabl create mode 100644 app/api/templates/user_discovored_by_and_users.rabl create mode 100644 app/api/templates/user_stats.rabl create mode 100644 app/assets/javascripts/apidocs.js delete mode 100644 app/controllers/concerns/twitter_oauth_echo_authentication.rb create mode 100644 app/helpers/apidocs_helper.rb delete mode 100644 app/views/about/api.html.haml delete mode 100644 app/views/errors/render_error.json.jbuilder delete mode 100644 app/views/tweets/_tweet.json.jbuilder delete mode 100644 app/views/tweets/_tweets.json.jbuilder delete mode 100644 app/views/tweets/show.json.jbuilder delete mode 100644 app/views/users/_users_list.json.jbuilder delete mode 100644 app/views/users/stats.json.jbuilder create mode 100644 config/initializers/rabl.rb delete mode 100644 lib/apidoc.rb delete mode 100644 lib/apidoc/controller_dsl.rb delete mode 100644 lib/apidoc/controller_dsl/endpoint.rb delete mode 100644 lib/apidoc/controller_dsl/parameters.rb delete mode 100644 lib/apidoc/controller_dsl/resources.rb delete mode 100644 lib/apidoc/endpoint.rb delete mode 100644 lib/apidoc/exceptions.rb delete mode 100644 lib/apidoc/parameter.rb delete mode 100644 lib/apidoc/railtie.rb delete mode 100644 lib/apidoc/resource.rb diff --git a/Gemfile b/Gemfile index 8b5d380..772aee0 100644 --- a/Gemfile +++ b/Gemfile @@ -4,11 +4,12 @@ gem "rails", "~> 4.0.2" gem "mysql2" gem "settingslogic" gem "yajl-ruby", require: "yajl" +gem "grape" +gem "grape-rabl" gem "twitter" gem "twitter-text" gem "omniauth-twitter" gem "haml-rails" -gem "jbuilder" gem "sass-rails" gem "coffee-rails" gem "uglifier" diff --git a/Gemfile.lock b/Gemfile.lock index 10ebc25..0b3328b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -26,20 +26,25 @@ GEM thread_safe (~> 0.1) tzinfo (~> 0.3.37) addressable (2.3.5) - arel (4.0.1) + arel (4.0.2) atomic (1.1.14) - bootstrap-sass (3.0.3.0) + axiom-types (0.0.5) + descendants_tracker (~> 0.0.1) + ice_nine (~> 0.9) + bootstrap-sass (3.1.0.2) sass (~> 3.2) buftok (0.2.0) builder (3.1.4) coderay (1.1.0) + coercible (1.0.0) + descendants_tracker (~> 0.0.1) coffee-rails (4.0.1) coffee-script (>= 2.2.0) railties (>= 4.0.0, < 5.0) coffee-script (2.2.0) coffee-script-source execjs - coffee-script-source (1.6.3) + coffee-script-source (1.7.0) cool.io (1.1.1) iobuffer (>= 1.0.0) coveralls (0.7.0) @@ -48,14 +53,14 @@ GEM simplecov (>= 0.7) term-ansicolor thor - crack (0.4.1) - safe_yaml (~> 0.9.0) + crack (0.4.2) + safe_yaml (~> 1.0.0) daemon-spawn (0.4.2) daemons (1.1.9) dalli (2.7.0) descendants_tracker (0.0.3) diff-lcs (1.2.5) - docile (1.1.2) + docile (1.1.3) em-work_queue (0.0.1) eventmachine equalizer (0.0.9) @@ -69,6 +74,21 @@ GEM railties (>= 3.0.0) faraday (0.9.0) multipart-post (>= 1.2, < 3) + grape (0.6.1) + activesupport + builder + hashie (>= 1.2.0) + multi_json (>= 1.3.2) + multi_xml (>= 0.5.2) + rack (>= 1.3.0) + rack-accept + rack-mount + virtus (>= 1.0.0) + grape-rabl (0.2.2) + grape + i18n + rabl + tilt haml (4.0.5) tilt haml-rails (0.5.3) @@ -82,15 +102,13 @@ GEM http_parser.rb http_parser.rb (0.6.0) i18n (0.6.9) + ice_nine (0.11.0) iobuffer (1.1.2) - jbuilder (2.0.2) - activesupport (>= 3.0.0) - multi_json (>= 1.2.0) - jquery-rails (3.0.4) + jquery-rails (3.1.0) railties (>= 3.0, < 5.0) thor (>= 0.14, < 2.0) json (1.8.1) - kgio (2.8.1) + kgio (2.9.1) mail (2.5.4) mime-types (~> 1.16) treetop (~> 1.4.8) @@ -104,6 +122,7 @@ GEM cool.io (~> 1.1.1) msgpack (~> 0.5.8) multi_json (1.8.4) + multi_xml (0.5.5) multipart-post (2.0.0) mysql2 (0.3.15) naught (1.0.0) @@ -118,7 +137,7 @@ GEM multi_json (~> 1.3) omniauth-oauth (~> 1.0) polyglot (0.3.3) - pry (0.9.12.5) + pry (0.9.12.6) coderay (~> 1.0) method_source (~> 0.8) slop (~> 3.4) @@ -126,7 +145,13 @@ GEM pry (>= 0.9.10) quiet_assets (1.0.2) railties (>= 3.1, < 5.0) + rabl (0.9.3) + activesupport (>= 2.3.14) rack (1.5.2) + rack-accept (0.4.5) + rack (>= 0.4) + rack-mount (0.8.3) + rack (>= 1.0.0) rack-test (0.6.2) rack (>= 1.0) rails (4.0.2) @@ -151,9 +176,9 @@ GEM rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) rspec-core (2.14.7) - rspec-expectations (2.14.4) + rspec-expectations (2.14.5) diff-lcs (>= 1.1.3, < 2.0) - rspec-mocks (2.14.4) + rspec-mocks (2.14.5) rspec-rails (2.14.1) actionpack (>= 3.0) activemodel (>= 3.0) @@ -162,7 +187,7 @@ GEM rspec-core (~> 2.14.0) rspec-expectations (~> 2.14.0) rspec-mocks (~> 2.14.0) - safe_yaml (0.9.7) + safe_yaml (1.0.1) sass (3.2.14) sass-rails (4.0.1) railties (>= 4.0.0, < 5.0) @@ -185,8 +210,8 @@ GEM actionpack (>= 3.0) activesupport (>= 3.0) sprockets (~> 2.8) - term-ansicolor (1.2.2) - tins (~> 0.8) + term-ansicolor (1.3.0) + tins (~> 1.0) thin (1.6.1) daemons (>= 1.0.9) eventmachine (>= 1.0.0) @@ -195,7 +220,7 @@ GEM thread_safe (0.1.3) atomic tilt (1.4.1) - tins (0.13.1) + tins (1.0.0) treetop (1.4.15) polyglot polyglot (>= 0.3.1) @@ -220,13 +245,18 @@ GEM unf (0.1.3) unf_ext unf_ext (0.0.6) - unicorn (4.8.0) + unicorn (4.8.2) kgio (~> 2.6) rack raindrops (~> 0.7) unicorn-worker-killer (0.4.2) unicorn (~> 4) - webmock (1.17.1) + virtus (1.0.1) + axiom-types (~> 0.0.5) + coercible (~> 1.0) + descendants_tracker (~> 0.0.1) + equalizer (~> 0.0.7) + webmock (1.17.2) addressable (>= 2.2.7) crack (>= 0.3.2) yajl-ruby (1.2.0) @@ -242,8 +272,9 @@ DEPENDENCIES dalli em-work_queue factory_girl_rails + grape + grape-rabl haml-rails - jbuilder jquery-rails msgpack msgpack-rpc diff --git a/app/api/api.rb b/app/api/api.rb new file mode 100644 index 0000000..d7ada1d --- /dev/null +++ b/app/api/api.rb @@ -0,0 +1,48 @@ +class Api < Grape::API + extend TwitterOauthEchoAuthentication + + format :json + formatter :json, Grape::Formatter::Rabl + error_formatter :json, ->(message, backtrace, options, env) do + { error: { message: message } }.to_json + end + + rescue_from ActiveRecord::RecordNotFound, Aclog::Exceptions::NotFound do + error_response message: "That page does not exists.", status: 404 + end + rescue_from Aclog::Exceptions::Forbidden do + error_response message: "You do not have permission to access this page.", status: 403 + end + rescue_from Aclog::Exceptions::OAuthEchoError do + error_response message: "Invalid OAuth Echo data.", status: 401 + end + + rescue_from :all + + helpers do + def current_user + @_current_user ||= begin + if request.headers["X-Verify-Credentials-Authorization"] + user_id = authenticate_with_twitter_oauth_echo + User.find(user_id) + end + end + rescue + raise Aclog::Exceptions::OAuthEchoError + end + + def permitted_to_see?(user_or_tweet) + user_or_tweet.is_a?(User) ? + !user_or_tweet.protected? || current_user.try(:permitted_to_see?, user_or_tweet) : + !user_or_tweet.user.protected? || current_user.try(:permitted_to_see?, user_or_tweet.user) + end + end + + mount ApiTweets + mount ApiUsers + mount ApiDeprecated + + route :any, "*path", ignore: true do + raise Aclog::Exceptions::NotFound + end +end diff --git a/app/api/api_deprecated.rb b/app/api/api_deprecated.rb new file mode 100644 index 0000000..9af04fb --- /dev/null +++ b/app/api/api_deprecated.rb @@ -0,0 +1,81 @@ +class ApiDeprecated < Grape::API + resource :tweets do + params_user = -> do + params do + optional :user_id, type: Integer + optional :screen_name, type: String + end + end + + params_user_b = -> do + params do + optional :user_id_b, type: Integer + optional :screen_name_b, type: String + end + end + + params_pagination = -> do + params do + optional :count, type: Integer + optional :page, type: Integer + end + end + + params_pagination_with_ids = -> do + params_pagination[] + params do + optional :since_id, type: Integer + optional :max_id, type: Integer + end + end + + helpers do + def user + @_user ||= begin + user = User.find(id: params[:user_id], screen_name: params[:screen_name]) + raise Aclog::Exceptions::Forbidden unless permitted_to_see?(user) + user + end + end + + def paginate(tweets) + count = [(params[:count] || Settings.tweets.count.default).to_i, Settings.tweets.count.max].min + tweets.limit(count).page((params[:page] || 1).to_i) + end + + def paginate_with_ids(tweets) + paginate(tweets).max_id(params[:max_id]).since_id(params[:since_id]) + end + end + + desc "Deprecated. Use GET tweets/user_best" + params_user[] + params_pagination[] + get "best", rabl: "tweets" do + @tweets = paginate user.tweets.reacted.order_by_reactions + end + + desc "Deprecated. Use GET tweets/user_timeline" + params_user[] + params_pagination_with_ids[] + get "timeline", rabl: "tweets" do + @tweets = paginate_with_ids user.tweets.reacted.order_by_id + end + + desc "Deprecated. Use GET tweets/user_discoveries" + params_user[] + params_pagination_with_ids[] + get "discoveries", rabl: "tweets" do + @tweets = paginate_with_ids(Tweet).discovered_by(user).order_by_id + end + + desc "Deprecated. Use GET tweets/user_discovered_by" + params_user[] + params_user_b[] + params_pagination_with_ids[] + get "discovered_by", rabl: "tweets" do + @tweets = paginate_with_ids(user.tweets).discovered_by(user_b).order_by_id + end + end +end + diff --git a/app/api/api_tweets.rb b/app/api/api_tweets.rb new file mode 100644 index 0000000..168fef3 --- /dev/null +++ b/app/api/api_tweets.rb @@ -0,0 +1,111 @@ +class ApiTweets < Grape::API + resource :tweets do + params_user = -> do + params do + optional :user_id, type: Integer, desc: "The numerical ID of the user for whom to return results for." + optional :screen_name, type: String, desc: "The username of the user for whom to return results for." + end + end + + params_source_user = -> do + params do + optional :source_user_id, type: Integer, desc: "The numerical ID of the user for whom to return results for." + optional :source_screen_name, type: String, desc: "The username of the user for whom to return results for." + end + end + + params_pagination = -> do + params do + optional :count, type: Integer, desc: "The number of tweets to retrieve. Must be less than or equal to #{Settings.tweets.count.max}, defaults to #{Settings.tweets.count.default}." + optional :page, type: Integer, desc: "The page number of results to retrieve." + end + end + + params_pagination_with_ids = -> do + params_pagination[] + params do + optional :since_id, type: Integer, desc: "Returns results with an ID greater than the specified ID." + optional :max_id, type: Integer, desc: "Returns results with an ID less than or equal to the specified ID." + end + end + + params_threshold = -> do + params do + optional :reactions, type: Integer, desc: "Returns Tweets which has received reactions more than (or equal to) the specified number of times." + end + end + + helpers do + def user + @_user ||= begin + user = User.find(id: params[:user_id], screen_name: params[:screen_name]) + raise Aclog::Exceptions::Forbidden unless permitted_to_see?(user) + user + end + end + + def source_user + user = User.find(id: params[:source_user_id], screen_name: params[:source_screen_name]) + raise Aclog::Exceptions::Forbidden unless permitted_to_see?(user) + user + end + + def paginate(tweets) + count = [(params[:count] || Settings.tweets.count.default).to_i, Settings.tweets.count.max].min + tweets.limit(count).page((params[:page] || 1).to_i) + end + + def paginate_with_ids(tweets) + paginate(tweets).max_id(params[:max_id]).since_id(params[:since_id]) + end + end + + desc "Returns a single Tweet, specified by ID.", example_params: { id: 43341783446466560 } + params do + requires :id, type: Integer, desc: "The numerical ID of the desired Tweet." + end + get "show", rabl: "tweet" do + @tweet = Tweet.find(params[:id]) + raise Aclog::Exceptions::Forbidden unless permitted_to_see?(@tweet) + end + + desc "Returns Tweets, specified by comma-separated IDs.", example_params: { ids: "43341783446466560,50220624609685505" } + params do + requires :ids, type: String, regexp: /^\d+(,\d+)*$/, desc: "A comma-separated list of Tweet IDs, up to #{Settings.tweets.count.max} are allowed in a single request." + end + get "lookup", rabl: "tweets" do + @tweets = Tweet.where(id: params[:ids].split(",").map(&:to_i)) + @tweets = @tweets.select {|tweet| permitted_to_see?(tweet) } + end + + desc "Returns the best Tweets of a user, specified by username or user ID.", example_params: { user_id: 280414022, count: 2, page: 3 } + params_user[] + params_pagination[] + get "user_best", rabl: "tweets" do + @tweets = paginate user.tweets.reacted.order_by_reactions + end + + desc "Returns the newest Tweets of a user, specified by username or user ID.", example_params: { screen_name: "toshi_a", count: 3, max_id: 432112694871605249 } + params_user[] + params_pagination_with_ids[] + params_threshold[] + get "user_timeline", rabl: "tweets" do + @tweets = paginate_with_ids user.tweets.reacted(params[:reactions]).order_by_id + end + + desc "Returns the Tweets which a user specified by username or user ID discovered.", example_params: { user_id: 120726371, count: 2 } + params_user[] + params_pagination_with_ids[] + get "user_discoveries", rabl: "tweets" do + @tweets = paginate_with_ids(Tweet).discovered_by(user).order_by_id + end + + desc "Returns the specified user's Tweets which another specified user discovered.", example_params: { user_id: 120726371, count: 2, source_screen_name: "cn" } + params_user[] + params_source_user[] + params_pagination_with_ids[] + get "user_discovered_by", rabl: "tweets" do + @tweets = paginate_with_ids(user.tweets).discovered_by(source_user).order_by_id + end + end +end diff --git a/app/api/api_users.rb b/app/api/api_users.rb new file mode 100644 index 0000000..898856e --- /dev/null +++ b/app/api/api_users.rb @@ -0,0 +1,48 @@ +class ApiUsers < Grape::API + resource :users do + params_user = -> do + params do + optional :id, type: Integer, desc: "The numerical ID of the user for whom to return results for." + optional :screen_name, type: String, desc: "The username of the user for whom to return results for." + end + end + + helpers do + def user + @_user ||= begin + user = User.find(id: params[:id] || params[:user_id], screen_name: params[:screen_name]) + raise Aclog::Exceptions::Forbidden unless permitted_to_see?(user) + user + end + end + end + + desc "Returns the stats of a user, specified by username or user ID.", example_params: { user_id: 280414022 } + params_user[] + get "stats", rabl: "user_stats" do + @user = user + end + + desc "Returns the list of the users who discovored the Tweets of a user, specified by username or user ID.", example_params: { user_id: 99008565 } + params_user[] + get "discovored_by", rabl: "user_discovored_by_and_users" do + @result = user.count_discovered_by.take(Settings.users.count) + end + + desc "Returns the list of the users discovored by a user, specified by username or user ID.", example_params: { screen_name: "cn" } + params_user[] + get "discovored_users", rabl: "user_discovored_by_and_users" do + @result = user.count_discovered_users.take(Settings.users.count) + end + + desc "", nodoc: true, example_params: { id: "280414022,99008565" } + params do + requires :id, type: String + end + get "screen_name" do + # does not use RABL + user_ids = (params[:id] || params[:ids] || params[:user_id] || params[:user_ids]).split(",").map { |i| i.to_i } + User.where(id: user_ids).pluck(:id, :screen_name).map { |id, screen_name| { id: id, screen_name: screen_name } } + end + end +end diff --git a/app/api/concerns/twitter_oauth_echo_authentication.rb b/app/api/concerns/twitter_oauth_echo_authentication.rb new file mode 100644 index 0000000..21a9613 --- /dev/null +++ b/app/api/concerns/twitter_oauth_echo_authentication.rb @@ -0,0 +1,22 @@ +require "open-uri" + +module TwitterOauthEchoAuthentication + def authenticate_with_twitter_oauth_echo + twitter_provider = "https://api.twitter.com/1.1/account/verify_credentials.json" + + provider = request.headers["X-Auth-Service-Provider"] + credentials = request.headers["X-Verify-Credentials-Authorization"] + unless provider == twitter_provider && credentials + raise Aclog::Exceptions::OAuthEchoUnauthorized, "X-Auth-Service-Provider is invalid" + end + + json = open(twitter_provider, "Authorization" => credentials) {|res| + Yajl::Parser.parse(res.read) + } + + json["id"] + rescue + raise Aclog::Exceptions::OAuthEchoUnauthorized, $! + end +end + diff --git a/app/api/templates/tweet.rabl b/app/api/templates/tweet.rabl new file mode 100644 index 0000000..494e790 --- /dev/null +++ b/app/api/templates/tweet.rabl @@ -0,0 +1,6 @@ +object @tweet + +attributes :id, :user_id, :favorites_count, :retweets_count +node(:favoriters) {|obj| obj.favoriters.pluck(:user_id) } +node(:retweeters) {|obj| obj.retweeters.pluck(:user_id) } + diff --git a/app/api/templates/tweets.rabl b/app/api/templates/tweets.rabl new file mode 100644 index 0000000..1017230 --- /dev/null +++ b/app/api/templates/tweets.rabl @@ -0,0 +1,3 @@ +collection @tweets +extends "tweet" + diff --git a/app/api/templates/user_discovored_by_and_users.rabl b/app/api/templates/user_discovored_by_and_users.rabl new file mode 100644 index 0000000..54b7360 --- /dev/null +++ b/app/api/templates/user_discovored_by_and_users.rabl @@ -0,0 +1,6 @@ +collection @result + +node(:id) {|obj| obj[0] } +node(:favorites_count) {|obj| obj[1] } +node(:retweets_count) {|obj| obj[2] } + diff --git a/app/api/templates/user_stats.rabl b/app/api/templates/user_stats.rabl new file mode 100644 index 0000000..cd28dc9 --- /dev/null +++ b/app/api/templates/user_stats.rabl @@ -0,0 +1,5 @@ +object @user + +attributes :id +node(:reactions_count) {|obj| obj.stats.reactions_count } + diff --git a/app/assets/javascripts/apidocs.js b/app/assets/javascripts/apidocs.js new file mode 100644 index 0000000..fa17510 --- /dev/null +++ b/app/assets/javascripts/apidocs.js @@ -0,0 +1,12 @@ +$(function() { + var loading = $("#example_request_loading"); + if (loading) { + var code = loading.parent(); + $.ajax($("#example_request_uri").text()).done(function(data) { + code.text(JSON.stringify(data, null, 2)); + }).fail(function() { + code.text("failed to load example...."); + }); + } +}); + diff --git a/app/controllers/apidocs_controller.rb b/app/controllers/apidocs_controller.rb index a24a2f2..003e71b 100644 --- a/app/controllers/apidocs_controller.rb +++ b/app/controllers/apidocs_controller.rb @@ -2,26 +2,46 @@ class ApidocsController < ApplicationController before_action :reload_docs def index - @resources = Apidoc.resources + @routes = @@routes end def endpoint - @resource = Apidoc.resources[params[:resource].to_sym] + @routes = @@routes - unless @resource + method = @@routes[params[:method]] + unless method raise Aclog::Exceptions::DocumentNotFound end - @endpoint = @resource.endpoints[params[:name].to_sym] + @resource = method[params[:namespace]] + unless @resource + raise Aclog::Exceptions::DocumentNotFound + end + @endpoint = @resource[params[:path]] unless @endpoint raise Aclog::Exceptions::DocumentNotFound end + + if @endpoint.route_example_params + @example_request_uri = api_url + @endpoint.route_path.sub(/\(\.:format\)$/, ".json") + @example_request_uri += "?" + @endpoint.route_example_params.to_param + end end private def reload_docs - Apidoc.reload! if Rails.env.development? + @@routes ||= begin + h = {} + Api.routes.reject {|r| r.route_ignore }.each {|route| + # /tweets/show(.:format) -> tweets, show + method = route.route_method.downcase + namespace = route.route_namespace[1..-1] + path = route.route_path.sub(route.route_namespace, "")[1..-11] # 10: "(.:format)".size + ((h[method] ||= {})[namespace] ||= {})[path] = route + } + h + end end end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index b756c2f..fb738a0 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -1,37 +1,31 @@ class ApplicationController < ActionController::Base - include TwitterOauthEchoAuthentication - protect_from_forgery + after_action :set_content_type_to_xhtml, :tidy_response_body - helper_method :current_user, :logged_in? + helper_method :logged_in?, :current_user helper_method :authorized_to_show_user?, :authorized_to_show_user_best? protected - def current_user - return @_current_user if defined? @_current_user + def logged_in? + !!session[:user_id] + end - @_current_user = begin - if session[:user_id] + def current_user + @_current_user ||= begin + if logged_in? User.find(session[:user_id]) - elsif request.headers["X-Verify-Credentials-Authorization"] - user_id = authenticate_with_twitter_oauth_echo - User.find(user_id) + else + nil end - rescue - nil end end - def logged_in? - !!current_user - end - def authorized_to_show_user?(user) - !user.protected? || current_user == user || current_user.try(:following?, user) || false + !user.protected? || (logged_in? && current_user.permitted_to_see?(user)) end def authorized_to_show_user_best?(user) - !user.private? || current_user == user + (!user.private? || current_user == user) && authorized_to_show_user?(user) end def authorize_to_show_user!(user) diff --git a/app/controllers/concerns/twitter_oauth_echo_authentication.rb b/app/controllers/concerns/twitter_oauth_echo_authentication.rb deleted file mode 100644 index 21a9613..0000000 --- a/app/controllers/concerns/twitter_oauth_echo_authentication.rb +++ /dev/null @@ -1,22 +0,0 @@ -require "open-uri" - -module TwitterOauthEchoAuthentication - def authenticate_with_twitter_oauth_echo - twitter_provider = "https://api.twitter.com/1.1/account/verify_credentials.json" - - provider = request.headers["X-Auth-Service-Provider"] - credentials = request.headers["X-Verify-Credentials-Authorization"] - unless provider == twitter_provider && credentials - raise Aclog::Exceptions::OAuthEchoUnauthorized, "X-Auth-Service-Provider is invalid" - end - - json = open(twitter_provider, "Authorization" => credentials) {|res| - Yajl::Parser.parse(res.read) - } - - json["id"] - rescue - raise Aclog::Exceptions::OAuthEchoUnauthorized, $! - end -end - diff --git a/app/controllers/tweets_controller.rb b/app/controllers/tweets_controller.rb index f25a7e2..078830b 100644 --- a/app/controllers/tweets_controller.rb +++ b/app/controllers/tweets_controller.rb @@ -1,24 +1,4 @@ class TweetsController < ApplicationController - param_group :pagination_with_page_number do - optional :count, 5, "The number of tweets to retrieve. Must be less than or equal to #{Settings.tweets.count.max}, defaults to #{Settings.tweets.count.default}." - optional :page, 2, "The page number of results to retrieve." - end - - param_group :pagination_with_ids do - param_group :pagination_with_page_number - optional :since_id, 12345, "Returns results with an ID greater than the specified ID." - optional :max_id, 54321, "Returns results with an ID less than or equal to the specified ID." - end - - param_group :user do - optional :user_id, 15926668, "The numerical ID of the user for whom to return results for." - optional :screen_name, "toshi_a", "The username of the user for whom to return results for." - end - - param_group :threshold do - optional :reactions, 5, "Returns Tweets which has received reactions more than (or equal to) the specified number of times." - end - def index begin best @@ -29,26 +9,12 @@ class TweetsController < ApplicationController end end - get "tweets/show" - description "Returns a single Tweet, specified by ID." - requires :id, 43341783446466560, "The numerical ID of the desired Tweet." def show @tweet = Tweet.find(params[:id]) @user = @tweet.user authorize_to_show_user! @user end - get "tweets/lookup" - description "Returns Tweets, specified by comma-separated IDs." - requires :ids, "43341783446466560,50220624609685505", "A comma-separated list of Tweet IDs, up to #{Settings.tweets.count.max} are allowed in a single request." - def lookup - @tweets = Tweet.where(id: (params[:ids] || params[:id]).split(",").map(&:to_i)) - end - - get "tweets/best" - description "Returns the best Tweets of a user, specified by username or user ID." - param_group :user - param_group :pagination_with_page_number def best @user = require_user authorize_to_show_user! @user @@ -56,11 +22,6 @@ class TweetsController < ApplicationController @tweets = paginate_with_page_number(@user.tweets.reacted.order_by_reactions) end - get "tweets/recent" - nodoc - description "Returns the best Tweets in the recent three days of a user, specified by username or user ID." - param_group :user - param_group :pagination_with_page_number def recent @user = require_user authorize_to_show_user! @user @@ -68,55 +29,30 @@ class TweetsController < ApplicationController @tweets = paginate_with_page_number(@user.tweets.reacted.recent.order_by_reactions) end - get "tweets/timeline" - description "Returns the newest Tweets of a user, specified by username or user ID." - param_group :user - param_group :pagination_with_ids - param_group :threshold def timeline @user = require_user authorize_to_show_user! @user @tweets = paginate(@user.tweets.reacted(params[:reactions]).order_by_id) end - get "tweets/discoveries" - description "Returns the Tweets which a user specified by username or user ID discovered." - param_group :user - param_group :pagination_with_ids def discoveries @user = require_user authorize_to_show_user! @user @tweets = paginate(Tweet).discovered_by(@user).order_by_id end - get "tweets/favorites" - nodoc - description "Returns the Tweets which a user specified by username or user ID favorited." - param_group :user - param_group :pagination_with_ids def favorites @user = require_user authorize_to_show_user! @user @tweets = paginate(Tweet).favorited_by(@user).order_by_id end - get "tweets/retweets" - nodoc - description "Returns the Tweets which a user specified by username or user ID retweeted." - param_group :user - param_group :pagination_with_ids def retweets @user = require_user authorize_to_show_user! @user @tweets = paginate(Tweet).retweeted_by(@user).order_by_id end - get "tweets/discovered_by" - description "Returns the Tweets which a user specified by username or user ID retweeted." - param_group :user - optional :user_id_b, 280414022, "The numerical ID of the subject user." - optional :screen_name_b, "cn", "The username of the subject user." - param_group :pagination_with_ids def discovered_by @user = require_user authorize_to_show_user! @user @@ -125,38 +61,22 @@ class TweetsController < ApplicationController @tweets = paginate(@user.tweets.discovered_by(@source_user).order_by_id) end - get "tweets/all_best" - nodoc - param_group :pagination_with_page_number def all_best @tweets = paginate_with_page_number(Tweet.reacted.order_by_reactions) end - get "tweets/all_recent" - nodoc - param_group :pagination_with_page_number def all_recent @tweets = paginate_with_page_number(Tweet.recent.reacted.order_by_reactions) end - get "tweets/all_timeline" - nodoc - param_group :pagination_with_ids - param_group :threshold def all_timeline @tweets = paginate(Tweet.reacted(params[:reactions]).order_by_id) end - get "tweets/filter" - nodoc - param_group :pagination_with_ids def filter @tweets = paginate(Tweet.reacted.recent(7).filter_by_query(params[:q].to_s).not_protected.order_by_id) end - get "tweets/import" - nodoc - requires :id, 43341783446466560, "The numerical ID of the desired Tweet." def import raise Aclog::Exceptions::LoginRequired unless logged_in? tweet = current_user.account.import(params[:id]) @@ -193,27 +113,12 @@ class TweetsController < ApplicationController end def render(*args) - if !request.xhr? && request.format == :json - # JSON API / Atom - begin - super(*args) - rescue ActionView::MissingTemplate - if @tweets - super("_tweets") - elsif @tweet - super("_tweet") - else - raise - end - end + if @tweets && request.xhr? + super(json: { html: render_to_string(partial: "tweet", collection: @tweets, as: :tweet, formats: :html), + next_url: @next_url, + prev_url: @prev_url }) else - if @tweets && request.xhr? - super(json: { html: render_to_string(partial: "tweet", collection: @tweets, as: :tweet, formats: :html), - next_url: @next_url, - prev_url: @prev_url }) - else - super(*args) - end + super(*args) end end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 95b4626..611e106 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -1,63 +1,20 @@ class UsersController < ApplicationController - param_group :user do - optional :id, 15926668, "The numerical ID of the user for whom to return results for." - optional :screen_name, "toshi_a", "The username of the user for whom to return results for." - end - - get "users/stats" - description "Returns the stats of a user, specified by username or user ID." - param_group :user def stats @user = require_user end - get "users/discovered_by" - description "Returns the list of the users who discovored the Tweets of a user, specified by username or user ID." - param_group :user def discovered_by @user = require_user authorize_to_show_user_best! @user @result = @user.count_discovered_by.take(Settings.users.count) - - respond_to do |format| - format.html do - @cached_users = Hash[User.find(@result.map {|user_id, count| user_id }).map {|user| [user.id, user] }] - end - - format.json do - render "_users_list" - end - end + @cached_users = Hash[User.find(@result.map {|user_id, count| user_id }).map {|user| [user.id, user] }] end - get "users/discovered_users" - description "Returns the list of the users discovored by a user, specified by username or user ID." - param_group :user def discovered_users @user = require_user authorize_to_show_user_best! @user @result = @user.count_discovered_users.take(Settings.users.count) - - respond_to do |format| - format.html do - @cached_users = Hash[User.find(@result.map {|user_id, count| user_id }).map {|user| [user.id, user] }] - end - - format.json do - render "_users_list" - end - end - end - - get "users/screen_name" - nodoc - [:id, :ids, :user_id, :user_ids].each do |n| - optional n, "230367516,280414022", "A comma-separated list of User IDs." - end - def screen_name - user_ids = (params[:id] || params[:ids] || params[:user_id] || params[:user_ids]).split(",").map { |i| i.to_i } - result = User.where(id: user_ids).pluck(:id, :screen_name).map { |id, screen_name| { id: id, screen_name: screen_name } } - render json: result + @cached_users = Hash[User.find(@result.map {|user_id, count| user_id }).map {|user| [user.id, user] }] end private diff --git a/app/helpers/apidocs_helper.rb b/app/helpers/apidocs_helper.rb new file mode 100644 index 0000000..dc3892b --- /dev/null +++ b/app/helpers/apidocs_helper.rb @@ -0,0 +1,5 @@ +module ApidocsHelper + def format_endpoint_name(endpoint) + endpoint.route_method + " " + endpoint.route_path[1..-11] + end +end diff --git a/app/models/user.rb b/app/models/user.rb index 001f783..45a9b30 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -24,7 +24,7 @@ class User < ActiveRecord::Base key, value = hash.delete_if {|k, v| v.nil? }.first - where(key => value).order(updated_at: :desc).first || raise(ActiveRecord::RecordNotFound, "Couldn't find User with #{key}=#{value}") + key && where(key => value).order(updated_at: :desc).first || raise(ActiveRecord::RecordNotFound, "Couldn't find User with #{key}=#{value}") end def self.from_json(json) @@ -54,6 +54,10 @@ class User < ActiveRecord::Base !!account end + def permitted_to_see?(user) + !user.protected? || user.id == self.id || self.account.try(:following?, user.id) || false + end + def account Account.where(user_id: id).first end diff --git a/app/views/about/api.html.haml b/app/views/about/api.html.haml deleted file mode 100644 index 441f34d..0000000 --- a/app/views/about/api.html.haml +++ /dev/null @@ -1,12 +0,0 @@ -- title "API" -- caption :title -- sidebar :i -%p - aclog では JSON 形式の API を提供しています。 -%p - = link_to "API ドキュメント", api_docs_path -%p - 非公開アカウントのデータを取得するために OAuth Echo を使う場合はこれを使用してください。
- X-Auth-Service-Provider: https://api.twitter.com/1.1/account/verify_credentials.json - - diff --git a/app/views/about/index.html.haml b/app/views/about/index.html.haml index 736e5ec..03a7f2c 100644 --- a/app/views/about/index.html.haml +++ b/app/views/about/index.html.haml @@ -4,8 +4,6 @@ aclog - if Rails.env.development? %span.text-error (dev) - %p - is a clone service of Favstar. %p = link_to "Tweet", "https://twitter.com/share", class: "twitter-share-button", data: {text: "aclog - Twitter ふぁぼられ収集サービス", count: "none", url: "http://aclog.koba789.com/"} diff --git a/app/views/apidocs/endpoint.html.haml b/app/views/apidocs/endpoint.html.haml index ef15417..f84d0cc 100644 --- a/app/views/apidocs/endpoint.html.haml +++ b/app/views/apidocs/endpoint.html.haml @@ -2,27 +2,35 @@ - caption nil - sidebar :i %ul.breadcrumb - %li= link_to "Documentation", api_docs_path - %li.active= @endpoint -%h1.page-header= @endpoint + %li= link_to "Documentation", about_api_path + %li.active= format_endpoint_name(@endpoint) +%h1.page-header= format_endpoint_name(@endpoint) %div - %div= @endpoint.description + %div= @endpoint.route_description %h2 Resource URL - %div= url_for(controller: params[:resource], action: params[:name], only_path: false) + %div= api_url + @endpoint.route_path %h2 Parameters %table.table %tbody - - @endpoint.parameters.each do |parameter| + - @endpoint.route_params.each do |name, options| %tr %td - %strong= parameter.name - - if parameter.required? + %strong= name + - if options[:required] %small required - else %small optional %td - %p= parameter.description - - if parameter.example - %p - Example Value: - = parameter.example + %p= options[:desc] + %p + Type: + = options[:type] + - if @example_request_uri + %h2 Example Request + .request + %span= @endpoint.route_method + %code#example_request_uri= @example_request_uri + .response + %pre + %code + #example_request_loading= image_tag "loading.gif", alt: "loading..." diff --git a/app/views/apidocs/index.html.haml b/app/views/apidocs/index.html.haml index 198e9f8..2f37d73 100644 --- a/app/views/apidocs/index.html.haml +++ b/app/views/apidocs/index.html.haml @@ -3,16 +3,34 @@ - sidebar :i %ul.breadcrumb %li.active Documentation -%h2.page-header Resources -- @resources.each do |name, resource| - %h3= resource.name +%h1.page-header Aclog API +%p + Aclog では JSON 形式の API を提供しています。 +%hr +%p + ツイートを非公開にしているアカウントの情報にアクセスするには本人であるか、対象のアカウントをフォローしている必要があります。Aclog API では、その認証に OAuth Echo を使用します。 +%p + OAuth Echo を使用するには以下のリクエストヘッダを使用します。 + %pre + %code + :preserve + X-Auth-Service-Provider: https://api.twitter.com/1.1/account/verify_credentials.json + X-Verify-Credentials-Authorization: OAuth rea... +%p + OAuth Echo の詳細については + = link_to "Twitter のドキュメント", "https://dev.twitter.com/pages/oauth_echo" + を参照してください。 + +%h2.page-header Endpoints +- @routes.values.inject(:merge).each do |namespace, endpoints| + %h3= namespace.titleize %table.table %thead %th Resource %th Description %tbody - - resource.endpoints.reject {|a, e| e.nodoc }.each do |action, endpoint| + - endpoints.select {|_, e| !e.route_nodoc }.each do |path, endpoint| %tr - %td= link_to endpoint, api_docs_endpoint_path(name, action) - %td= endpoint.description + %td= link_to format_endpoint_name(endpoint), about_api_endpoint_path(endpoint.route_method.downcase, namespace, path) + %td= endpoint.route_description diff --git a/app/views/errors/render_error.json.jbuilder b/app/views/errors/render_error.json.jbuilder deleted file mode 100644 index ebbeb4d..0000000 --- a/app/views/errors/render_error.json.jbuilder +++ /dev/null @@ -1,5 +0,0 @@ -json.error do |json| - json.status response.status - json.message @message -end - diff --git a/app/views/shared/sidebar/_i.html.haml b/app/views/shared/sidebar/_i.html.haml index 776cde6..04150c2 100644 --- a/app/views/shared/sidebar/_i.html.haml +++ b/app/views/shared/sidebar/_i.html.haml @@ -5,4 +5,3 @@ = link_to "best", best_path, class: "list-group-item" = link_to "recent", recent_path, class: "list-group-item" = link_to "timeline", timeline_path, class: "list-group-item" - = link_to "filter", filter_path, class: "list-group-item" diff --git a/app/views/tweets/_tweet.json.jbuilder b/app/views/tweets/_tweet.json.jbuilder deleted file mode 100644 index 993b824..0000000 --- a/app/views/tweets/_tweet.json.jbuilder +++ /dev/null @@ -1,9 +0,0 @@ -json.(tweet, :id, :favorites_count, :retweets_count) - -json.user_id tweet.user_id - -user_limit = Integer(params[:limit]) rescue nil - -json.favoriters tweet.favorites.limit(user_limit).pluck(:user_id) -json.retweeters tweet.retweets.limit(user_limit).pluck(:user_id) - diff --git a/app/views/tweets/_tweets.json.jbuilder b/app/views/tweets/_tweets.json.jbuilder deleted file mode 100644 index c5bcd47..0000000 --- a/app/views/tweets/_tweets.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.array!(@tweets, partial: "tweet", as: :tweet) diff --git a/app/views/tweets/show.json.jbuilder b/app/views/tweets/show.json.jbuilder deleted file mode 100644 index c92aaa0..0000000 --- a/app/views/tweets/show.json.jbuilder +++ /dev/null @@ -1 +0,0 @@ -json.partial! "tweet", tweet: @tweet diff --git a/app/views/users/_users_list.json.jbuilder b/app/views/users/_users_list.json.jbuilder deleted file mode 100644 index 57bcb32..0000000 --- a/app/views/users/_users_list.json.jbuilder +++ /dev/null @@ -1,6 +0,0 @@ -json.array! @result do |user_id, favorites_count, retweets_count| - json.user_id user_id - json.favorites_count favorites_count - json.retweets_count retweets_count -end - diff --git a/app/views/users/stats.json.jbuilder b/app/views/users/stats.json.jbuilder deleted file mode 100644 index 46f07b6..0000000 --- a/app/views/users/stats.json.jbuilder +++ /dev/null @@ -1,2 +0,0 @@ -json.id @user.id -json.(@user.stats, :reactions_count) diff --git a/config/application.rb b/config/application.rb index 8844010..8e8204f 100644 --- a/config/application.rb +++ b/config/application.rb @@ -6,7 +6,6 @@ require "action_controller/railtie" require "action_mailer/railtie" require "sprockets/railtie" # require "rails/test_unit/railtie" -require "./lib/apidoc" # Require the gems listed in Gemfile, including any gems # you've limited to :test, :development, or :production. @@ -18,6 +17,10 @@ module Aclog # Application configuration should go into files in config/initializers # -- all .rb files in that directory are automatically loaded. + # Configure grape + config.paths.add "app/api", eager_load: true + config.paths.add "app/api/concerns", eager_load: true + # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += Dir["#{config.root}/lib/", "#{config.root}/lib/**/"] @@ -42,5 +45,9 @@ module Aclog g.test_framework :rspec g.fixture_replacement :factory_girl end + + config.middleware.use(Rack::Config) do |env| + env["api.tilt.root"] = "#{config.root}/app/api/templates" + end end end diff --git a/config/initializers/rabl.rb b/config/initializers/rabl.rb new file mode 100644 index 0000000..f11eb19 --- /dev/null +++ b/config/initializers/rabl.rb @@ -0,0 +1,3 @@ +Rabl.configure do |config| + config.include_json_root = false +end diff --git a/config/routes.rb b/config/routes.rb index e43d793..067ea79 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,11 +1,7 @@ Aclog::Application.routes.draw do root to: "about#index" - # JSON API - scope "/api", format: false, defaults: { format: "json" } do - get "/users/:action.json", controller: "users" - get "/tweets/:action.json", controller: "tweets" - end + mount Aclog::Api => "/api" # Internals / SessionsController get "/i/callback" => "sessions#create" @@ -25,9 +21,8 @@ Aclog::Application.routes.draw do get "/i/filter" => "tweets#filter", as: "filter" get "/about" => "about#about", as: "about" - get "/about/api" => "about#api", as: "about_api" - get "/about/api/docs" => "apidocs#index", as: "api_docs" - get "/about/api/docs/:resource/:name" => "apidocs#endpoint", as: "api_docs_endpoint" + get "/about/api" => "apidocs#index", as: "about_api" + get "/about/api/:method/:namespace/:path" => "apidocs#endpoint", as: "about_api_endpoint", constraints: { namespace: /[\w\/]+/ } # User pages scope "/:screen_name" do diff --git a/lib/aclog/exceptions.rb b/lib/aclog/exceptions.rb index 9b7078f..5eafc99 100644 --- a/lib/aclog/exceptions.rb +++ b/lib/aclog/exceptions.rb @@ -17,5 +17,10 @@ module Aclog class AccountPrivate < UserError; end class DocumentNotFound < StandardError; end + + class AclogError < StandardError; end + class NotFound < AclogError; end + class Forbidden < AclogError; end + class OAuthEchoError < AclogError; end end end diff --git a/lib/apidoc.rb b/lib/apidoc.rb deleted file mode 100644 index c85eceb..0000000 --- a/lib/apidoc.rb +++ /dev/null @@ -1,17 +0,0 @@ -Dir.glob(File.expand_path("../apidoc/**/*.rb", __FILE__)) {|file| require file } - -module Apidoc - extend self - - def resources - @@resources ||= {} - end - - def reload! - @@resources = nil - dir = "#{Rails.root}/app/controllers/" - Dir.glob("#{dir}**/*.rb") do |path| - ActiveSupport::Dependencies.load_file path - end - end -end diff --git a/lib/apidoc/controller_dsl.rb b/lib/apidoc/controller_dsl.rb deleted file mode 100644 index 38b1727..0000000 --- a/lib/apidoc/controller_dsl.rb +++ /dev/null @@ -1,38 +0,0 @@ -module Apidoc - module ControllerDsl - include Resources - include Endpoints - include Parameters - - private - def method_added(method_name) - super(method_name) - - if _apidoc_endpoint_started? - _apidoc_resource.endpoints[method_name] = _apidoc_current_endpoint - self._apidoc_current_endpoint = nil - end - end - - def _apidoc_resource - name = self.name.sub(/Controller$/, "").underscore - Apidoc.resources[name.to_sym] ||= Resource.new(name.titleize) - end - - def _apidoc_current_endpoint - @_apidoc_current_endpoint || raise(DslError, "Endpoint definition is not started.") - end - - def _apidoc_current_endpoint=(value) - @_apidoc_current_endpoint = value - end - - def _apidoc_endpoint_started? - @_apidoc_current_endpoint.present? - end - - def _apidoc_param_groups - @_apidoc_param_groups ||= {} - end - end -end diff --git a/lib/apidoc/controller_dsl/endpoint.rb b/lib/apidoc/controller_dsl/endpoint.rb deleted file mode 100644 index 18d4fe2..0000000 --- a/lib/apidoc/controller_dsl/endpoint.rb +++ /dev/null @@ -1,30 +0,0 @@ -module Apidoc - module ControllerDsl - module Endpoints - def get(endpoint) - _apidoc_endpoint(:get, endpoint) - end - - def post(endpoint) - _apidoc_endpoint(:post, endpoint) - end - - def _apidoc_endpoint(method, endpoint) - if _apidoc_endpoint_started? - raise DslError, "Previous endpoint #{_apidoc_current_endpoint} definition is not completed." - end - - self._apidoc_current_endpoint = Endpoint.new(method, endpoint) - end - - def description(description) - _apidoc_current_endpoint.description = description - end - - def nodoc - _apidoc_current_endpoint.nodoc = true - end - end - end -end - diff --git a/lib/apidoc/controller_dsl/parameters.rb b/lib/apidoc/controller_dsl/parameters.rb deleted file mode 100644 index c3d285f..0000000 --- a/lib/apidoc/controller_dsl/parameters.rb +++ /dev/null @@ -1,27 +0,0 @@ -module Apidoc - module ControllerDsl - module Parameters - def requires(name, example, description) - _apidoc_current_endpoint.parameters << Parameter.new(name, example, description, required: true) - end - - def optional(name, example, description) - _apidoc_current_endpoint.parameters << Parameter.new(name, example, description, required: false) - end - - def param_group(name, &blk) - if block_given? - _apidoc_param_groups[name] = blk - else - blk = _apidoc_param_groups[name] - if blk - blk.call - else - raise DslError, "Parameters group #{name} is not defined." - end - end - end - end - end -end - diff --git a/lib/apidoc/controller_dsl/resources.rb b/lib/apidoc/controller_dsl/resources.rb deleted file mode 100644 index 110b564..0000000 --- a/lib/apidoc/controller_dsl/resources.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Apidoc - module ControllerDsl - module Resources - private - def resource_description(description) - _apidoc_resource.description = description - end - end - end -end - - diff --git a/lib/apidoc/endpoint.rb b/lib/apidoc/endpoint.rb deleted file mode 100644 index 14dedeb..0000000 --- a/lib/apidoc/endpoint.rb +++ /dev/null @@ -1,18 +0,0 @@ -module Apidoc - class Endpoint - attr_reader :method, :name, :parameters - attr_accessor :description, :nodoc - - def initialize(method, name) - @method = method - @name = name - @parameters = [] - @description = nil - @nodoc = false - end - - def to_s - "#{method.to_s.upcase} #{name}" - end - end -end diff --git a/lib/apidoc/exceptions.rb b/lib/apidoc/exceptions.rb deleted file mode 100644 index ee4685f..0000000 --- a/lib/apidoc/exceptions.rb +++ /dev/null @@ -1,17 +0,0 @@ -module Apidoc - class Error < StandardError; end - - class ParameterMissing < Error - def initialize(param) - super("Parameter is missing or the value is empty: #{param}") - end - end - - class ParameterInvalid < Error - def initialize(param) - super("Parameter is invalid: #{param}") - end - end - - class DslError < SyntaxError; end -end diff --git a/lib/apidoc/parameter.rb b/lib/apidoc/parameter.rb deleted file mode 100644 index 58b3d26..0000000 --- a/lib/apidoc/parameter.rb +++ /dev/null @@ -1,13 +0,0 @@ -module Apidoc - class Parameter - attr_reader :name, :example, :description, :required - alias required? required - - def initialize(name, example, description, required: false) - @name = name - @example = example - @description = description - @required = required - end - end -end diff --git a/lib/apidoc/railtie.rb b/lib/apidoc/railtie.rb deleted file mode 100644 index 9e992a3..0000000 --- a/lib/apidoc/railtie.rb +++ /dev/null @@ -1,9 +0,0 @@ -module Apidoc - class Railtie < Rails::Railtie - initializer "apidoc.controller_injections" do - ActiveSupport.on_load :action_controller do - extend ControllerDsl - end - end - end -end diff --git a/lib/apidoc/resource.rb b/lib/apidoc/resource.rb deleted file mode 100644 index b9afdf3..0000000 --- a/lib/apidoc/resource.rb +++ /dev/null @@ -1,12 +0,0 @@ -module Apidoc - class Resource - attr_reader :endpoints, :name - attr_accessor :description - - def initialize(name) - @name = name - @description = nil - @endpoints = {} - end - end -end -- cgit v1.2.3