lib/apipie/method_description.rb
module Apipie
class MethodDescription
attr_reader :full_description, :method, :resource, :apis, :examples, :see, :formats, :headers, :show
attr_accessor :metadata
def initialize(method, resource, dsl_data)
@method = method.to_s
@resource = resource
@from_concern = dsl_data[:from_concern]
@apis = ApisService.new(resource, method, dsl_data).call
@full_description = dsl_data[:description] || ''
@errors = dsl_data[:errors].map do |args|
Apipie::ErrorDescription.from_dsl_data(args)
end
@tag_list = dsl_data[:tag_list]
@returns = dsl_data[:returns].map do |code,args|
Apipie::ResponseDescription.from_dsl_data(self, code, args)
end
@see = dsl_data[:see].map do |args|
Apipie::SeeDescription.new(args)
end
@formats = dsl_data[:formats]
@examples = dsl_data[:examples]
@examples += load_recorded_examples
@metadata = dsl_data[:meta]
@params_ordered = dsl_data[:params].map do |args|
Apipie::ParamDescription.from_dsl_data(self, args)
end.reject(&:response_only?)
@params_ordered = ParamDescription.unify(@params_ordered)
@headers = dsl_data[:headers]
@show = if dsl_data.key? :show
dsl_data[:show]
else
true
end
end
def id
"#{resource._id}##{method}"
end
def params
params_ordered.reduce(ActiveSupport::OrderedHash.new) { |h,p| h[p.name] = p; h }
end
def params_ordered_self
@params_ordered
end
def params_ordered
all_params = []
parent = Apipie.get_resource_description(@resource.controller.superclass)
# get params from parent resource description
[parent, @resource].compact.each do |resource|
resource_params = resource._params_args.map do |args|
Apipie::ParamDescription.from_dsl_data(self, args)
end
merge_params(all_params, resource_params)
end
merge_params(all_params, @params_ordered)
all_params.find_all(&:validator)
end
def returns_self
@returns
end
def tag_list
all_tag_list = []
parent = Apipie.get_resource_description(@resource.controller.superclass)
# get tags from parent resource description
parent_tags = [parent, @resource].compact.flat_map(&:_tag_list_arg)
Apipie::TagListDescription.new((parent_tags + @tag_list).uniq.compact)
end
def returns
all_returns = []
parent = Apipie.get_resource_description(@resource.controller.superclass)
# get response descriptions from parent resource description
[parent, @resource].compact.each do |resource|
resource_returns = resource._returns_args.map do |code, args|
Apipie::ResponseDescription.from_dsl_data(self, code, args)
end
merge_returns(all_returns, resource_returns)
end
merge_returns(all_returns, @returns)
end
def errors
return @merged_errors if @merged_errors
@merged_errors = []
if @resource
resource_errors = @resource._errors_args.map do |args|
Apipie::ErrorDescription.from_dsl_data(args)
end
# exclude overwritten parent errors
@merged_errors = resource_errors.find_all do |err|
!@errors.any? { |e| e.code == err.code }
end
end
@merged_errors.concat(@errors)
return @merged_errors
end
def version
resource._version
end
def doc_url
crumbs = []
crumbs << @resource._version if Apipie.configuration.version_in_url
crumbs << @resource._id
crumbs << @method
Apipie.full_url crumbs.join('/')
end
def create_api_url(api)
path = api.path
unless api.from_routes
path = "#{@resource._api_base_url}#{path}"
end
path = path[0..-2] if path[-1..-1] == '/'
return path
end
def method_apis_to_json(lang = nil)
@apis.each.collect do |api|
{
:api_url => create_api_url(api),
:http_method => api.http_method.to_s,
:short_description => Apipie.app.translate(api.short_description, lang),
:deprecated => resource._deprecated || api.options[:deprecated]
}
end
end
def see
@see
end
def formats
@formats || @resource._formats
end
def to_json(lang = nil)
{
:doc_url => doc_url,
:name => @method,
:apis => method_apis_to_json(lang),
:formats => formats,
:full_description => Apipie.markup_to_html(Apipie.app.translate(@full_description, lang)),
:errors => errors.map{ |error| error.to_json(lang) }.flatten,
:params => params_ordered.map{ |param| param.to_json(lang) }.flatten,
:returns => @returns.map{ |return_item| return_item.to_json(lang) }.flatten,
:examples => @examples,
:metadata => @metadata,
:see => see.map(&:to_json),
:headers => headers,
:show => @show
}
end
# was the description defines in a module instead of directly in controller?
def from_concern?
@from_concern
end
def method_name
@method
end
private
def merge_params(params, new_params)
new_param_names = Set.new(new_params.map(&:name))
params.delete_if { |p| new_param_names.include?(p.name) }
params.concat(new_params)
end
def merge_returns(returns, new_returns)
new_return_codes = Set.new(new_returns.map(&:code))
returns.delete_if { |p| new_return_codes.include?(p.code) }
returns.concat(new_returns)
end
def load_recorded_examples
(Apipie.recorded_examples[id] || []).
find_all { |ex| ex["show_in_doc"].to_i > 0 }.
find_all { |ex| ex["versions"].nil? || ex["versions"].include?(self.version) }.
sort_by { |ex| ex["show_in_doc"] }.
map { |ex| format_example(ex.symbolize_keys) }
end
def format_example_data(data)
case data
when Array, Hash
JSON.pretty_generate(data).gsub(/: \[\s*\]/,": []").gsub(/\{\s*\}/,"{}")
else
data
end
end
def format_example(ex)
example = ""
example << "// #{ex[:title]}\n" if ex[:title].present?
example << "#{ex[:verb]} #{ex[:path]}"
example << "?#{ex[:query]}" unless ex[:query].blank?
example << "\n" << format_example_data(ex[:request_data]).to_s if ex[:request_data]
example << "\n" << ex[:code].to_s
example << "\n" << format_example_data(ex[:response_data]).to_s if ex[:response_data]
example
end
end
end