diff --git a/app/views/apipie/apipies/_action.md.erb b/app/views/apipie/apipies/_action.md.erb new file mode 100644 index 00000000..e324da48 --- /dev/null +++ b/app/views/apipie/apipies/_action.md.erb @@ -0,0 +1,18 @@ +<%- + first_api = action[:apis].try(:first) || {} +-%> +## <%= action[:name] == "index" ? "List" : action[:name].capitalize %> [<%= [first_api[:http_method], first_api[:api_url]].join(' ') %>] +<%= first_api[:short_description] %> + ++ Request (application/json) + + Attributes + <%- + action[:params].each do |p| -%> + <%= render(:partial => "param", :locals => {:param => p, :action => action}) %> + <%- + end + -%> + +<%- action[:responses].each do |r| -%> +<%= render(:partial => 'response', :locals => {:response => r, :action => action}) %> +<%- end -%> diff --git a/app/views/apipie/apipies/_headers.md.erb b/app/views/apipie/apipies/_headers.md.erb new file mode 100644 index 00000000..e69de29b diff --git a/app/views/apipie/apipies/_param.md.erb b/app/views/apipie/apipies/_param.md.erb new file mode 100644 index 00000000..4dce7383 --- /dev/null +++ b/app/views/apipie/apipies/_param.md.erb @@ -0,0 +1,5 @@ +<%- + meta = [param[:expected_type], param[:required] ? 'required' : 'optional'].compact.join(', ') + allowed_params = param[:allowed] ? ':' + param[:allowed].join(', ') : nil +-%> +- <%= ["`#{param[:full_name]}`", allowed_params, "(#{meta})", param[:description]].compact.join(' ') %> diff --git a/app/views/apipie/apipies/_resource.md.erb b/app/views/apipie/apipies/_resource.md.erb new file mode 100644 index 00000000..ff49609b --- /dev/null +++ b/app/views/apipie/apipies/_resource.md.erb @@ -0,0 +1,8 @@ +# Group <%= resource[:name].titlecase %> +<%= raw resource[:short_description] %> +<%= raw resource[:full_description] unless resource[:full_description].blank? %> +<%= render(:partial => 'headers', :locals => { headers: resource[:headers], h_level: 2 }) %> +<%- resource[:methods].each do |a| -%> + <% next if !a[:show] %> +<%= render(:partial => "action", :locals => {:action => a, :resource => resource.except(:methods)}) -%> +<%- end -%> diff --git a/app/views/apipie/apipies/_response.md.erb b/app/views/apipie/apipies/_response.md.erb new file mode 100644 index 00000000..df83d616 --- /dev/null +++ b/app/views/apipie/apipies/_response.md.erb @@ -0,0 +1,11 @@ ++ Response <%= response[:code] %> (application/vnd.api+json) + +<%= + if response[:body] + begin + JSON.pretty_generate(response[:body]).gsub(/^/, ' ') + rescue JSON::GeneratorError => e + response[:body].html_safe + end + end +-%> diff --git a/app/views/apipie/apipies/static.md.erb b/app/views/apipie/apipies/static.md.erb new file mode 100644 index 00000000..04b969d0 --- /dev/null +++ b/app/views/apipie/apipies/static.md.erb @@ -0,0 +1,11 @@ +FORMAT: 1A +DOC_HOST: <%= @doc[:doc_url].html_safe %> +API_HOST: <%= @doc[:api_url].html_safe %> + +# <%= @doc[:name].html_safe %> +<%= @doc[:info].html_safe %> +<%= @doc[:copyright].html_safe %> + +<%- @doc[:resources].sort_by(&:first).each do |key, resource| -%> +<%= render(partial: "resource", locals: {resource: resource}) -%> +<%- end -%> diff --git a/app/views/layouts/apipie/apipie.md.erb b/app/views/layouts/apipie/apipie.md.erb new file mode 100644 index 00000000..37f0bddb --- /dev/null +++ b/app/views/layouts/apipie/apipie.md.erb @@ -0,0 +1 @@ +<%= yield %> diff --git a/lib/apipie-rails.rb b/lib/apipie-rails.rb index a4b7fe97..f7ef7821 100644 --- a/lib/apipie-rails.rb +++ b/lib/apipie-rails.rb @@ -5,13 +5,13 @@ require "apipie/routing" require "apipie/markup" require "apipie/apipie_module" -require "apipie/dsl_definition" +require "apipie/dsl/definition" require "apipie/configuration" require "apipie/method_description" require "apipie/resource_description" require "apipie/param_description" require "apipie/errors" -require "apipie/error_description" +require "apipie/response_description" require "apipie/see_description" require "apipie/validator" require "apipie/railtie" @@ -21,3 +21,11 @@ if Rails.version.start_with?("3.0") warn 'Warning: apipie-rails is not going to support Rails 3.0 anymore in future versions' end + +module Apipie + + def self.root + @root ||= Pathname.new(File.dirname(File.expand_path(File.dirname(__FILE__), '/../'))) + end + +end diff --git a/lib/apipie/client/generator.rb b/lib/apipie/client/generator.rb index ede403a8..e429f440 100755 --- a/lib/apipie/client/generator.rb +++ b/lib/apipie/client/generator.rb @@ -109,7 +109,7 @@ def substituted_url(method) end def transformation_hash(method) - method[:params].find_all { |p| p[:expected_type] == "hash" && !p[:params].nil? }.reduce({ }) do |h, p| + method[:params].find_all { |p| p[:expected_type] == "object" && !p[:params].nil? }.reduce({ }) do |h, p| h.update(p[:name] => p[:params].map { |pp| pp[:name] }) end end diff --git a/lib/apipie/dsl/action.rb b/lib/apipie/dsl/action.rb new file mode 100644 index 00000000..d32eed98 --- /dev/null +++ b/lib/apipie/dsl/action.rb @@ -0,0 +1,77 @@ +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Action + + def def_param_group(name, &block) + Apipie.add_param_group(self, name, &block) + end + + # + # # load paths from routes and don't provide description + # api + # + def api(method, path, desc = nil, options={}) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:api] = true + _apipie_dsl_data[:api_args] << [method, path, desc, options] + end + + # # load paths from routes + # api! "short description", + # + def api!(desc = nil, options={}) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:api] = true + _apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options } + end + + # Reference other similar method + # + # api :PUT, '/articles/:id' + # see "articles#create" + # def update; end + def see(*args) + return unless Apipie.active_dsl? + _apipie_dsl_data[:see] << args + end + + # Show some example of what does the described + # method return. + def example(example) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:examples] << example.strip_heredoc + end + + # Determine if the method should be included + # in the documentation + def show(show) + return unless Apipie.active_dsl? + _apipie_dsl_data[:show] = show + end + + # Describe whole resource + # + # Example: + # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012' + # param :id, Fixnum, :desc => "User ID", :required => true + # desc <<-EOS + # Long description... + # EOS + def resource_description(options = {}, &block) #:doc: + return unless Apipie.active_dsl? + raise ArgumentError, "Block expected" unless block_given? + + dsl_data = Resource::Description.eval_dsl(self, &block) + versions = dsl_data[:api_versions] + @apipie_resource_descriptions = versions.map do |version| + Apipie.define_resource_description(self, version, dsl_data) + end + Apipie.set_controller_versions(self, versions) + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/base.rb b/lib/apipie/dsl/base.rb new file mode 100644 index 00000000..b6043e6e --- /dev/null +++ b/lib/apipie/dsl/base.rb @@ -0,0 +1,42 @@ +# Apipie DSL functions. +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Base + attr_reader :apipie_resource_descriptions, :api_params + + private + + def _apipie_dsl_data + @_apipie_dsl_data ||= _apipie_dsl_data_init + end + + def _apipie_dsl_data_clear + @_apipie_dsl_data = nil + end + + def _apipie_dsl_data_init + @_apipie_dsl_data = { + :api => false, + :api_args => [], + :api_from_routes => nil, + :responses => [], + :params => [], + :headers => [], + :resource_id => nil, + :short_description => nil, + :description => nil, + :examples => [], + :see => [], + :formats => nil, + :api_versions => [], + :meta => nil, + :show => true + } + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/common.rb b/lib/apipie/dsl/common.rb new file mode 100644 index 00000000..445012d3 --- /dev/null +++ b/lib/apipie/dsl/common.rb @@ -0,0 +1,170 @@ +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Common + def api_versions(*versions) + _apipie_dsl_data[:api_versions].concat(versions) + end + alias :api_version :api_versions + + # Describe the next method. + # + # Example: + # desc "print hello world" + # def hello_world + # puts "hello world" + # end + # + def desc(description) #:doc: + return unless Apipie.active_dsl? + if _apipie_dsl_data[:description] + raise "Double method description." + end + _apipie_dsl_data[:description] = description + end + alias :description :desc + alias :full_description :desc + + # describe next method with document in given path + # in convension, these doc located under "#{Rails.root}/doc" + # Example: + # document "hello_world.md" + # def hello_world + # puts "hello world" + # end + def document path + content = File.open(File.join(Rails.root, Apipie.configuration.doc_path, path)).read + desc content + end + + # Describe available request/response formats + # + # formats ['json', 'jsonp', 'xml'] + def formats(formats) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:formats] = formats + end + + # Describe additional metadata + # + # meta :author => { :name => 'John', :surname => 'Doe' } + def meta(meta) #:doc: + _apipie_dsl_data[:meta] = meta + end + + + # Describe possible responses + # + # Example: + # response :desc => "speaker is sleeping", :code => 500, :meta => [:some, :more, :data] + # response 500, "speaker is sleeping" + # def hello_world + # return 500 if self.speaker.sleeping? + # puts "hello world" + # end + # + def response(code, example=nil, options={}) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:responses] << [code, example, options] + end + + def error(code, example=nil, options={}) + response(code, example, options) + end + + def success(code, example=nil, options={}) + response(code, example, options) + end + + def _apipie_define_validators(description) + + # [re]define method only if validation is turned on + if description && (Apipie.configuration.validate == true || + Apipie.configuration.validate == :implicitly || + Apipie.configuration.validate == :explicitly) + + _apipie_save_method_params(description.method, description.params) + + unless instance_methods.include?(:apipie_validations) + define_method(:apipie_validations) do + method_params = self.class._apipie_get_method_params(action_name) + + if Apipie.configuration.validate_presence? + method_params.each do |_, param| + # check if required parameters are present + raise ParamMissing.new(param) if param.required && !params.has_key?(param.name) + end + end + + if Apipie.configuration.validate_value? + method_params.each do |_, param| + # params validations + param.validate(params[:"#{param.name}"]) if params.has_key?(param.name) + end + end + + # Only allow params passed in that are defined keys in the api + # Auto skip the default params (format, controller, action) + if Apipie.configuration.validate_key? + params.reject{|k,_| %w[format controller action].include?(k.to_s) }.each_key do |param| + # params allowed + raise UnknownParam.new(param) if method_params.select {|_,p| p.name.to_s == param.to_s}.empty? + end + end + + if Apipie.configuration.process_value? + @api_params ||= {} + method_params.each do |_, param| + # params processing + @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name) + end + end + end + end + + if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true) + old_method = instance_method(description.method) + + define_method(description.method) do |*args| + apipie_validations + + # run the original method code + old_method.bind(self).call(*args) + end + end + + end + end + + def _apipie_save_method_params(method, params) + @method_params ||= {} + @method_params[method] = params + end + + def _apipie_get_method_params(method) + @method_params[method] + end + + # Describe request header. + # Headers can't be validated with config.validate_presence = true + # + # Example: + # header 'ClientId', "client-id" + # def show + # render :text => headers['HTTP_CLIENT_ID'] + # end + # + def header(header_name, description, options = {}) #:doc + return unless Apipie.active_dsl? + _apipie_dsl_data[:headers] << { + name: header_name, + description: description, + options: options + } + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/concern.rb b/lib/apipie/dsl/concern.rb new file mode 100644 index 00000000..7d9ccdc8 --- /dev/null +++ b/lib/apipie/dsl/concern.rb @@ -0,0 +1,44 @@ +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Concern + include Apipie::DSL::Base + include Apipie::DSL::Common + include Apipie::DSL::Action + include Apipie::DSL::Param + + # the concern was included into a controller + def included(controller) + super + _apipie_concern_data.each do |method_name, _apipie_dsl_data| + # remove method description if exists and create new one + description = Apipie.define_method_description(controller, method_name, _apipie_dsl_data) + controller._apipie_define_validators(description) + end + end + + def _apipie_concern_data + @_apipie_concern_data ||= [] + end + + def apipie_concern? + true + end + + # create method api and redefine newly added method + def method_added(method_name) #:doc: + super + + return if ! Apipie.active_dsl? || !_apipie_dsl_data[:api] + + _apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)] + ensure + _apipie_dsl_data_clear + end + + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/controller.rb b/lib/apipie/dsl/controller.rb new file mode 100644 index 00000000..a52384c5 --- /dev/null +++ b/lib/apipie/dsl/controller.rb @@ -0,0 +1,74 @@ +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Controller + include Apipie::DSL::Base + include Apipie::DSL::Common + include Apipie::DSL::Action + include Apipie::DSL::Param + + # defines the substitutions to be made in the API paths deifned + # in concerns included. For example: + # + # There is this method defined in concern: + # + # api GET ':controller_path/:id' + # def show + # # ... + # end + # + # If you include the concern into some controller, you can + # specify the value for :controller_path like this: + # + # apipie_concern_subst(:controller_path => '/users') + # include ::Concerns::SampleController + # + # The resulting path will be '/users/:id'. + # + # It has to be specified before the concern is included. + # + # If not specified, the default predefined substitions are + # + # {:conroller_path => controller.controller_path, + # :resource_id => `resource_id_from_apipie` } + def apipie_concern_subst(subst_hash) + _apipie_concern_subst.merge!(subst_hash) + end + + def _apipie_concern_subst + @_apipie_concern_subst ||= {:controller_path => self.controller_path, + :resource_id => Apipie.get_resource_name(self)} + end + + def _apipie_perform_concern_subst(string) + return _apipie_concern_subst.reduce(string) do |ret, (key, val)| + ret.gsub(":#{key}", val) + end + end + + def apipie_concern? + false + end + + # create method api and redefine newly added method + def method_added(method_name) #:doc: + super + return if !Apipie.active_dsl? || !_apipie_dsl_data[:api] + + return if _apipie_dsl_data[:api_args].blank? && _apipie_dsl_data[:api_from_routes].blank? + + # remove method description if exists and create new one + Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name) + description = Apipie.define_method_description(self, method_name, _apipie_dsl_data) + + _apipie_dsl_data_clear + _apipie_define_validators(description) + ensure + _apipie_dsl_data_clear + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/definition.rb b/lib/apipie/dsl/definition.rb new file mode 100644 index 00000000..484495ab --- /dev/null +++ b/lib/apipie/dsl/definition.rb @@ -0,0 +1,8 @@ +require "apipie/dsl/base" +require "apipie/dsl/action" +require "apipie/dsl/common" +require "apipie/dsl/param" +require "apipie/dsl/concern" + +require "apipie/dsl/resource" +require "apipie/dsl/controller" diff --git a/lib/apipie/dsl/param.rb b/lib/apipie/dsl/param.rb new file mode 100644 index 00000000..2bbcc64e --- /dev/null +++ b/lib/apipie/dsl/param.rb @@ -0,0 +1,59 @@ +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + # this describes the params, it's in separate module because it's + # used in Validators as well + module Param + # Describe method's parameter + # + # Example: + # param :greeting, String, :desc => "arbitrary text", :required => true + # def hello_world(greeting) + # puts greeting + # end + # + def param(param_name, validator, desc_or_options = nil, options = {}, &block) #:doc: + return unless Apipie.active_dsl? + _apipie_dsl_data[:params] << [param_name, + validator, + desc_or_options, + options.merge(:param_group => @_current_param_group), + block] + end + + # Reuses param group for this method. The definition is looked up + # in scope of this controller. If the group was defined in + # different controller, the second param can be used to specify it. + # when using action_aware parmas, you can specify :as => + # :create or :update to explicitly say how it should behave + def param_group(name, scope_or_options = nil, options = {}) + if scope_or_options.is_a? Hash + options.merge!(scope_or_options) + scope = options[:scope] + else + scope = scope_or_options + end + scope ||= _default_param_group_scope + + @_current_param_group = { + :scope => scope, + :name => name, + :options => options, + :from_concern => scope.apipie_concern? + } + self.instance_exec(&Apipie.get_param_group(scope, name)) + ensure + @_current_param_group = nil + end + + # where the group definition should be looked up when no scope + # given. This is expected to return a controller. + def _default_param_group_scope + self + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl/resource.rb b/lib/apipie/dsl/resource.rb new file mode 100644 index 00000000..714e1b54 --- /dev/null +++ b/lib/apipie/dsl/resource.rb @@ -0,0 +1,65 @@ +# Apipie DSL functions. +module Apipie + + # DSL is a module that provides #api, #error, #param, #error. + module DSL + + module Resource + + class Description + include Apipie::DSL::Base + include Apipie::DSL::Common + include Apipie::DSL::Resource + include Apipie::DSL::Param + + def initialize(controller) + @controller = controller + end + + def _eval_dsl(&block) + instance_eval(&block) + return _apipie_dsl_data + end + + # evaluates resource description DSL and returns results + def self.eval_dsl(controller, &block) + dsl_data = self.new(controller)._eval_dsl(&block) + if dsl_data[:api_versions].empty? + dsl_data[:api_versions] = Apipie.controller_versions(controller) + end + dsl_data + end + end + + # by default, the resource id is derived from controller_name + # it can be overwritten with. + # + # resource_id "my_own_resource_id" + def resource_id(resource_id) + Apipie.set_resource_id(@controller, resource_id) + end + + def name(name) + _apipie_dsl_data[:resource_name] = name + end + + def api_base_url(url) + _apipie_dsl_data[:api_base_url] = url + end + + def short(short) + _apipie_dsl_data[:short_description] = short + end + alias :short_description :short + + def path(path) + _apipie_dsl_data[:path] = path + end + + def app_info(app_info) + _apipie_dsl_data[:app_info] = app_info + end + end + + end # module DSL +end # module Apipie diff --git a/lib/apipie/dsl_definition.rb b/lib/apipie/dsl_definition.rb deleted file mode 100644 index 88a78c95..00000000 --- a/lib/apipie/dsl_definition.rb +++ /dev/null @@ -1,480 +0,0 @@ -# Apipie DSL functions. - -module Apipie - - # DSL is a module that provides #api, #error, #param, #error. - module DSL - - module Base - attr_reader :apipie_resource_descriptions, :api_params - - private - - def _apipie_dsl_data - @_apipie_dsl_data ||= _apipie_dsl_data_init - end - - def _apipie_dsl_data_clear - @_apipie_dsl_data = nil - end - - def _apipie_dsl_data_init - @_apipie_dsl_data = { - :api => false, - :api_args => [], - :api_from_routes => nil, - :errors => [], - :params => [], - :headers => [], - :resource_id => nil, - :short_description => nil, - :description => nil, - :examples => [], - :see => [], - :formats => nil, - :api_versions => [], - :meta => nil, - :show => true - } - end - end - - module Resource - # by default, the resource id is derived from controller_name - # it can be overwritten with. - # - # resource_id "my_own_resource_id" - def resource_id(resource_id) - Apipie.set_resource_id(@controller, resource_id) - end - - def name(name) - _apipie_dsl_data[:resource_name] = name - end - - def api_base_url(url) - _apipie_dsl_data[:api_base_url] = url - end - - def short(short) - _apipie_dsl_data[:short_description] = short - end - alias :short_description :short - - def path(path) - _apipie_dsl_data[:path] = path - end - - def app_info(app_info) - _apipie_dsl_data[:app_info] = app_info - end - end - - module Action - - def def_param_group(name, &block) - Apipie.add_param_group(self, name, &block) - end - - # - # # load paths from routes and don't provide description - # api - # - def api(method, path, desc = nil, options={}) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:api] = true - _apipie_dsl_data[:api_args] << [method, path, desc, options] - end - - # # load paths from routes - # api! "short description", - # - def api!(desc = nil, options={}) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:api] = true - _apipie_dsl_data[:api_from_routes] = { :desc => desc, :options =>options } - end - - # Reference other similar method - # - # api :PUT, '/articles/:id' - # see "articles#create" - # def update; end - def see(*args) - return unless Apipie.active_dsl? - _apipie_dsl_data[:see] << args - end - - # Show some example of what does the described - # method return. - def example(example) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:examples] << example.strip_heredoc - end - - # Determine if the method should be included - # in the documentation - def show(show) - return unless Apipie.active_dsl? - _apipie_dsl_data[:show] = show - end - - # Describe whole resource - # - # Example: - # api :desc => "Show user profile", :path => "/users/", :version => '1.0 - 3.4.2012' - # param :id, Fixnum, :desc => "User ID", :required => true - # desc <<-EOS - # Long description... - # EOS - def resource_description(options = {}, &block) #:doc: - return unless Apipie.active_dsl? - raise ArgumentError, "Block expected" unless block_given? - - dsl_data = ResourceDescriptionDsl.eval_dsl(self, &block) - versions = dsl_data[:api_versions] - @apipie_resource_descriptions = versions.map do |version| - Apipie.define_resource_description(self, version, dsl_data) - end - Apipie.set_controller_versions(self, versions) - end - end - - module Common - def api_versions(*versions) - _apipie_dsl_data[:api_versions].concat(versions) - end - alias :api_version :api_versions - - # Describe the next method. - # - # Example: - # desc "print hello world" - # def hello_world - # puts "hello world" - # end - # - def desc(description) #:doc: - return unless Apipie.active_dsl? - if _apipie_dsl_data[:description] - raise "Double method description." - end - _apipie_dsl_data[:description] = description - end - alias :description :desc - alias :full_description :desc - - # describe next method with document in given path - # in convension, these doc located under "#{Rails.root}/doc" - # Example: - # document "hello_world.md" - # def hello_world - # puts "hello world" - # end - def document path - content = File.open(File.join(Rails.root, Apipie.configuration.doc_path, path)).read - desc content - end - - # Describe available request/response formats - # - # formats ['json', 'jsonp', 'xml'] - def formats(formats) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:formats] = formats - end - - # Describe additional metadata - # - # meta :author => { :name => 'John', :surname => 'Doe' } - def meta(meta) #:doc: - _apipie_dsl_data[:meta] = meta - end - - - # Describe possible errors - # - # Example: - # error :desc => "speaker is sleeping", :code => 500, :meta => [:some, :more, :data] - # error 500, "speaker is sleeping" - # def hello_world - # return 500 if self.speaker.sleeping? - # puts "hello world" - # end - # - def error(code_or_options, desc=nil, options={}) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:errors] << [code_or_options, desc, options] - end - - def _apipie_define_validators(description) - - # [re]define method only if validation is turned on - if description && (Apipie.configuration.validate == true || - Apipie.configuration.validate == :implicitly || - Apipie.configuration.validate == :explicitly) - - _apipie_save_method_params(description.method, description.params) - - unless instance_methods.include?(:apipie_validations) - define_method(:apipie_validations) do - method_params = self.class._apipie_get_method_params(action_name) - - if Apipie.configuration.validate_presence? - method_params.each do |_, param| - # check if required parameters are present - raise ParamMissing.new(param) if param.required && !params.has_key?(param.name) - end - end - - if Apipie.configuration.validate_value? - method_params.each do |_, param| - # params validations - param.validate(params[:"#{param.name}"]) if params.has_key?(param.name) - end - end - - # Only allow params passed in that are defined keys in the api - # Auto skip the default params (format, controller, action) - if Apipie.configuration.validate_key? - params.reject{|k,_| %w[format controller action].include?(k.to_s) }.each_key do |param| - # params allowed - raise UnknownParam.new(param) if method_params.select {|_,p| p.name.to_s == param.to_s}.empty? - end - end - - if Apipie.configuration.process_value? - @api_params ||= {} - method_params.each do |_, param| - # params processing - @api_params[param.as] = param.process_value(params[:"#{param.name}"]) if params.has_key?(param.name) - end - end - end - end - - if (Apipie.configuration.validate == :implicitly || Apipie.configuration.validate == true) - old_method = instance_method(description.method) - - define_method(description.method) do |*args| - apipie_validations - - # run the original method code - old_method.bind(self).call(*args) - end - end - - end - end - - def _apipie_save_method_params(method, params) - @method_params ||= {} - @method_params[method] = params - end - - def _apipie_get_method_params(method) - @method_params[method] - end - - # Describe request header. - # Headers can't be validated with config.validate_presence = true - # - # Example: - # header 'ClientId', "client-id" - # def show - # render :text => headers['HTTP_CLIENT_ID'] - # end - # - def header(header_name, description, options = {}) #:doc - return unless Apipie.active_dsl? - _apipie_dsl_data[:headers] << { - name: header_name, - description: description, - options: options - } - end - end - - # this describes the params, it's in separate module because it's - # used in Validators as well - module Param - # Describe method's parameter - # - # Example: - # param :greeting, String, :desc => "arbitrary text", :required => true - # def hello_world(greeting) - # puts greeting - # end - # - def param(param_name, validator, desc_or_options = nil, options = {}, &block) #:doc: - return unless Apipie.active_dsl? - _apipie_dsl_data[:params] << [param_name, - validator, - desc_or_options, - options.merge(:param_group => @_current_param_group), - block] - end - - # Reuses param group for this method. The definition is looked up - # in scope of this controller. If the group was defined in - # different controller, the second param can be used to specify it. - # when using action_aware parmas, you can specify :as => - # :create or :update to explicitly say how it should behave - def param_group(name, scope_or_options = nil, options = {}) - if scope_or_options.is_a? Hash - options.merge!(scope_or_options) - scope = options[:scope] - else - scope = scope_or_options - end - scope ||= _default_param_group_scope - - @_current_param_group = { - :scope => scope, - :name => name, - :options => options, - :from_concern => scope.apipie_concern? - } - self.instance_exec(&Apipie.get_param_group(scope, name)) - ensure - @_current_param_group = nil - end - - # where the group definition should be looked up when no scope - # given. This is expected to return a controller. - def _default_param_group_scope - self - end - end - - module Controller - include Apipie::DSL::Base - include Apipie::DSL::Common - include Apipie::DSL::Action - include Apipie::DSL::Param - - # defines the substitutions to be made in the API paths deifned - # in concerns included. For example: - # - # There is this method defined in concern: - # - # api GET ':controller_path/:id' - # def show - # # ... - # end - # - # If you include the concern into some controller, you can - # specify the value for :controller_path like this: - # - # apipie_concern_subst(:controller_path => '/users') - # include ::Concerns::SampleController - # - # The resulting path will be '/users/:id'. - # - # It has to be specified before the concern is included. - # - # If not specified, the default predefined substitions are - # - # {:conroller_path => controller.controller_path, - # :resource_id => `resource_id_from_apipie` } - def apipie_concern_subst(subst_hash) - _apipie_concern_subst.merge!(subst_hash) - end - - def _apipie_concern_subst - @_apipie_concern_subst ||= {:controller_path => self.controller_path, - :resource_id => Apipie.get_resource_name(self)} - end - - def _apipie_perform_concern_subst(string) - return _apipie_concern_subst.reduce(string) do |ret, (key, val)| - ret.gsub(":#{key}", val) - end - end - - def apipie_concern? - false - end - - # create method api and redefine newly added method - def method_added(method_name) #:doc: - super - return if !Apipie.active_dsl? || !_apipie_dsl_data[:api] - - return if _apipie_dsl_data[:api_args].blank? && _apipie_dsl_data[:api_from_routes].blank? - - # remove method description if exists and create new one - Apipie.remove_method_description(self, _apipie_dsl_data[:api_versions], method_name) - description = Apipie.define_method_description(self, method_name, _apipie_dsl_data) - - _apipie_dsl_data_clear - _apipie_define_validators(description) - ensure - _apipie_dsl_data_clear - end - end - - module Concern - include Apipie::DSL::Base - include Apipie::DSL::Common - include Apipie::DSL::Action - include Apipie::DSL::Param - - # the concern was included into a controller - def included(controller) - super - _apipie_concern_data.each do |method_name, _apipie_dsl_data| - # remove method description if exists and create new one - description = Apipie.define_method_description(controller, method_name, _apipie_dsl_data) - controller._apipie_define_validators(description) - end - end - - def _apipie_concern_data - @_apipie_concern_data ||= [] - end - - def apipie_concern? - true - end - - # create method api and redefine newly added method - def method_added(method_name) #:doc: - super - - return if ! Apipie.active_dsl? || !_apipie_dsl_data[:api] - - _apipie_concern_data << [method_name, _apipie_dsl_data.merge(:from_concern => true)] - ensure - _apipie_dsl_data_clear - end - - end - - class ResourceDescriptionDsl - include Apipie::DSL::Base - include Apipie::DSL::Common - include Apipie::DSL::Resource - include Apipie::DSL::Param - - def initialize(controller) - @controller = controller - end - - def _eval_dsl(&block) - instance_eval(&block) - return _apipie_dsl_data - end - - # evaluates resource description DSL and returns results - def self.eval_dsl(controller, &block) - dsl_data = self.new(controller)._eval_dsl(&block) - if dsl_data[:api_versions].empty? - dsl_data[:api_versions] = Apipie.controller_versions(controller) - end - dsl_data - end - end - - end # module DSL -end # module Apipie diff --git a/lib/apipie/error_description.rb b/lib/apipie/error_description.rb deleted file mode 100644 index 518d2d32..00000000 --- a/lib/apipie/error_description.rb +++ /dev/null @@ -1,37 +0,0 @@ -module Apipie - - class ErrorDescription - - attr_reader :code, :description, :metadata - - def self.from_dsl_data(args) - code_or_options, desc, options = args - Apipie::ErrorDescription.new(code_or_options, - desc, - options) - end - - def initialize(code_or_options, desc=nil, options={}) - if code_or_options.is_a? Hash - code_or_options.symbolize_keys! - @code = code_or_options[:code] - @metadata = code_or_options[:meta] - @description = code_or_options[:desc] || code_or_options[:description] - else - @code = code_or_options - @metadata = options[:meta] - @description = desc - end - end - - def to_json - { - :code => code, - :description => description, - :metadata => metadata - } - end - - end - -end diff --git a/lib/apipie/markup.rb b/lib/apipie/markup.rb index aa32b543..c239a1a0 100644 --- a/lib/apipie/markup.rb +++ b/lib/apipie/markup.rb @@ -27,7 +27,8 @@ def initialize end def to_html(text) - Maruku.new(text).to_html + # Maruku.new(text).to_html + text end end diff --git a/lib/apipie/method_description.rb b/lib/apipie/method_description.rb index 43946045..83298691 100644 --- a/lib/apipie/method_description.rb +++ b/lib/apipie/method_description.rb @@ -30,8 +30,8 @@ def initialize(method, resource, dsl_data) desc = dsl_data[:description] || '' @full_description = Apipie.markup_to_html(desc) - @errors = dsl_data[:errors].map do |args| - Apipie::ErrorDescription.from_dsl_data(args) + @responses = dsl_data[:responses].map do |args| + Apipie::ResponseDescription.from_dsl_data(args) end @see = dsl_data[:see].map do |args| @@ -81,21 +81,21 @@ def params_ordered all_params.find_all(&:validator) end - def errors - return @merged_errors if @merged_errors - @merged_errors = [] + def responses + return @merged_responses if @merged_responses + @merged_responses = [] if @resource - resource_errors = @resource._errors_args.map do |args| - Apipie::ErrorDescription.from_dsl_data(args) + resource_responses = @resource._responses_args.map do |args| + Apipie::ResponseDescription.from_dsl_data(args) end - # exclude overwritten parent errors - @merged_errors = resource_errors.find_all do |err| - !@errors.any? { |e| e.code == err.code } + # exclude overwritten parent responses + @merged_responses = resource_responses.find_all do |response| + !@responses.any? { |r| r.code == response.code } end end - @merged_errors.concat(@errors) - return @merged_errors + @merged_responses.concat(@responses) + return @merged_responses end def version @@ -145,7 +145,7 @@ def to_json(lang=nil) :apis => method_apis_to_json(lang), :formats => formats, :full_description => Apipie.app.translate(@full_description, lang), - :errors => errors.map(&:to_json), + :responses => responses.map(&:to_json), :params => params_ordered.map{ |param| param.to_json(lang) }.flatten, :examples => @examples, :metadata => @metadata, diff --git a/lib/apipie/param_description.rb b/lib/apipie/param_description.rb index 6c3780ed..72c5f8a7 100644 --- a/lib/apipie/param_description.rb +++ b/lib/apipie/param_description.rb @@ -8,7 +8,7 @@ module Apipie # validator - Validator::BaseValidator subclass class ParamDescription - attr_reader :method_description, :name, :desc, :allow_nil, :allow_blank, :validator, :options, :metadata, :show, :as, :validations + attr_reader :method_description, :name, :desc, :allowed, :allow_nil, :allow_blank, :validator, :options, :metadata, :show, :as, :validations attr_accessor :parent, :required def self.from_dsl_data(method_description, args) @@ -44,6 +44,8 @@ def initialize(method_description, name, validator, desc_or_options = nil, optio @as = options[:as] || @name @desc = preformat_text(@options[:desc]) + @allowed = Array === validator ? @options[:allowed] : [] + @parent = @options[:parent] @metadata = @options[:meta] @@ -126,6 +128,7 @@ def to_json(lang = nil) :required => required, :allow_nil => allow_nil, :allow_blank => allow_blank, + :allowed => allowed, :validator => validator.to_s, :expected_type => validator.expected_type, :metadata => metadata, diff --git a/lib/apipie/render.rb b/lib/apipie/render.rb new file mode 100644 index 00000000..1a72e18b --- /dev/null +++ b/lib/apipie/render.rb @@ -0,0 +1,59 @@ +module Apipie + module Render + # Attempt to use the Rails application views, otherwise default to built in views + def self.renderer(formats = nil) + return @apipie_renderer if @apipie_renderer + + @apipie_renderer = ActionView::Base.new([base_path, layouts_path], {}, nil, formats) + @apipie_renderer.singleton_class.send(:include, ApipieHelper) + return @apipie_renderer + end + + def self.page(file_name, template, variables, layout = 'apipie', formats = nil) + av = renderer(formats) + File.open(file_name, "w") do |f| + variables.each do |var, val| + av.instance_variable_set("@#{var}", val) + end + f.write av.render( + :template => "#{template}", + :layout => (layout && "apipie/#{layout}"), + :formats => formats) + end + end + + def self.with_loaded_documentation + Apipie.configuration.use_cache = false # we don't want to skip DSL evaluation + Apipie.reload_documentation + yield + end + + def self.copy_jscss(dest) + src = File.expand_path(Apipie.root.join('app', 'public', 'apipie')) + FileUtils.mkdir_p dest + FileUtils.cp_r "#{src}/.", dest + end + + def self.lang_ext(lang = nil) + lang ? ".#{lang}" : "" + end + + private + + def self.base_path + if File.directory?("#{Rails.root}/app/views/apipie/apipies") + "#{Rails.root}/app/views/apipie/apipies" + else + File.expand_path(Apipie.root.join('app', 'views', 'apipie', 'apipies')) + end + end + + def self.layouts_path + if File.directory?("#{Rails.root}/app/views/layouts/apipie") + "#{Rails.root}/app/views/layouts" + else + File.expand_path(Apipie.root.join('app', 'views', 'layouts')) + end + end + end +end diff --git a/lib/apipie/render/html.rb b/lib/apipie/render/html.rb new file mode 100644 index 00000000..75e50aee --- /dev/null +++ b/lib/apipie/render/html.rb @@ -0,0 +1,72 @@ +require 'apipie/render' + +module Apipie + module Render + module HTML + + def self.one_page(file_base, doc, lang = nil) + FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) + + Render.page("#{file_base}-onepage#{Render.lang_ext(lang)}.html", "static", {:doc => doc[:docs], + :language => lang, :languages => Apipie.configuration.languages}) + end + + def self.plain_page(file_base, doc, lang = nil) + FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) + + Render.page("#{file_base}-plain#{Render.lang_ext(lang)}.html", "plain", {:doc => doc[:docs], + :language => lang, :languages => Apipie.configuration.languages}, nil) + end + + def self.index_page(file_base, doc, include_json = false, show_versions = false, lang = nil) + FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) + versions = show_versions && Apipie.available_versions + Render.page("#{file_base}#{Render.lang_ext(lang)}.html", "index", {:doc => doc[:docs], + :versions => versions, :language => lang, :languages => Apipie.configuration.languages}) + + File.open("#{file_base}#{Render.lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json + end + + def self.resource_pages(version, file_base, doc, include_json = false, lang = nil) + doc[:docs][:resources].each do |resource_name, _| + resource_file_base = File.join(file_base, resource_name.to_s) + FileUtils.mkdir_p(File.dirname(resource_file_base)) unless File.exists?(File.dirname(resource_file_base)) + + doc = Apipie.to_json(version, resource_name, nil, lang) + doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") + Render.page("#{resource_file_base}#{Render.lang_ext(lang)}.html", "resource", {:doc => doc[:docs], + :resource => doc[:docs][:resources].first, :language => lang, :languages => Apipie.configuration.languages}) + File.open("#{resource_file_base}#{Render.lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json + end + end + + def self.method_pages(version, file_base, doc, include_json = false, lang = nil) + doc[:docs][:resources].each do |resource_name, resource_params| + resource_params[:methods].each do |method| + method_file_base = File.join(file_base, resource_name.to_s, method[:name].to_s) + FileUtils.mkdir_p(File.dirname(method_file_base)) unless File.exists?(File.dirname(method_file_base)) + + doc = Apipie.to_json(version, resource_name, method[:name], lang) + doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") + Render.page("#{method_file_base}#{Render.lang_ext(lang)}.html", "method", { + :doc => doc[:docs], + :resource => doc[:docs][:resources].first, + :method => doc[:docs][:resources].first[:methods].first, + :language => lang, + :languages => Apipie.configuration.languages + }) + + File.open("#{method_file_base}#{Render.lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json + end + end + end + + def self.copy_jscss(dest) + src = File.expand_path(Apipie.root.join('app', 'public', 'apipie')) + FileUtils.mkdir_p dest + FileUtils.cp_r "#{src}/.", dest + end + + end + end +end diff --git a/lib/apipie/render/json.rb b/lib/apipie/render/json.rb new file mode 100644 index 00000000..9f1b0477 --- /dev/null +++ b/lib/apipie/render/json.rb @@ -0,0 +1,16 @@ +require 'apipie/render' + +module Apipie + module Render + module JSON + + def self.one_page(file_base, doc, lang = nil) + FileUtils.mkdir_p(file_base) unless File.exists?(file_base) + + filename = "schema_apipie#{Render.lang_ext(lang)}.json" + File.open("#{file_base}/#{filename}", 'w') { |file| file.write(::JSON.pretty_generate(doc)) } + end + + end + end +end diff --git a/lib/apipie/render/markdown.rb b/lib/apipie/render/markdown.rb new file mode 100644 index 00000000..e75ef27f --- /dev/null +++ b/lib/apipie/render/markdown.rb @@ -0,0 +1,55 @@ +require 'apipie/render' + +module Apipie + module Render + module Markdown + + def self.one_page(file_base, doc, lang = nil) + FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) + + Render.page("#{file_base}-onepage#{Render.lang_ext(lang)}.md", "static", { + :doc => doc[:docs], + :language => lang, + :languages => Apipie.configuration.languages + }, 'apipie', [:md]) + end + + def self.resource_pages(version, file_base, doc, include_json = false, lang = nil) + doc[:docs][:resources].each do |resource_name, _| + resource_file_base = File.join(file_base, resource_name.to_s) + FileUtils.mkdir_p(File.dirname(resource_file_base)) unless File.exists?(File.dirname(resource_file_base)) + + doc = Apipie.to_json(version, resource_name, nil, lang) + doc[:docs][:link_extension] = (lang ? ".#{lang}.md" : ".md") + + Render.page("#{resource_file_base}#{Render.lang_ext(lang)}.md", "resource", { + :doc => doc[:docs], + :resource => doc[:docs][:resources].first, + :language => lang, + :languages => Apipie.configuration.languages + }, 'apipie', [:md]) + end + end + + def self.method_pages(version, file_base, doc, include_json = false, lang = nil) + doc[:docs][:resources].each do |resource_name, resource_params| + resource_params[:methods].each do |method| + method_file_base = File.join(file_base, resource_name.to_s, method[:name].to_s) + FileUtils.mkdir_p(File.dirname(method_file_base)) unless File.exists?(File.dirname(method_file_base)) + + doc = Apipie.to_json(version, resource_name, method[:name], lang) + doc[:docs][:link_extension] = (lang ? ".#{lang}.md" : ".md") + Render.page("#{method_file_base}#{Render.lang_ext(lang)}.md", "method", { + :doc => doc[:docs], + :resource => doc[:docs][:resources].first, + :method => doc[:docs][:resources].first[:methods].first, + :language => lang, + :languages => Apipie.configuration.languages + }, 'apipie') + end + end + end + + end + end +end diff --git a/lib/apipie/resource_description.rb b/lib/apipie/resource_description.rb index 083d6c25..3b28d8c0 100644 --- a/lib/apipie/resource_description.rb +++ b/lib/apipie/resource_description.rb @@ -13,14 +13,14 @@ module Apipie class ResourceDescription attr_reader :controller, :_short_description, :_full_description, :_methods, :_id, - :_path, :_name, :_params_args, :_errors_args, :_formats, :_parent, :_metadata, + :_path, :_name, :_params_args, :_responses_args, :_formats, :_parent, :_metadata, :_headers def initialize(controller, resource_name, dsl_data = nil, version = nil, &block) @_methods = ActiveSupport::OrderedHash.new @_params_args = [] - @_errors_args = [] + @_responses_args = [] @controller = controller @_id = resource_name @@ -37,7 +37,7 @@ def update_from_dsl_data(dsl_data) @_short_description = dsl_data[:short_description] @_path = dsl_data[:path] || "" @_formats = dsl_data[:formats] - @_errors_args = dsl_data[:errors] + @_responses_args = dsl_data[:responses] @_params_args = dsl_data[:params] @_metadata = dsl_data[:meta] @_api_base_url = dsl_data[:api_base_url] @@ -82,7 +82,12 @@ def doc_url Apipie.full_url crumbs.join('/') end - def api_url; "#{Apipie.api_base_url(_version)}#{@_path}"; end + def api_url + crumbs = [] + crumbs << @_api_base_url + crumbs << @_id + crumbs.join('/') + end def valid_method_name?(method_name) @_methods.keys.map(&:to_s).include?(method_name.to_s) diff --git a/lib/apipie/response_description.rb b/lib/apipie/response_description.rb new file mode 100644 index 00000000..5e36e13b --- /dev/null +++ b/lib/apipie/response_description.rb @@ -0,0 +1,37 @@ +module Apipie + + class ResponseDescription + + attr_reader :code, :body, :metadata, :headers + + def self.from_dsl_data(args) + code_or_options, desc, options = args + Apipie::ResponseDescription.new(code_or_options, + desc, + options) + end + + def initialize(code, body=nil, options={}) + if !code.is_a? Numeric + warn "First argument must be a response code (Integer)" + else + options.symbolize_keys! + @headers = options[:headers] + @body = body + @code = code + @metadata = options[:meta] + end + end + + def to_json + { + :code => code, + :headers => headers, + :body => body, + :metadata => metadata + } + end + + end + +end diff --git a/lib/apipie/validator.rb b/lib/apipie/validator.rb index b4973a60..85df859e 100644 --- a/lib/apipie/validator.rb +++ b/lib/apipie/validator.rb @@ -101,11 +101,11 @@ def description def expected_type if @type.ancestors.include? Hash - 'hash' + 'object' elsif @type.ancestors.include? Array 'array' elsif @type.ancestors.include? Numeric - 'numeric' + 'number' else 'string' end @@ -331,7 +331,7 @@ def description end def expected_type - 'hash' + 'object' end # where the group definition should be looked up when no scope diff --git a/lib/boolean.rb b/lib/boolean.rb new file mode 100644 index 00000000..ea9b4523 --- /dev/null +++ b/lib/boolean.rb @@ -0,0 +1,39 @@ +class Boolean + + def self.new(bool) + bool + end + + def self.true + true + end + + def self.false + false + end + +end + +class FalseClass + + def is_a?(other) + other == Boolean || super + end + + def self.===(other) + other == Boolean || super + end + +end + +class TrueClass + + def is_a?(other) + other == Boolean || super + end + + def self.===(other) + other == Boolean || super + end + +end diff --git a/lib/tasks/apipie.rake b/lib/tasks/apipie.rake index 71ffe8bb..41929490 100644 --- a/lib/tasks/apipie.rake +++ b/lib/tasks/apipie.rake @@ -1,56 +1,9 @@ # -*- coding: utf-8 -*- require 'fileutils' +require 'apipie/render/html' namespace :apipie do - desc "Generate static documentation" - # You can specify OUT=output_base_file to have the following structure: - # - # output_base_file.html - # output_base_file-onepage.html - # output_base_file - # | - resource1.html - # | - resource1 - # | - | - method1.html - # | - | - method2.html - # | - resource2.html - # - # By default OUT="#{Rails.root}/doc/apidoc" - task :static, [:version] => :environment do |t, args| - with_loaded_documentation do - args.with_defaults(:version => Apipie.configuration.default_version) - out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc') - subdir = File.basename(out) - copy_jscss(out) - Apipie.configuration.version_in_url = false - ([nil] + Apipie.configuration.languages).each do |lang| - I18n.locale = lang || Apipie.configuration.default_locale - Apipie.url_prefix = "./#{subdir}" - doc = Apipie.to_json(args[:version], nil, nil, lang) - doc[:docs][:link_extension] = "#{lang_ext(lang)}.html" - generate_one_page(out, doc, lang) - generate_plain_page(out, doc, lang) - generate_index_page(out, doc, false, false, lang) - Apipie.url_prefix = "../#{subdir}" - generate_resource_pages(args[:version], out, doc, false, lang) - Apipie.url_prefix = "../../#{subdir}" - generate_method_pages(args[:version], out, doc, false, lang) - end - end - end - - desc "Generate static documentation json" - task :static_json, [:version] => :environment do |t, args| - with_loaded_documentation do - args.with_defaults(:version => Apipie.configuration.default_version) - out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc') - ([nil] + Apipie.configuration.languages).each do |lang| - doc = Apipie.to_json(args[:version], nil, nil, lang) - generate_json_page(out, doc, lang) - end - end - end - # By default the full cache is built. # It is possible to generate index resp. resources only with # rake apipie:cache cache_part=index (resources resp.) @@ -61,7 +14,7 @@ namespace :apipie do cache_part = ENV['cache_part'] generate_index = (cache_part == 'resources' ? false : true) generate_resources = (cache_part == 'index' ? false : true) - with_loaded_documentation do + Apipie::Render.with_loaded_documentation do puts "#{Time.now} | Documents loaded..." ([nil] + Apipie.configuration.languages).each do |lang| I18n.locale = lang || Apipie.configuration.default_locale @@ -76,7 +29,7 @@ namespace :apipie do Apipie.url_prefix = "./#{subdir}" doc = Apipie.to_json(Apipie.configuration.default_version, nil, nil, lang) doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") - generate_index_page(file_base, doc, true, false, lang) + Apipie::Render::HTML.index_page(file_base, doc, true, false, lang) end Apipie.available_versions.each do |version| file_base_version = File.join(file_base, version) @@ -84,12 +37,12 @@ namespace :apipie do doc = Apipie.to_json(version, nil, nil, lang) doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") - generate_index_page(file_base_version, doc, true, true, lang) if generate_index + Apipie::Render::HTML.index_page(file_base_version, doc, true, true, lang) if generate_index if generate_resources Apipie.url_prefix = "../#{subdir_traversal_prefix}#{subdir}" - generate_resource_pages(version, file_base_version, doc, true, lang) + Apipie::Render::HTML.resource_pages(version, file_base_version, doc, true, lang) Apipie.url_prefix = "../../#{subdir_traversal_prefix}#{subdir}" - generate_method_pages(version, file_base_version, doc, true, lang) + Apipie::Render::HTML.method_pages(version, file_base_version, doc, true, lang) end end end @@ -97,158 +50,4 @@ namespace :apipie do puts "#{Time.now} | Finished" end - # Attempt to use the Rails application views, otherwise default to built in views - def renderer - return @apipie_renderer if @apipie_renderer - base_path = if File.directory?("#{Rails.root}/app/views/apipie/apipies") - "#{Rails.root}/app/views/apipie/apipies" - else - File.expand_path("../../../app/views/apipie/apipies", __FILE__) - end - layouts_path = if File.directory?("#{Rails.root}/app/views/layouts/apipie") - "#{Rails.root}/app/views/layouts" - else - File.expand_path("../../../app/views/layouts", __FILE__) - end - @apipie_renderer = ActionView::Base.new([base_path, layouts_path]) - @apipie_renderer.singleton_class.send(:include, ApipieHelper) - return @apipie_renderer - end - - def render_page(file_name, template, variables, layout = 'apipie') - av = renderer - File.open(file_name, "w") do |f| - variables.each do |var, val| - av.instance_variable_set("@#{var}", val) - end - f.write av.render( - :template => "#{template}", - :layout => (layout && "apipie/#{layout}")) - end - end - - def generate_json_page(file_base, doc, lang = nil) - FileUtils.mkdir_p(file_base) unless File.exists?(file_base) - - filename = "schema_apipie#{lang_ext(lang)}.json" - File.open("#{file_base}/#{filename}", 'w') { |file| file.write(JSON.pretty_generate(doc)) } - end - - def generate_one_page(file_base, doc, lang = nil) - FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) - - render_page("#{file_base}-onepage#{lang_ext(lang)}.html", "static", {:doc => doc[:docs], - :language => lang, :languages => Apipie.configuration.languages}) - end - - def generate_plain_page(file_base, doc, lang = nil) - FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) - - render_page("#{file_base}-plain#{lang_ext(lang)}.html", "plain", {:doc => doc[:docs], - :language => lang, :languages => Apipie.configuration.languages}, nil) - end - - def generate_index_page(file_base, doc, include_json = false, show_versions = false, lang = nil) - FileUtils.mkdir_p(File.dirname(file_base)) unless File.exists?(File.dirname(file_base)) - versions = show_versions && Apipie.available_versions - render_page("#{file_base}#{lang_ext(lang)}.html", "index", {:doc => doc[:docs], - :versions => versions, :language => lang, :languages => Apipie.configuration.languages}) - - File.open("#{file_base}#{lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json - end - - def generate_resource_pages(version, file_base, doc, include_json = false, lang = nil) - doc[:docs][:resources].each do |resource_name, _| - resource_file_base = File.join(file_base, resource_name.to_s) - FileUtils.mkdir_p(File.dirname(resource_file_base)) unless File.exists?(File.dirname(resource_file_base)) - - doc = Apipie.to_json(version, resource_name, nil, lang) - doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") - render_page("#{resource_file_base}#{lang_ext(lang)}.html", "resource", {:doc => doc[:docs], - :resource => doc[:docs][:resources].first, :language => lang, :languages => Apipie.configuration.languages}) - File.open("#{resource_file_base}#{lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json - end - end - - def generate_method_pages(version, file_base, doc, include_json = false, lang = nil) - doc[:docs][:resources].each do |resource_name, resource_params| - resource_params[:methods].each do |method| - method_file_base = File.join(file_base, resource_name.to_s, method[:name].to_s) - FileUtils.mkdir_p(File.dirname(method_file_base)) unless File.exists?(File.dirname(method_file_base)) - - doc = Apipie.to_json(version, resource_name, method[:name], lang) - doc[:docs][:link_extension] = (lang ? ".#{lang}.html" : ".html") - render_page("#{method_file_base}#{lang_ext(lang)}.html", "method", {:doc => doc[:docs], - :resource => doc[:docs][:resources].first, - :method => doc[:docs][:resources].first[:methods].first, - :language => lang, - :languages => Apipie.configuration.languages}) - - File.open("#{method_file_base}#{lang_ext(lang)}.json", "w") { |f| f << doc.to_json } if include_json - end - end - end - - def with_loaded_documentation - Apipie.configuration.use_cache = false # we don't want to skip DSL evaluation - Apipie.reload_documentation - yield - end - - - def copy_jscss(dest) - src = File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'app', 'public', 'apipie')) - FileUtils.mkdir_p dest - FileUtils.cp_r "#{src}/.", dest - end - - def lang_ext(lang = nil) - lang ? ".#{lang}" : "" - end - - desc "Generate CLI client for API documented with apipie gem. (deprecated)" - task :client do - puts <<MESSAGE -The apipie gem itself doesn't provide client code generator. See -https://github.com/Pajk/apipie-rails/wiki/CLI-client for more information on -how to write your own generator. -MESSAGE - end - - def plaintext(text) - text.gsub(/<.*?>/, '').gsub("\n",' ').strip - end - - desc "Update api description in controllers base on routes" - task :update_from_routes => [:environment] do - Apipie.configuration.force_dsl = true - ignored = Apipie.configuration.ignored_by_recorder - with_loaded_documentation do - apis_from_routes = Apipie::Extractor.apis_from_routes - apis_from_routes.each do |(controller, action), apis| - next if ignored.include?(controller) - next if ignored.include?("#{controller}##{action}") - Apipie::Extractor::Writer.update_action_description(controller.constantize, action) do |u| - u.update_apis(apis) - end - end - end - end - - desc "Convert your examples from the old yaml into the new json format" - task :convert_examples => :environment do - yaml_examples_file = File.join(Rails.root, Apipie.configuration.doc_path, "apipie_examples.yml") - if File.exists?(yaml_examples_file) - #if SafeYAML gem is enabled, it will load examples as an array of Hash, instead of hash - if defined? SafeYAML - examples = YAML.load_file(yaml_examples_file, :safe=>false) - else - examples = YAML.load_file(yaml_examples_file) - end - else - examples = {} - end - Apipie::Extractor::Writer.write_recorded_examples(examples) - end - end diff --git a/lib/tasks/renderers/html.rake b/lib/tasks/renderers/html.rake new file mode 100644 index 00000000..781c1549 --- /dev/null +++ b/lib/tasks/renderers/html.rake @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +require 'fileutils' +require 'apipie/render/html' + +namespace :apipie do + namespace :render do + + desc "Generate static documentation" + # You can specify OUT=output_base_file to have the following structure: + # + # output_base_file.html + # output_base_file-onepage.html + # output_base_file + # | - resource1.html + # | - resource1 + # | - | - method1.html + # | - | - method2.html + # | - resource2.html + # + # By default OUT="#{Rails.root}/doc/apidoc" + task :html, [:version] => :environment do |t, args| + Apipie::Render.with_loaded_documentation do + args.with_defaults(:version => Apipie.configuration.default_version) + out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc') + subdir = File.basename(out) + Apipie::Render::HTML.copy_jscss(out) + Apipie.configuration.version_in_url = false + ([nil] + Apipie.configuration.languages).each do |lang| + I18n.locale = lang || Apipie.configuration.default_locale + Apipie.url_prefix = "./#{subdir}" + doc = Apipie.to_json(args[:version], nil, nil, lang) + doc[:docs][:link_extension] = "#{Apipie::Render.lang_ext(lang)}.html" + Apipie::Render::HTML.one_page(out, doc, lang) + Apipie::Render::HTML.plain_page(out, doc, lang) + Apipie::Render::HTML.index_page(out, doc, false, false, lang) + Apipie.url_prefix = "../#{subdir}" + Apipie::Render::HTML.resource_pages(args[:version], out, doc, false, lang) + Apipie.url_prefix = "../../#{subdir}" + Apipie::Render::HTML.method_pages(args[:version], out, doc, false, lang) + end + end + end + + end + +end diff --git a/lib/tasks/renderers/json.rake b/lib/tasks/renderers/json.rake new file mode 100644 index 00000000..8b156dcf --- /dev/null +++ b/lib/tasks/renderers/json.rake @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +require 'fileutils' +require 'apipie/render/json' + +namespace :apipie do + + namespace :render do + + desc "Generate static documentation json" + task :json, [:version] => :environment do |t, args| + Apipie::Render.with_loaded_documentation do + args.with_defaults(:version => Apipie.configuration.default_version) + out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc') + ([nil] + Apipie.configuration.languages).each do |lang| + doc = Apipie.to_json(args[:version], nil, nil, lang) + Apipie::Render::JSON.one_page(out, doc, lang) + end + end + end + + end + +end diff --git a/lib/tasks/renderers/markdown.rake b/lib/tasks/renderers/markdown.rake new file mode 100644 index 00000000..244b42f4 --- /dev/null +++ b/lib/tasks/renderers/markdown.rake @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +require 'fileutils' +require 'apipie/render/markdown' + +namespace :apipie do + + namespace :render do + desc "Generate static markdown documentation" + task :markdown, [:version] => :environment do |t, args| + Apipie::Render.with_loaded_documentation do + args.with_defaults(:version => Apipie.configuration.default_version) + out = ENV["OUT"] || File.join(::Rails.root, Apipie.configuration.doc_path, 'apidoc') + subdir = File.basename(out) + Apipie.configuration.version_in_url = false + ([nil] + Apipie.configuration.languages).each do |lang| + I18n.locale = lang || Apipie.configuration.default_locale + Apipie.url_prefix = "./#{subdir}" + doc = Apipie.to_json(args[:version], nil, nil, lang) + doc[:docs][:link_extension] = "#{Apipie::Render.lang_ext(lang)}.md" + Apipie::Render::Markdown.one_page(out, doc, lang) + end + end + end + + end + +end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 239a278d..8d83bb48 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -243,9 +243,10 @@ def reload_controllers :required => false, :allow_nil => true, :allow_blank => false, + :allowed => [], :metadata => nil, :show => true, - :expected_type => "hash", + :expected_type => 'object', :validations => []) end @@ -439,12 +440,12 @@ def reload_controllers it "should contain possible errors description" do a = Apipie.get_method_description(UsersController, :show) - expect(a.errors[0].code).to eq(500) - expect(a.errors[0].description).to include("crashed") - expect(a.errors[1].code).to eq(401) - expect(a.errors[1].description).to eq("Unauthorized") - expect(a.errors[2].code).to eq(404) - expect(a.errors[2].description).to eq("Not Found") + expect(a.responses[0].code).to eq(500) + expect(a.responses[0].body).to include("crashed") + expect(a.responses[1].code).to eq(401) + expect(a.responses[1].body).to eq("Unauthorized") + expect(a.responses[2].code).to eq(404) + expect(a.responses[2].body).to eq("Not Found") end it "should contain all params description" do @@ -521,80 +522,109 @@ def reload_controllers it "should be described by valid json" do json = Apipie[UsersController, :two_urls].to_json - expected_hash = { - :errors => [{:code=>404, :description=>"Missing", :metadata => {:some => "metadata"}}, - {:code=>500, :description=>"Server crashed for some <%= reason %>"}], - :examples => [], - :doc_url => "#{Apipie.configuration.doc_base_url}/development/users/two_urls", - :formats=>["json"], - :full_description => '', - :params => [{:full_name=>"oauth", - :required=>false, - :allow_nil => false, - :allow_blank => false, - :validator=>"Must be a String", - :description=>"\n<p>Authorization</p>\n", - :name=>"oauth", - :show=>true, - :expected_type=>"string"}, - {:validator=>"Must be a Hash", - :description=>"\n<p>Deprecated parameter not documented</p>\n", - :expected_type=>"hash", - :allow_nil=>false, - :allow_blank => false, - :name=>"legacy_param", - :required=>false, - :full_name=>"legacy_param", - :show=>false, - :params=> - [{:validator=>"Must be a Hash", - :description=>"\n<p>Param description for all methods</p>\n", - :expected_type=>"hash", - :allow_nil=>false, - :allow_blank => false, - :name=>"resource_param", - :required=>false, - :full_name=>"resource_param", - :show=>true, - :params=> - [{:required=>true, - :allow_nil => false, - :allow_blank => false, - :validator=>"Must be a String", - :description=>"\n<p>Username for login</p>\n", - :name=>"ausername", :full_name=>"resource_param[ausername]", - :show=>true, - :expected_type=>"string"}, - {:required=>true, - :allow_nil => false, - :allow_blank => false, - :validator=>"Must be a String", - :description=>"\n<p>Password for login</p>\n", - :name=>"apassword", :full_name=>"resource_param[apassword]", - :show=>true, - :expected_type=>"string"} - ]} - ] - }, - {:required=>false, :validator=>"Parameter has to be Integer.", - :allow_nil => false, - :allow_blank => false, - :description=>"\n<p>Company ID</p>\n", - :name=>"id", :full_name=>"id", - :show=>true, - :expected_type=>"numeric"}, - ], - :name => 'two_urls', - :show => true, - :apis => [ + expected_hash = expected_hash = { + responses: [ + { + code: 404, + body: 'Not Found', + metadata: + { + some: 'metadata' + } + }, + { + code: 500, + body: 'Server crashed for some <%= reason %>' + } + ], + examples: [], + doc_url: "#{Apipie.configuration.doc_base_url}/development/users/two_urls", + formats: ['json'], + full_description: '', + params: [ + { + full_name: 'oauth', + required: false, + allow_nil: false, + allow_blank: false, + validator: 'Must be a String', + description: "\n<p>Authorization</p>\n", + name: 'oauth', + show: true, + expected_type: 'string' + }, + { + validator: 'Must be a Hash', + description: "\n<p>Deprecated parameter not documented</p>\n", + expected_type: 'object', + allow_nil: false, + allow_blank: false, + name: 'legacy_param', + required: false, + full_name: 'legacy_param', + show: false, + params: [ + { + validator: 'Must be a Hash', + description: "\n<p>Param description for all methods</p>\n", + expected_type: 'object', + allow_nil: false, + allow_blank: false, + name: 'resource_param', + required: false, + full_name: 'resource_param', + show: true, + params: [ + { + required: true, + allow_nil: false, + allow_blank: false, + validator: 'Must be a String', + description: "\n<p>Username for login</p>\n", + name: 'ausername', + full_name: 'resource_param[ausername]', + show: true, + expected_type: 'string' + }, + { + required: true, + allow_nil: false, + allow_blank: false, + validator: 'Must be a String', + description: "\n<p>Password for login</p>\n", + name: 'apassword', + full_name: 'resource_param[apassword]', + show: true, + expected_type: 'string' + } + ] + } + ] + }, + { + required: false, + validator: 'Parameter has to be Integer.', + allow_nil: false, + allow_blank: false, + description: "\n<p>Company ID</p>\n", + name: 'id', + full_name: 'id', + show: true, + expected_type: 'number' + } + ], + name: 'two_urls', + show: true, + apis: [ + { + http_method: 'GET', + short_description: 'Get company users', + api_url: "#{Apipie.api_base_url}/company_users" + }, { - :http_method => 'GET', - :short_description => 'Get company users', - :api_url => "#{Apipie.api_base_url}/company_users" - },{ - :http_method => 'GET', - :short_description => 'Get users working in given company', - :api_url =>"#{Apipie.api_base_url}/company/:id/users" + http_method: 'GET', + short_description: 'Get users working in given company', + api_url: "#{Apipie.api_base_url}/company/:id/users" } ] } diff --git a/spec/dummy/app/controllers/users_controller.rb b/spec/dummy/app/controllers/users_controller.rb index 7b70cea3..19c31b38 100644 --- a/spec/dummy/app/controllers/users_controller.rb +++ b/spec/dummy/app/controllers/users_controller.rb @@ -12,7 +12,7 @@ class UsersController < ApplicationController end end api_version "development" - error 404, "Missing", :meta => {:some => "metadata"} + error 404, "Not Found", :meta => {:some => "metadata"} error 500, "Server crashed for some <%= reason %>" meta :new_style => true, :author => { :name => 'John', :surname => 'Doe' } description <<-EOS @@ -172,7 +172,7 @@ class UsersController < ApplicationController show false formats ['json', 'jsonp'] error 401, "Unauthorized" - error :code => 404, :description => "Not Found" + error 404, "Not Found" param :id, Integer, :desc => "user id", :required => true param :session, String, :desc => "user is logged in", :required => true, :missing_message => lambda { "session_parameter_is_required" } param :regexp_param, /^[0-9]* years/, :desc => "regexp param" @@ -242,8 +242,8 @@ def admin_create end api :GET, "/users", "List users" - error :code => 401, :desc => "Unauthorized" - error :code => 404, :desc => "Not Found" + error 401, "Unauthorized" + error 404, "Not Found" desc "List all users." param :oauth, nil, :desc => "Hide this global param (eg dont need auth here)" diff --git a/spec/dummy/config/initializers/apipie.rb b/spec/dummy/config/initializers/apipie.rb index a2546bcc..73795de8 100644 --- a/spec/dummy/config/initializers/apipie.rb +++ b/spec/dummy/config/initializers/apipie.rb @@ -50,7 +50,7 @@ # set default version info, to describe specific version use # config.app_info[version] = description # or put this in your base or application controller - config.app_info = "Dummy app for testing" + config.app_info = "Dummy app for development" # show debug informations config.debug = false @@ -103,6 +103,6 @@ def description end def expected_type - 'numeric' + 'number' end end diff --git a/spec/lib/rake_spec.rb b/spec/lib/renderers/html_spec.rb similarity index 53% rename from spec/lib/rake_spec.rb rename to spec/lib/renderers/html_spec.rb index a0d2f86a..be2b2c3a 100644 --- a/spec/lib/rake_spec.rb +++ b/spec/lib/renderers/html_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe 'rake tasks' do +describe 'rake render' do include_context "rake" let(:doc_path) { "user_specified_doc_path" } @@ -11,7 +11,7 @@ subject.invoke(*task_args) end - describe 'static pages' do + describe 'html pages' do let(:apidoc_html) do File.read("#{doc_output}.html") @@ -22,11 +22,11 @@ end after do - Dir["#{doc_output}*"].each { |static_file| FileUtils.rm_rf(static_file) } + Dir["#{doc_output}*"].each { |html_file| FileUtils.rm_rf(html_file) } end - describe 'apipie:static' do - it "generates static files for the default version of apipie docs" do + describe 'apipie:render:html' do + it "generates html files for the default version of apipie docs" do expect(apidoc_html).to match(/Test app #{Apipie.configuration.default_version}/) end @@ -36,8 +36,8 @@ end end - describe 'apipie:static[2.0]' do - it "generates static files for the default version of apipie docs" do + describe 'apipie:render:html[2.0]' do + it "generates html files for the default version of apipie docs" do expect(apidoc_html).to match(/Test app 2.0/) end @@ -48,24 +48,4 @@ end end - describe 'apipie:cache' do - let(:cache_output) do - File.join(::Rails.root, 'public', 'apipie-cache') - end - - let(:apidoc_html) do - File.read("#{cache_output}.html") - end - - after do - Dir["#{cache_output}*"].each { |static_file| FileUtils.rm_rf(static_file) } - end - - it "generates cache files" do - expect(File).to exist(File.join(cache_output, 'apidoc.html')) - expect(File).to exist(File.join(cache_output, 'apidoc/development.html')) - expect(File).to exist(File.join(cache_output, 'apidoc/development/users.html')) - - end - end end diff --git a/spec/lib/renderers/json_spec.rb b/spec/lib/renderers/json_spec.rb new file mode 100644 index 00000000..af9d612a --- /dev/null +++ b/spec/lib/renderers/json_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe 'rake render' do + include_context "rake" + + let(:doc_path) { "user_specified_doc_path" } + + before do + Apipie.configuration.doc_path = doc_path + allow(Apipie).to receive(:reload_documentation) + subject.invoke(*task_args) + end + + describe 'json pages' do + + let(:json_doc) do + JSON.parse(File.read(json_path)) + end + + let(:json_path) do + "#{doc_output}/schema_apipie.json" + end + + let(:doc_output) do + File.join(::Rails.root, doc_path, 'apidoc') + end + + after do + Dir["#{doc_output}*"].each { |json_file| FileUtils.rm_rf(json_file) } + end + + describe 'apipie:render:json' do + it "generates json files for the default version of apipie docs" do + expect(File).to exist(json_path) + expect(json_doc['docs']['name']).to match(/Test app/) + expect(json_doc['docs']['info']).to match(/#{Apipie.configuration.default_version}/) + end + + end + + describe 'apipie:render:json[2.0]' do + it "generates json files for the default version of apipie docs" do + expect(File).to exist(json_path) + expect(json_doc['docs']['name']).to match(/Test app/) + expect(json_doc['docs']['info']).to match(/2.0/) + end + end + end + +end diff --git a/spec/lib/renderers/markdown_spec.rb b/spec/lib/renderers/markdown_spec.rb new file mode 100644 index 00000000..e47eb408 --- /dev/null +++ b/spec/lib/renderers/markdown_spec.rb @@ -0,0 +1,45 @@ +require 'spec_helper' + +describe 'rake render' do + include_context "rake" + + let(:doc_path) { "user_specified_doc_path" } + + before do + Apipie.configuration.doc_path = doc_path + allow(Apipie).to receive(:reload_documentation) + subject.invoke(*task_args) + end + + describe 'markdown pages' do + + let(:apidoc_md) do + File.read("#{doc_output}/apidoc-onepage.md") + end + + let(:doc_output) do + File.join(::Rails.root, doc_path) + end + + after do + Dir["#{doc_output}*"].each { |markdown_file| FileUtils.rm_rf(markdown_file) } + end + + describe 'apipie:render:markdown' do + it "generates markdown files for the default version of apipie docs" do + expect(apidoc_md).to match(/# Test app/) + expect(apidoc_md).to match(/Dummy app for #{Apipie.configuration.default_version}/) + end + + end + + describe 'apipie:render:markdown[2.0]' do + it "generates markdown files for the default version of apipie docs" do + expect(apidoc_md).to match(/# Test app/) + expect(apidoc_md).to match(/Version 2.0 description/) + end + + end + end + +end diff --git a/spec/lib/validator_spec.rb b/spec/lib/validator_spec.rb index badcc346..01cc8442 100644 --- a/spec/lib/validator_spec.rb +++ b/spec/lib/validator_spec.rb @@ -20,9 +20,9 @@ context "expected type" do - it "should return hash for type Hash" do + it "should return object for type Hash" do validator = Apipie::Validator::TypeValidator.new(params_desc, Hash) - expect(validator.expected_type).to eq('hash') + expect(validator.expected_type).to eq('object') end it "should return array for type Array" do @@ -30,9 +30,9 @@ expect(validator.expected_type).to eq('array') end - it "should return numeric for type Numeric" do + it "should return number for type Numeric" do validator = Apipie::Validator::TypeValidator.new(params_desc, Numeric) - expect(validator.expected_type).to eq('numeric') + expect(validator.expected_type).to eq('number') end it "should return string by default" do diff --git a/spec/support/rake.rb b/spec/support/rake.rb index fd8ed5ab..0f4170fb 100644 --- a/spec/support/rake.rb +++ b/spec/support/rake.rb @@ -5,16 +5,23 @@ let(:rake) { Rake::Application.new } let(:task_name) { rake.parse_task_string(self.class.description).first } let(:task_args) { rake.parse_task_string(self.class.description).last } - let(:task_path) { "lib/tasks/apipie" } + let(:task_path) { "lib/tasks" } subject { rake[task_name] } def loaded_files_excluding_current_rake_file $".reject {|file| file == File.expand_path("#{task_path}.rake", APIPIE_ROOT) } + ($" - rake_files.map(&File.method(:realpath))) + end + + def rake_files + Dir["#{task_path}/**/*.rake"] end before do Rake.application = rake - Rake.application.rake_require(task_path, [APIPIE_ROOT], loaded_files_excluding_current_rake_file) + rake_files.each do |file| + Rake.application.rake_require(file.gsub(/.rake$/, ''), [APIPIE_ROOT], loaded_files_excluding_current_rake_file) + end Rake::Task.define_task(:environment) end