haveapi 0.18.2 → 0.19.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/haveapi.gemspec +2 -1
 - data/lib/haveapi/action.rb +72 -31
 - data/lib/haveapi/authentication/base.rb +1 -1
 - data/lib/haveapi/authentication/basic/provider.rb +2 -2
 - data/lib/haveapi/authentication/chain.rb +4 -4
 - data/lib/haveapi/authentication/oauth2/config.rb +52 -14
 - data/lib/haveapi/authentication/oauth2/provider.rb +98 -17
 - data/lib/haveapi/authentication/oauth2/revoke_endpoint.rb +36 -0
 - data/lib/haveapi/authentication/token/config.rb +1 -0
 - data/lib/haveapi/authorization.rb +19 -12
 - data/lib/haveapi/client_examples/js_client.rb +11 -1
 - data/lib/haveapi/client_examples/php_client.rb +43 -1
 - data/lib/haveapi/context.rb +21 -2
 - data/lib/haveapi/example.rb +9 -9
 - data/lib/haveapi/hooks.rb +23 -23
 - data/lib/haveapi/metadata.rb +1 -1
 - data/lib/haveapi/model_adapter.rb +14 -14
 - data/lib/haveapi/model_adapters/active_record.rb +20 -20
 - data/lib/haveapi/output_formatter.rb +4 -4
 - data/lib/haveapi/output_formatters/base.rb +1 -1
 - data/lib/haveapi/parameters/resource.rb +22 -22
 - data/lib/haveapi/parameters/typed.rb +7 -7
 - data/lib/haveapi/params.rb +24 -22
 - data/lib/haveapi/resource.rb +9 -3
 - data/lib/haveapi/resources/action_state.rb +16 -16
 - data/lib/haveapi/route.rb +3 -2
 - data/lib/haveapi/server.rb +113 -98
 - data/lib/haveapi/spec/mock_action.rb +7 -7
 - data/lib/haveapi/spec/spec_methods.rb +8 -8
 - data/lib/haveapi/tasks/yard.rb +2 -2
 - data/lib/haveapi/validator.rb +13 -13
 - data/lib/haveapi/validator_chain.rb +6 -6
 - data/lib/haveapi/validators/acceptance.rb +2 -2
 - data/lib/haveapi/validators/confirmation.rb +4 -4
 - data/lib/haveapi/validators/exclusion.rb +4 -4
 - data/lib/haveapi/validators/format.rb +4 -4
 - data/lib/haveapi/validators/inclusion.rb +3 -3
 - data/lib/haveapi/validators/length.rb +1 -1
 - data/lib/haveapi/validators/numericality.rb +3 -3
 - data/lib/haveapi/validators/presence.rb +2 -2
 - data/lib/haveapi/version.rb +1 -1
 - data/lib/haveapi/views/version_page/auth_body.erb +6 -4
 - data/lib/haveapi/views/version_page/resource_body.erb +2 -0
 - data/lib/haveapi.rb +1 -0
 - data/spec/authorization_spec.rb +28 -28
 - data/spec/envelope_spec.rb +4 -4
 - data/spec/parameters/typed_spec.rb +3 -3
 - data/spec/params_spec.rb +2 -2
 - data/spec/validators/acceptance_spec.rb +2 -2
 - data/spec/validators/confirmation_spec.rb +4 -4
 - data/spec/validators/exclusion_spec.rb +2 -2
 - data/spec/validators/format_spec.rb +5 -5
 - data/spec/validators/inclusion_spec.rb +8 -8
 - data/spec/validators/presence_spec.rb +1 -1
 - metadata +19 -4
 
| 
         @@ -51,7 +51,17 @@ api.authenticate("token", { 
     | 
|
| 
       51 
51 
     | 
    
         
             
            END
         
     | 
| 
       52 
52 
     | 
    
         | 
| 
       53 
53 
     | 
    
         
             
                  when :oauth2
         
     | 
| 
       54 
     | 
    
         
            -
                     
     | 
| 
      
 54 
     | 
    
         
            +
                    <<END
         
     | 
| 
      
 55 
     | 
    
         
            +
            #{init}
         
     | 
| 
      
 56 
     | 
    
         
            +
            // The JavaScript client must be configured with OAuth2 access token, it does not
         
     | 
| 
      
 57 
     | 
    
         
            +
            // support the authorization procedure to obtain a new access token.
         
     | 
| 
      
 58 
     | 
    
         
            +
            var accessToken = {
         
     | 
| 
      
 59 
     | 
    
         
            +
              access_token: "the access token"
         
     | 
| 
      
 60 
     | 
    
         
            +
            };
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
            // The client is authenticated immediately, no need for a callback
         
     | 
| 
      
 63 
     | 
    
         
            +
            api.authenticate("oauth2", {access_token: accessToken});
         
     | 
| 
      
 64 
     | 
    
         
            +
            END
         
     | 
| 
       55 
65 
     | 
    
         
             
                  end
         
     | 
| 
       56 
66 
     | 
    
         
             
                end
         
     | 
| 
       57 
67 
     | 
    
         | 
| 
         @@ -35,7 +35,49 @@ $api->authenticate("token", ["token" => $savedToken]); 
     | 
|
| 
       35 
35 
     | 
    
         
             
            END
         
     | 
| 
       36 
36 
     | 
    
         | 
| 
       37 
37 
     | 
    
         
             
                  when :oauth2
         
     | 
| 
       38 
     | 
    
         
            -
                     
     | 
| 
      
 38 
     | 
    
         
            +
                    <<END
         
     | 
| 
      
 39 
     | 
    
         
            +
            // OAuth2 requires session
         
     | 
| 
      
 40 
     | 
    
         
            +
            session_start();
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            // Client instance
         
     | 
| 
      
 43 
     | 
    
         
            +
            #{init}
         
     | 
| 
      
 44 
     | 
    
         
            +
            // Check if we already have an access token
         
     | 
| 
      
 45 
     | 
    
         
            +
            if (isset($_SESSION["access_token"])) {
         
     | 
| 
      
 46 
     | 
    
         
            +
              // We're already authenticated, reuse the existing access token
         
     | 
| 
      
 47 
     | 
    
         
            +
              $api->authenticate("oauth2", ["access_token" => $_SESSION["access_token"]]);
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            } else {
         
     | 
| 
      
 50 
     | 
    
         
            +
              // Follow the OAuth2 authorization process to get an access token using
         
     | 
| 
      
 51 
     | 
    
         
            +
              // authorization code
         
     | 
| 
      
 52 
     | 
    
         
            +
              $api->authenticate("oauth2", [
         
     | 
| 
      
 53 
     | 
    
         
            +
                // Client id and secret are given by the API server
         
     | 
| 
      
 54 
     | 
    
         
            +
                "client_id" => "your client id",
         
     | 
| 
      
 55 
     | 
    
         
            +
                "client_secret" => "your client secret",
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                // This example code should run on the URL below
         
     | 
| 
      
 58 
     | 
    
         
            +
                "redirect_uri" => "https://your-client.tld/oauth2-callback",
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                // Scopes are specific to the API implementation
         
     | 
| 
      
 61 
     | 
    
         
            +
                "scope" => "all",
         
     | 
| 
      
 62 
     | 
    
         
            +
              ]);
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              $provider = $api->getAuthenticationProvider();
         
     | 
| 
      
 65 
     | 
    
         
            +
             
     | 
| 
      
 66 
     | 
    
         
            +
              // We don't have authorization code yet, request one
         
     | 
| 
      
 67 
     | 
    
         
            +
              if (!isset($_GET['code'])) {
         
     | 
| 
      
 68 
     | 
    
         
            +
                // Redirect the user to the authorization endpoint
         
     | 
| 
      
 69 
     | 
    
         
            +
                $provider->requestAuthorizationCode();
         
     | 
| 
      
 70 
     | 
    
         
            +
                exit;
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              } else {
         
     | 
| 
      
 73 
     | 
    
         
            +
                // Request access token using the token endpoint
         
     | 
| 
      
 74 
     | 
    
         
            +
                $provider->requestAccessToken();
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                // Store the access token in the session
         
     | 
| 
      
 77 
     | 
    
         
            +
                $_SESSION['access_token'] = $provider->jsonSerialize();
         
     | 
| 
      
 78 
     | 
    
         
            +
              }
         
     | 
| 
      
 79 
     | 
    
         
            +
            }
         
     | 
| 
      
 80 
     | 
    
         
            +
            END
         
     | 
| 
       39 
81 
     | 
    
         
             
                  end
         
     | 
| 
       40 
82 
     | 
    
         
             
                end
         
     | 
| 
       41 
83 
     | 
    
         | 
    
        data/lib/haveapi/context.rb
    CHANGED
    
    | 
         @@ -1,12 +1,12 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module HaveAPI
         
     | 
| 
       2 
2 
     | 
    
         
             
              class Context
         
     | 
| 
       3 
3 
     | 
    
         
             
                attr_accessor :server, :version, :request, :resource, :action, :path, :args,
         
     | 
| 
       4 
     | 
    
         
            -
                              :params, :current_user, :authorization, :endpoint,
         
     | 
| 
      
 4 
     | 
    
         
            +
                              :params, :current_user, :authorization, :endpoint, :resource_path,
         
     | 
| 
       5 
5 
     | 
    
         
             
                              :action_instance, :action_prepare, :layout
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
                def initialize(server, version: nil, request: nil, resource: [], action: nil,
         
     | 
| 
       8 
8 
     | 
    
         
             
                              path: nil, args: nil, params: nil, user: nil,
         
     | 
| 
       9 
     | 
    
         
            -
                              authorization: nil, endpoint: nil)
         
     | 
| 
      
 9 
     | 
    
         
            +
                              authorization: nil, endpoint: nil, resource_path: [])
         
     | 
| 
       10 
10 
     | 
    
         
             
                  @server = server
         
     | 
| 
       11 
11 
     | 
    
         
             
                  @version = version
         
     | 
| 
       12 
12 
     | 
    
         
             
                  @request = request
         
     | 
| 
         @@ -18,6 +18,7 @@ module HaveAPI 
     | 
|
| 
       18 
18 
     | 
    
         
             
                  @current_user = user
         
     | 
| 
       19 
19 
     | 
    
         
             
                  @authorization = authorization
         
     | 
| 
       20 
20 
     | 
    
         
             
                  @endpoint = endpoint
         
     | 
| 
      
 21 
     | 
    
         
            +
                  @resource_path = resource_path
         
     | 
| 
       21 
22 
     | 
    
         
             
                end
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
                def resolved_path
         
     | 
| 
         @@ -71,6 +72,24 @@ module HaveAPI 
     | 
|
| 
       71 
72 
     | 
    
         
             
                  path_for(action, call_path_params(action, obj))
         
     | 
| 
       72 
73 
     | 
    
         
             
                end
         
     | 
| 
       73 
74 
     | 
    
         | 
| 
      
 75 
     | 
    
         
            +
                def path_params_from_args
         
     | 
| 
      
 76 
     | 
    
         
            +
                  ret = {}
         
     | 
| 
      
 77 
     | 
    
         
            +
                  return ret if args.nil?
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                  my_args = args.clone
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                  path.scan(/\{([a-zA-Z\-_]+)\}/) do |match|
         
     | 
| 
      
 82 
     | 
    
         
            +
                    path_param = match.first
         
     | 
| 
      
 83 
     | 
    
         
            +
                    ret[path_param] = my_args.shift
         
     | 
| 
      
 84 
     | 
    
         
            +
                  end
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
                  ret
         
     | 
| 
      
 87 
     | 
    
         
            +
                end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
                def action_scope
         
     | 
| 
      
 90 
     | 
    
         
            +
                  resource_path.map(&:downcase).join('.') + '#' + action.action_name.underscore
         
     | 
| 
      
 91 
     | 
    
         
            +
                end
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
       74 
93 
     | 
    
         
             
                private
         
     | 
| 
       75 
94 
     | 
    
         
             
                def resolve_arg!(path, arg)
         
     | 
| 
       76 
95 
     | 
    
         
             
                  path.sub!(/\{[a-zA-Z\-_]+\}/, arg.to_s)
         
     | 
    
        data/lib/haveapi/example.rb
    CHANGED
    
    | 
         @@ -58,15 +58,15 @@ module HaveAPI 
     | 
|
| 
       58 
58 
     | 
    
         
             
                def describe(context)
         
     | 
| 
       59 
59 
     | 
    
         
             
                  if provided?
         
     | 
| 
       60 
60 
     | 
    
         
             
                    {
         
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
      
 61 
     | 
    
         
            +
                      title: @title,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      comment: @comment,
         
     | 
| 
      
 63 
     | 
    
         
            +
                      path_params: @path_params,
         
     | 
| 
      
 64 
     | 
    
         
            +
                      request: filter_input_params(context, @request),
         
     | 
| 
      
 65 
     | 
    
         
            +
                      response: filter_output_params(context, @response),
         
     | 
| 
      
 66 
     | 
    
         
            +
                      status: @status.nil? ? true : @status,
         
     | 
| 
      
 67 
     | 
    
         
            +
                      message: @message,
         
     | 
| 
      
 68 
     | 
    
         
            +
                      errors: @errors,
         
     | 
| 
      
 69 
     | 
    
         
            +
                      http_status: @http_status || 200,
         
     | 
| 
       70 
70 
     | 
    
         
             
                    }
         
     | 
| 
       71 
71 
     | 
    
         
             
                  else
         
     | 
| 
       72 
72 
     | 
    
         
             
                    {}
         
     | 
    
        data/lib/haveapi/hooks.rb
    CHANGED
    
    | 
         @@ -79,15 +79,15 @@ module HaveAPI 
     | 
|
| 
       79 
79 
     | 
    
         
             
              module Hooks
         
     | 
| 
       80 
80 
     | 
    
         
             
                INSTANCE_VARIABLE = '@_haveapi_hooks'
         
     | 
| 
       81 
81 
     | 
    
         | 
| 
       82 
     | 
    
         
            -
                # Register a hook defined by  
     | 
| 
       83 
     | 
    
         
            -
                #  
     | 
| 
       84 
     | 
    
         
            -
                #  
     | 
| 
       85 
     | 
    
         
            -
                # 
     | 
| 
       86 
     | 
    
         
            -
                # 
     | 
| 
       87 
     | 
    
         
            -
                # 
     | 
| 
       88 
     | 
    
         
            -
                # 
     | 
| 
       89 
     | 
    
         
            -
                # 
     | 
| 
       90 
     | 
    
         
            -
                # 
     | 
| 
      
 82 
     | 
    
         
            +
                # Register a hook defined by `klass` with `name`.
         
     | 
| 
      
 83 
     | 
    
         
            +
                # @param klass [Class]  an instance of Class, that is class name, not it's instance
         
     | 
| 
      
 84 
     | 
    
         
            +
                # @param opts [Hash]
         
     | 
| 
      
 85 
     | 
    
         
            +
                # @option opts [String] :desc why this hook exists, when it's called
         
     | 
| 
      
 86 
     | 
    
         
            +
                # @option opts [String] :context the context in which given blocks are called
         
     | 
| 
      
 87 
     | 
    
         
            +
                # @option opts [Hash] :args hash of block positional arguments
         
     | 
| 
      
 88 
     | 
    
         
            +
                # @option opts [Hash] :kwargs hash of block keyword arguments
         
     | 
| 
      
 89 
     | 
    
         
            +
                # @option opts [Hash] :initial - hash of initial values
         
     | 
| 
      
 90 
     | 
    
         
            +
                # @option opts [Hash] :ret hash of return values
         
     | 
| 
       91 
91 
     | 
    
         
             
                def self.register_hook(klass, name, opts = {})
         
     | 
| 
       92 
92 
     | 
    
         
             
                  classified = hook_classify(klass)
         
     | 
| 
       93 
93 
     | 
    
         
             
                  opts[:listeners] = []
         
     | 
| 
         @@ -101,13 +101,13 @@ module HaveAPI 
     | 
|
| 
       101 
101 
     | 
    
         
             
                  @hooks
         
     | 
| 
       102 
102 
     | 
    
         
             
                end
         
     | 
| 
       103 
103 
     | 
    
         | 
| 
       104 
     | 
    
         
            -
                # Connect class hook defined in  
     | 
| 
       105 
     | 
    
         
            -
                #  
     | 
| 
      
 104 
     | 
    
         
            +
                # Connect class hook defined in `klass` with `name` to `block`.
         
     | 
| 
      
 105 
     | 
    
         
            +
                # `klass` is a class name.
         
     | 
| 
       106 
106 
     | 
    
         
             
                def self.connect_hook(klass, name, &block)
         
     | 
| 
       107 
107 
     | 
    
         
             
                  @hooks[hook_classify(klass)][name][:listeners] << block
         
     | 
| 
       108 
108 
     | 
    
         
             
                end
         
     | 
| 
       109 
109 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
                # Connect instance hook from instance  
     | 
| 
      
 110 
     | 
    
         
            +
                # Connect instance hook from instance `klass` with `name` to `block`.
         
     | 
| 
       111 
111 
     | 
    
         
             
                def self.connect_instance_hook(instance, name, &block)
         
     | 
| 
       112 
112 
     | 
    
         
             
                  hooks = instance.instance_variable_get(INSTANCE_VARIABLE)
         
     | 
| 
       113 
113 
     | 
    
         | 
| 
         @@ -120,11 +120,11 @@ module HaveAPI 
     | 
|
| 
       120 
120 
     | 
    
         
             
                  hooks[name][:listeners] << block
         
     | 
| 
       121 
121 
     | 
    
         
             
                end
         
     | 
| 
       122 
122 
     | 
    
         | 
| 
       123 
     | 
    
         
            -
                # Call all blocks that are connected to hook in  
     | 
| 
      
 123 
     | 
    
         
            +
                # Call all blocks that are connected to hook in `klass` with `name`.
         
     | 
| 
       124 
124 
     | 
    
         
             
                # +klass+ may be a class name or an object instance.
         
     | 
| 
       125 
     | 
    
         
            -
                # If  
     | 
| 
       126 
     | 
    
         
            -
                #  
     | 
| 
       127 
     | 
    
         
            -
                # to all block is always a return value from previous block or  
     | 
| 
      
 125 
     | 
    
         
            +
                # If `where` is set, the blocks are executed in it with instance_exec.
         
     | 
| 
      
 126 
     | 
    
         
            +
                # `args` is an array of arguments given to all blocks. The first argument
         
     | 
| 
      
 127 
     | 
    
         
            +
                # to all block is always a return value from previous block or `initial`,
         
     | 
| 
       128 
128 
     | 
    
         
             
                # which defaults to an empty hash.
         
     | 
| 
       129 
129 
     | 
    
         
             
                #
         
     | 
| 
       130 
130 
     | 
    
         
             
                # Blocks are executed one by one in the order they were connected.
         
     | 
| 
         @@ -193,17 +193,17 @@ module HaveAPI 
     | 
|
| 
       193 
193 
     | 
    
         
             
              # Classes that define hooks must include this module.
         
     | 
| 
       194 
194 
     | 
    
         
             
              module Hookable
         
     | 
| 
       195 
195 
     | 
    
         
             
                module ClassMethods
         
     | 
| 
       196 
     | 
    
         
            -
                  # Register a hook named  
     | 
| 
      
 196 
     | 
    
         
            +
                  # Register a hook named `name`.
         
     | 
| 
       197 
197 
     | 
    
         
             
                  def has_hook(name, opts = {})
         
     | 
| 
       198 
198 
     | 
    
         
             
                    Hooks.register_hook(self.to_s, name, opts)
         
     | 
| 
       199 
199 
     | 
    
         
             
                  end
         
     | 
| 
       200 
200 
     | 
    
         | 
| 
       201 
     | 
    
         
            -
                  # Connect  
     | 
| 
      
 201 
     | 
    
         
            +
                  # Connect `block` to registered hook with `name`.
         
     | 
| 
       202 
202 
     | 
    
         
             
                  def connect_hook(name, &block)
         
     | 
| 
       203 
203 
     | 
    
         
             
                    Hooks.connect_hook(self.to_s, name, &block)
         
     | 
| 
       204 
204 
     | 
    
         
             
                  end
         
     | 
| 
       205 
205 
     | 
    
         | 
| 
       206 
     | 
    
         
            -
                  # Call all hooks for  
     | 
| 
      
 206 
     | 
    
         
            +
                  # Call all hooks for `name`. see {Hooks.call_for}.
         
     | 
| 
       207 
207 
     | 
    
         
             
                  def call_hooks(*args, **kwargs)
         
     | 
| 
       208 
208 
     | 
    
         
             
                    Hooks.call_for(self.to_s, *args, **kwargs)
         
     | 
| 
       209 
209 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -228,7 +228,7 @@ module HaveAPI 
     | 
|
| 
       228 
228 
     | 
    
         
             
                    Hooks.call_for(self.class, name, where, args: args, kwargs: kwargs, initial: initial)
         
     | 
| 
       229 
229 
     | 
    
         
             
                  end
         
     | 
| 
       230 
230 
     | 
    
         | 
| 
       231 
     | 
    
         
            -
                  # Call hooks for different  
     | 
| 
      
 231 
     | 
    
         
            +
                  # Call hooks for different `klass`.
         
     | 
| 
       232 
232 
     | 
    
         
             
                  def call_hooks_as_for(klass, *args, **kwargs)
         
     | 
| 
       233 
233 
     | 
    
         
             
                    ret = call_instance_hooks_as_for(klass, *args, **kwargs)
         
     | 
| 
       234 
234 
     | 
    
         | 
| 
         @@ -236,17 +236,17 @@ module HaveAPI 
     | 
|
| 
       236 
236 
     | 
    
         
             
                    call_class_hooks_as_for(klass.class, *args, **kwargs)
         
     | 
| 
       237 
237 
     | 
    
         
             
                  end
         
     | 
| 
       238 
238 
     | 
    
         | 
| 
       239 
     | 
    
         
            -
                  # Call only instance hooks for different  
     | 
| 
      
 239 
     | 
    
         
            +
                  # Call only instance hooks for different `klass`.
         
     | 
| 
       240 
240 
     | 
    
         
             
                  def call_instance_hooks_as_for(klass, *args, **kwargs)
         
     | 
| 
       241 
241 
     | 
    
         
             
                    Hooks.call_for(klass, *args, **kwargs)
         
     | 
| 
       242 
242 
     | 
    
         
             
                  end
         
     | 
| 
       243 
243 
     | 
    
         | 
| 
       244 
     | 
    
         
            -
                  # Call only class hooks for different  
     | 
| 
      
 244 
     | 
    
         
            +
                  # Call only class hooks for different `klass`.
         
     | 
| 
       245 
245 
     | 
    
         
             
                  def call_class_hooks_as_for(klass, *args, **kwargs)
         
     | 
| 
       246 
246 
     | 
    
         
             
                    Hooks.call_for(klass, *args, **kwargs)
         
     | 
| 
       247 
247 
     | 
    
         
             
                  end
         
     | 
| 
       248 
248 
     | 
    
         | 
| 
       249 
     | 
    
         
            -
                  # Connect instance level hook  
     | 
| 
      
 249 
     | 
    
         
            +
                  # Connect instance level hook `name` to `block`.
         
     | 
| 
       250 
250 
     | 
    
         
             
                  def connect_hook(name, &block)
         
     | 
| 
       251 
251 
     | 
    
         
             
                    Hooks.connect_instance_hook(self, name, &block)
         
     | 
| 
       252 
252 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/haveapi/metadata.rb
    CHANGED
    
    
| 
         @@ -2,8 +2,8 @@ module HaveAPI 
     | 
|
| 
       2 
2 
     | 
    
         
             
              # Model adapters are used to automate handling of action
         
     | 
| 
       3 
3 
     | 
    
         
             
              # input/output.
         
     | 
| 
       4 
4 
     | 
    
         
             
              #
         
     | 
| 
       5 
     | 
    
         
            -
              # Adapters are chosen based on the  
     | 
| 
       6 
     | 
    
         
            -
              # If no  
     | 
| 
      
 5 
     | 
    
         
            +
              # Adapters are chosen based on the `model` set on a HaveAPI::Resource.
         
     | 
| 
      
 6 
     | 
    
         
            +
              # If no `model` is specified, ModelAdapters::Hash is used as a default
         
     | 
| 
       7 
7 
     | 
    
         
             
              # adapter.
         
     | 
| 
       8 
8 
     | 
    
         
             
              #
         
     | 
| 
       9 
9 
     | 
    
         
             
              # All model adapters are based on this class.
         
     | 
| 
         @@ -17,7 +17,7 @@ module HaveAPI 
     | 
|
| 
       17 
17 
     | 
    
         
             
                    ModelAdapter.adapters << Kernel.const_get(self.to_s)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                  # Returns an adapter suitable for  
     | 
| 
      
 20 
     | 
    
         
            +
                  # Returns an adapter suitable for `layout` and `obj`.
         
     | 
| 
       21 
21 
     | 
    
         
             
                  # Adapters are iterated over and the first to return true to handle?()
         
     | 
| 
       22 
22 
     | 
    
         
             
                  # is returned.
         
     | 
| 
       23 
23 
     | 
    
         
             
                  def for(layout, obj)
         
     | 
| 
         @@ -41,21 +41,21 @@ module HaveAPI 
     | 
|
| 
       41 
41 
     | 
    
         
             
                    self::Output.new(*args)
         
     | 
| 
       42 
42 
     | 
    
         
             
                  end
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                  # Override this method to load validators from  
     | 
| 
       45 
     | 
    
         
            -
                  # to  
     | 
| 
      
 44 
     | 
    
         
            +
                  # Override this method to load validators from `model`
         
     | 
| 
      
 45 
     | 
    
         
            +
                  # to `params`.
         
     | 
| 
       46 
46 
     | 
    
         
             
                  def load_validators(model, params)
         
     | 
| 
       47 
47 
     | 
    
         | 
| 
       48 
48 
     | 
    
         
             
                  end
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
50 
     | 
    
         
             
                  # Called when mounting the API. Model adapters may use this method
         
     | 
| 
       51 
     | 
    
         
            -
                  # to add custom meta parameters to  
     | 
| 
       52 
     | 
    
         
            -
                  #  
     | 
| 
      
 51 
     | 
    
         
            +
                  # to add custom meta parameters to `action`. `direction` is one of
         
     | 
| 
      
 52 
     | 
    
         
            +
                  # `:input` and `:output`.
         
     | 
| 
       53 
53 
     | 
    
         
             
                  def used_by(direction, action)
         
     | 
| 
       54 
54 
     | 
    
         
             
                    case direction
         
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
       57 
     | 
    
         
            -
             
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
      
 55 
     | 
    
         
            +
                    when :input
         
     | 
| 
      
 56 
     | 
    
         
            +
                      self::Input.used_by(action)
         
     | 
| 
      
 57 
     | 
    
         
            +
                    when :output
         
     | 
| 
      
 58 
     | 
    
         
            +
                      self::Output.used_by(action)
         
     | 
| 
       59 
59 
     | 
    
         
             
                    end
         
     | 
| 
       60 
60 
     | 
    
         
             
                  end
         
     | 
| 
       61 
61 
     | 
    
         
             
                end
         
     | 
| 
         @@ -72,12 +72,12 @@ module HaveAPI 
     | 
|
| 
       72 
72 
     | 
    
         
             
                  end
         
     | 
| 
       73 
73 
     | 
    
         | 
| 
       74 
74 
     | 
    
         
             
                  # Return true if input parameters contain parameter
         
     | 
| 
       75 
     | 
    
         
            -
                  # with  
     | 
| 
      
 75 
     | 
    
         
            +
                  # with `name`.
         
     | 
| 
       76 
76 
     | 
    
         
             
                  def has_param?(name)
         
     | 
| 
       77 
77 
     | 
    
         
             
                    @input.has_key?(name)
         
     | 
| 
       78 
78 
     | 
    
         
             
                  end
         
     | 
| 
       79 
79 
     | 
    
         | 
| 
       80 
     | 
    
         
            -
                  # Return parameter with  
     | 
| 
      
 80 
     | 
    
         
            +
                  # Return parameter with `name`.
         
     | 
| 
       81 
81 
     | 
    
         
             
                  def [](name)
         
     | 
| 
       82 
82 
     | 
    
         
             
                    @input[name]
         
     | 
| 
       83 
83 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -101,7 +101,7 @@ module HaveAPI 
     | 
|
| 
       101 
101 
     | 
    
         
             
                  end
         
     | 
| 
       102 
102 
     | 
    
         | 
| 
       103 
103 
     | 
    
         
             
                  # Return true if input parameters contain parameter
         
     | 
| 
       104 
     | 
    
         
            -
                  # with  
     | 
| 
      
 104 
     | 
    
         
            +
                  # with `name`.
         
     | 
| 
       105 
105 
     | 
    
         
             
                  def has_param?(name)
         
     | 
| 
       106 
106 
     | 
    
         | 
| 
       107 
107 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -21,7 +21,7 @@ module HaveAPI::ModelAdapters 
     | 
|
| 
       21 
21 
     | 
    
         
             
                  module InstanceMethods
         
     | 
| 
       22 
22 
     | 
    
         
             
                    # Helper method that sets correct ActiveRecord includes
         
     | 
| 
       23 
23 
     | 
    
         
             
                    # according to the meta includes sent by the user.
         
     | 
| 
       24 
     | 
    
         
            -
                    #  
     | 
| 
      
 24 
     | 
    
         
            +
                    # `q` is the model or partial AR query. If not set,
         
     | 
| 
       25 
25 
     | 
    
         
             
                    # action's model class is used instead.
         
     | 
| 
       26 
26 
     | 
    
         
             
                    def with_includes(q = nil)
         
     | 
| 
       27 
27 
     | 
    
         
             
                      q ||= self.class.model
         
     | 
| 
         @@ -179,14 +179,14 @@ END 
     | 
|
| 
       179 
179 
     | 
    
         
             
                    end
         
     | 
| 
       180 
180 
     | 
    
         | 
| 
       181 
181 
     | 
    
         
             
                    {
         
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
       183 
     | 
    
         
            -
             
     | 
| 
      
 182 
     | 
    
         
            +
                      path_params: params.is_a?(Array) ? params : [params],
         
     | 
| 
      
 183 
     | 
    
         
            +
                      resolved: true
         
     | 
| 
       184 
184 
     | 
    
         
             
                    }
         
     | 
| 
       185 
185 
     | 
    
         
             
                  end
         
     | 
| 
       186 
186 
     | 
    
         | 
| 
       187 
187 
     | 
    
         
             
                  protected
         
     | 
| 
       188 
     | 
    
         
            -
                  # Return representation of an associated resource  
     | 
| 
       189 
     | 
    
         
            -
                  # with its instance in  
     | 
| 
      
 188 
     | 
    
         
            +
                  # Return representation of an associated resource `param`
         
     | 
| 
      
 189 
     | 
    
         
            +
                  # with its instance in `val`.
         
     | 
| 
       190 
190 
     | 
    
         
             
                  #
         
     | 
| 
       191 
191 
     | 
    
         
             
                  # By default, it returns an unresolved resource, which contains
         
     | 
| 
       192 
192 
     | 
    
         
             
                  # only object id and label. Resource will be resolved
         
     | 
| 
         @@ -204,11 +204,11 @@ END 
     | 
|
| 
       204 
204 
     | 
    
         
             
                      pass_includes = includes_pass_on_to(param.name)
         
     | 
| 
       205 
205 
     | 
    
         | 
| 
       206 
206 
     | 
    
         
             
                      show = res_show.new(
         
     | 
| 
       207 
     | 
    
         
            -
             
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
       210 
     | 
    
         
            -
             
     | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
      
 207 
     | 
    
         
            +
                        push_ins.request,
         
     | 
| 
      
 208 
     | 
    
         
            +
                        push_ins.version,
         
     | 
| 
      
 209 
     | 
    
         
            +
                        {},
         
     | 
| 
      
 210 
     | 
    
         
            +
                        nil,
         
     | 
| 
      
 211 
     | 
    
         
            +
                        @context
         
     | 
| 
       212 
212 
     | 
    
         
             
                      )
         
     | 
| 
       213 
213 
     | 
    
         
             
                      show.meta[:includes] = pass_includes
         
     | 
| 
       214 
214 
     | 
    
         | 
| 
         @@ -231,17 +231,17 @@ END 
     | 
|
| 
       231 
231 
     | 
    
         | 
| 
       232 
232 
     | 
    
         
             
                    else
         
     | 
| 
       233 
233 
     | 
    
         
             
                      {
         
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
             
     | 
| 
       236 
     | 
    
         
            -
             
     | 
| 
       237 
     | 
    
         
            -
             
     | 
| 
       238 
     | 
    
         
            -
             
     | 
| 
       239 
     | 
    
         
            -
             
     | 
| 
      
 234 
     | 
    
         
            +
                        param.value_id => val.send(res_output[param.value_id].db_name),
         
     | 
| 
      
 235 
     | 
    
         
            +
                        param.value_label => val.send(res_output[param.value_label].db_name),
         
     | 
| 
      
 236 
     | 
    
         
            +
                        _meta: {
         
     | 
| 
      
 237 
     | 
    
         
            +
                          :path_params => args.is_a?(Array) ? args : [args],
         
     | 
| 
      
 238 
     | 
    
         
            +
                          :resolved => false
         
     | 
| 
      
 239 
     | 
    
         
            +
                        }
         
     | 
| 
       240 
240 
     | 
    
         
             
                      }
         
     | 
| 
       241 
241 
     | 
    
         
             
                    end
         
     | 
| 
       242 
242 
     | 
    
         
             
                  end
         
     | 
| 
       243 
243 
     | 
    
         | 
| 
       244 
     | 
    
         
            -
                  # Should an association with  
     | 
| 
      
 244 
     | 
    
         
            +
                  # Should an association with `name` be resolved?
         
     | 
| 
       245 
245 
     | 
    
         
             
                  def includes_include?(name)
         
     | 
| 
       246 
246 
     | 
    
         
             
                    includes = @context.action_instance.meta[:includes]
         
     | 
| 
       247 
247 
     | 
    
         
             
                    return unless includes
         
     | 
| 
         @@ -324,7 +324,7 @@ END 
     | 
|
| 
       324 
324 
     | 
    
         | 
| 
       325 
325 
     | 
    
         
             
                  handle ::ActiveModel::Validations::ExclusionValidator do |v|
         
     | 
| 
       326 
326 
     | 
    
         
             
                    opts = {
         
     | 
| 
       327 
     | 
    
         
            -
             
     | 
| 
      
 327 
     | 
    
         
            +
                      values: v.options[:in].map { |v| v }
         
     | 
| 
       328 
328 
     | 
    
         
             
                    }
         
     | 
| 
       329 
329 
     | 
    
         
             
                    opts[:message] = v.options[:message] if v.options[:message]
         
     | 
| 
       330 
330 
     | 
    
         | 
| 
         @@ -333,7 +333,7 @@ END 
     | 
|
| 
       333 
333 
     | 
    
         | 
| 
       334 
334 
     | 
    
         
             
                  handle ::ActiveModel::Validations::FormatValidator do |v|
         
     | 
| 
       335 
335 
     | 
    
         
             
                    opts = {
         
     | 
| 
       336 
     | 
    
         
            -
             
     | 
| 
      
 336 
     | 
    
         
            +
                      rx: v.options[:with]
         
     | 
| 
       337 
337 
     | 
    
         
             
                    }
         
     | 
| 
       338 
338 
     | 
    
         
             
                    opts[:message] = v.options[:message] if v.options[:message]
         
     | 
| 
       339 
339 
     | 
    
         | 
| 
         @@ -342,7 +342,7 @@ END 
     | 
|
| 
       342 
342 
     | 
    
         | 
| 
       343 
343 
     | 
    
         
             
                  handle ::ActiveModel::Validations::InclusionValidator do |v|
         
     | 
| 
       344 
344 
     | 
    
         
             
                    opts = {
         
     | 
| 
       345 
     | 
    
         
            -
             
     | 
| 
      
 345 
     | 
    
         
            +
                      values: v.options[:in].map { |v| v }
         
     | 
| 
       346 
346 
     | 
    
         
             
                    }
         
     | 
| 
       347 
347 
     | 
    
         
             
                    opts[:message] = v.options[:message] if v.options[:message]
         
     | 
| 
       348 
348 
     | 
    
         | 
| 
         @@ -49,10 +49,10 @@ module HaveAPI 
     | 
|
| 
       49 
49 
     | 
    
         
             
                  ret = {}
         
     | 
| 
       50 
50 
     | 
    
         
             
                  ret[:version] = HaveAPI::PROTOCOL_VERSION if version
         
     | 
| 
       51 
51 
     | 
    
         
             
                  ret.update({
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
                    status: status,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    response: response,
         
     | 
| 
      
 54 
     | 
    
         
            +
                    message: message,
         
     | 
| 
      
 55 
     | 
    
         
            +
                    errors: errors
         
     | 
| 
       56 
56 
     | 
    
         
             
                  })
         
     | 
| 
       57 
57 
     | 
    
         
             
                  ret
         
     | 
| 
       58 
58 
     | 
    
         
             
                end
         
     | 
| 
         @@ -43,35 +43,35 @@ module HaveAPI::Parameters 
     | 
|
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
                def describe(context)
         
     | 
| 
       45 
45 
     | 
    
         
             
                  val_path = context.path_for(
         
     | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
      
 46 
     | 
    
         
            +
                    @resource::Show,
         
     | 
| 
      
 47 
     | 
    
         
            +
                    context.endpoint && context.action_prepare && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
         
     | 
| 
       48 
48 
     | 
    
         
             
                  )
         
     | 
| 
       49 
49 
     | 
    
         
             
                  val_method = @resource::Index.http_method.to_s.upcase
         
     | 
| 
       50 
50 
     | 
    
         | 
| 
       51 
51 
     | 
    
         
             
                  choices_path = context.path_for(
         
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
                    @choices,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    context.endpoint && context.layout == :object && context.call_path_params(context.action, context.action_prepare)
         
     | 
| 
       54 
54 
     | 
    
         
             
                  )
         
     | 
| 
       55 
55 
     | 
    
         
             
                  choices_method = @choices.http_method.to_s.upcase
         
     | 
| 
       56 
56 
     | 
    
         | 
| 
       57 
57 
     | 
    
         
             
                  {
         
     | 
| 
       58 
     | 
    
         
            -
             
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
             
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
             
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
             
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
             
     | 
| 
       70 
     | 
    
         
            -
             
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 58 
     | 
    
         
            +
                    required: required?,
         
     | 
| 
      
 59 
     | 
    
         
            +
                    label: @label,
         
     | 
| 
      
 60 
     | 
    
         
            +
                    description: @desc,
         
     | 
| 
      
 61 
     | 
    
         
            +
                    type: 'Resource',
         
     | 
| 
      
 62 
     | 
    
         
            +
                    resource: @resource_path,
         
     | 
| 
      
 63 
     | 
    
         
            +
                    value_id: @value_id,
         
     | 
| 
      
 64 
     | 
    
         
            +
                    value_label: @value_label,
         
     | 
| 
      
 65 
     | 
    
         
            +
                    value: context.action_prepare && {
         
     | 
| 
      
 66 
     | 
    
         
            +
                      path: val_path,
         
     | 
| 
      
 67 
     | 
    
         
            +
                      method: val_method,
         
     | 
| 
      
 68 
     | 
    
         
            +
                      help: "#{val_path}?method=#{val_method}",
         
     | 
| 
      
 69 
     | 
    
         
            +
                    },
         
     | 
| 
      
 70 
     | 
    
         
            +
                    choices: {
         
     | 
| 
      
 71 
     | 
    
         
            +
                      path: choices_path,
         
     | 
| 
      
 72 
     | 
    
         
            +
                      method: choices_method,
         
     | 
| 
      
 73 
     | 
    
         
            +
                      help: "#{choices_path}?method=#{choices_method}"
         
     | 
| 
      
 74 
     | 
    
         
            +
                    }
         
     | 
| 
       75 
75 
     | 
    
         
             
                  }
         
     | 
| 
       76 
76 
     | 
    
         
             
                end
         
     | 
| 
       77 
77 
     | 
    
         | 
| 
         @@ -93,7 +93,7 @@ module HaveAPI::Parameters 
     | 
|
| 
       93 
93 
     | 
    
         | 
| 
       94 
94 
     | 
    
         
             
                def clean(raw)
         
     | 
| 
       95 
95 
     | 
    
         
             
                  ::HaveAPI::ModelAdapter.for(
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
      
 96 
     | 
    
         
            +
                    show_action.input.layout, @resource.model
         
     | 
| 
       97 
97 
     | 
    
         
             
                  ).input_clean(@resource.model, raw, @extra)
         
     | 
| 
       98 
98 
     | 
    
         
             
                end
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
         @@ -46,13 +46,13 @@ module HaveAPI::Parameters 
     | 
|
| 
       46 
46 
     | 
    
         | 
| 
       47 
47 
     | 
    
         
             
                def describe(context)
         
     | 
| 
       48 
48 
     | 
    
         
             
                  {
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
             
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
             
     | 
| 
      
 49 
     | 
    
         
            +
                    required: required?,
         
     | 
| 
      
 50 
     | 
    
         
            +
                    label: @label,
         
     | 
| 
      
 51 
     | 
    
         
            +
                    description: @desc,
         
     | 
| 
      
 52 
     | 
    
         
            +
                    type: @type ? @type.to_s : String.to_s,
         
     | 
| 
      
 53 
     | 
    
         
            +
                    validators: @validators ? @validators.describe : {},
         
     | 
| 
      
 54 
     | 
    
         
            +
                    default: @default,
         
     | 
| 
      
 55 
     | 
    
         
            +
                    protected: @protected || false,
         
     | 
| 
       56 
56 
     | 
    
         
             
                  }
         
     | 
| 
       57 
57 
     | 
    
         
             
                end
         
     | 
| 
       58 
58 
     | 
    
         | 
    
        data/lib/haveapi/params.rb
    CHANGED
    
    | 
         @@ -178,12 +178,14 @@ module HaveAPI 
     | 
|
| 
       178 
178 
     | 
    
         | 
| 
       179 
179 
     | 
    
         
             
                  if @direction == :input
         
     | 
| 
       180 
180 
     | 
    
         
             
                    ret[:parameters] = context.authorization.filter_input(
         
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
             
     | 
| 
      
 181 
     | 
    
         
            +
                      @params,
         
     | 
| 
      
 182 
     | 
    
         
            +
                      ModelAdapters::Hash.output(context, ret[:parameters])
         
     | 
| 
      
 183 
     | 
    
         
            +
                    )
         
     | 
| 
       183 
184 
     | 
    
         
             
                  else
         
     | 
| 
       184 
185 
     | 
    
         
             
                    ret[:parameters] = context.authorization.filter_output(
         
     | 
| 
       185 
     | 
    
         
            -
             
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
      
 186 
     | 
    
         
            +
                      @params,
         
     | 
| 
      
 187 
     | 
    
         
            +
                      ModelAdapters::Hash.output(context, ret[:parameters])
         
     | 
| 
      
 188 
     | 
    
         
            +
                    )
         
     | 
| 
       187 
189 
     | 
    
         
             
                  end
         
     | 
| 
       188 
190 
     | 
    
         | 
| 
       189 
191 
     | 
    
         
             
                  ret
         
     | 
| 
         @@ -205,11 +207,11 @@ module HaveAPI 
     | 
|
| 
       205 
207 
     | 
    
         
             
                  end
         
     | 
| 
       206 
208 
     | 
    
         | 
| 
       207 
209 
     | 
    
         
             
                  case layout
         
     | 
| 
       208 
     | 
    
         
            -
             
     | 
| 
       209 
     | 
    
         
            -
             
     | 
| 
      
 210 
     | 
    
         
            +
                  when :object, :hash
         
     | 
| 
      
 211 
     | 
    
         
            +
                    params[namespace] ||= {}
         
     | 
| 
       210 
212 
     | 
    
         | 
| 
       211 
     | 
    
         
            -
             
     | 
| 
       212 
     | 
    
         
            -
             
     | 
| 
      
 213 
     | 
    
         
            +
                  when :object_list, :hash_list
         
     | 
| 
      
 214 
     | 
    
         
            +
                    params[namespace] ||= []
         
     | 
| 
       213 
215 
     | 
    
         
             
                  end
         
     | 
| 
       214 
216 
     | 
    
         
             
                end
         
     | 
| 
       215 
217 
     | 
    
         | 
| 
         @@ -298,14 +300,14 @@ module HaveAPI 
     | 
|
| 
       298 
300 
     | 
    
         | 
| 
       299 
301 
     | 
    
         
             
                def valid_layout?(params)
         
     | 
| 
       300 
302 
     | 
    
         
             
                  case layout
         
     | 
| 
       301 
     | 
    
         
            -
             
     | 
| 
       302 
     | 
    
         
            -
             
     | 
| 
      
 303 
     | 
    
         
            +
                  when :object, :hash
         
     | 
| 
      
 304 
     | 
    
         
            +
                    params[namespace].is_a?(Hash)
         
     | 
| 
       303 
305 
     | 
    
         | 
| 
       304 
     | 
    
         
            -
             
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
      
 306 
     | 
    
         
            +
                  when :object_list, :hash_list
         
     | 
| 
      
 307 
     | 
    
         
            +
                    params[namespace].is_a?(Array)
         
     | 
| 
       306 
308 
     | 
    
         | 
| 
       307 
     | 
    
         
            -
             
     | 
| 
       308 
     | 
    
         
            -
             
     | 
| 
      
 309 
     | 
    
         
            +
                  else
         
     | 
| 
      
 310 
     | 
    
         
            +
                    false
         
     | 
| 
       309 
311 
     | 
    
         
             
                  end
         
     | 
| 
       310 
312 
     | 
    
         
             
                end
         
     | 
| 
       311 
313 
     | 
    
         | 
| 
         @@ -313,16 +315,16 @@ module HaveAPI 
     | 
|
| 
       313 
315 
     | 
    
         
             
                  ns = namespace
         
     | 
| 
       314 
316 
     | 
    
         | 
| 
       315 
317 
     | 
    
         
             
                  case layout
         
     | 
| 
       316 
     | 
    
         
            -
             
     | 
| 
       317 
     | 
    
         
            -
             
     | 
| 
      
 318 
     | 
    
         
            +
                  when :object, :hash
         
     | 
| 
      
 319 
     | 
    
         
            +
                    yield(ns ? params[namespace] : params)
         
     | 
| 
       318 
320 
     | 
    
         | 
| 
       319 
     | 
    
         
            -
             
     | 
| 
       320 
     | 
    
         
            -
             
     | 
| 
       321 
     | 
    
         
            -
             
     | 
| 
       322 
     | 
    
         
            -
             
     | 
| 
      
 321 
     | 
    
         
            +
                  when :object_list, :hash_list
         
     | 
| 
      
 322 
     | 
    
         
            +
                    (ns ? params[namespace] : params).each do |object|
         
     | 
| 
      
 323 
     | 
    
         
            +
                      yield(object)
         
     | 
| 
      
 324 
     | 
    
         
            +
                    end
         
     | 
| 
       323 
325 
     | 
    
         | 
| 
       324 
     | 
    
         
            -
             
     | 
| 
       325 
     | 
    
         
            -
             
     | 
| 
      
 326 
     | 
    
         
            +
                  else
         
     | 
| 
      
 327 
     | 
    
         
            +
                    false
         
     | 
| 
       326 
328 
     | 
    
         
             
                  end
         
     | 
| 
       327 
329 
     | 
    
         
             
                end
         
     | 
| 
       328 
330 
     | 
    
         | 
    
        data/lib/haveapi/resource.rb
    CHANGED
    
    | 
         @@ -59,20 +59,21 @@ module HaveAPI 
     | 
|
| 
       59 
59 
     | 
    
         
             
                  singular ? resource_name.singularize.underscore : resource_name.tableize
         
     | 
| 
       60 
60 
     | 
    
         
             
                end
         
     | 
| 
       61 
61 
     | 
    
         | 
| 
       62 
     | 
    
         
            -
                def self.routes(prefix='/')
         
     | 
| 
      
 62 
     | 
    
         
            +
                def self.routes(prefix='/', resource_path: [])
         
     | 
| 
       63 
63 
     | 
    
         
             
                  ret = []
         
     | 
| 
       64 
64 
     | 
    
         
             
                  prefix = "#{prefix}#{@route || rest_name}/"
         
     | 
| 
      
 65 
     | 
    
         
            +
                  new_resource_path = resource_path + [resource_name.underscore]
         
     | 
| 
       65 
66 
     | 
    
         | 
| 
       66 
67 
     | 
    
         
             
                  actions do |a|
         
     | 
| 
       67 
68 
     | 
    
         
             
                    # Call used_by for selected model adapters. It is safe to do
         
     | 
| 
       68 
69 
     | 
    
         
             
                    # only when all classes are loaded.
         
     | 
| 
       69 
70 
     | 
    
         
             
                    a.initialize
         
     | 
| 
       70 
71 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
                    ret << Route.new(a.build_route(prefix).chomp('/'), a)
         
     | 
| 
      
 72 
     | 
    
         
            +
                    ret << Route.new(a.build_route(prefix).chomp('/'), a, new_resource_path)
         
     | 
| 
       72 
73 
     | 
    
         
             
                  end
         
     | 
| 
       73 
74 
     | 
    
         | 
| 
       74 
75 
     | 
    
         
             
                  resources do |r|
         
     | 
| 
       75 
     | 
    
         
            -
                    ret << {r => r.routes(prefix)}
         
     | 
| 
      
 76 
     | 
    
         
            +
                    ret << {r => r.routes(prefix, resource_path: new_resource_path)}
         
     | 
| 
       76 
77 
     | 
    
         
             
                  end
         
     | 
| 
       77 
78 
     | 
    
         | 
| 
       78 
79 
     | 
    
         
             
                  ret
         
     | 
| 
         @@ -83,6 +84,9 @@ module HaveAPI 
     | 
|
| 
       83 
84 
     | 
    
         | 
| 
       84 
85 
     | 
    
         
             
                  context.resource = self
         
     | 
| 
       85 
86 
     | 
    
         | 
| 
      
 87 
     | 
    
         
            +
                  orig_resource_path = context.resource_path
         
     | 
| 
      
 88 
     | 
    
         
            +
                  context.resource_path = context.resource_path + [resource_name.underscore]
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       86 
90 
     | 
    
         
             
                  hash[:actions].each do |action, path|
         
     | 
| 
       87 
91 
     | 
    
         
             
                    context.action = action
         
     | 
| 
       88 
92 
     | 
    
         
             
                    context.path = path
         
     | 
| 
         @@ -97,6 +101,8 @@ module HaveAPI 
     | 
|
| 
       97 
101 
     | 
    
         
             
                    ret[:resources][resource.resource_name.underscore] = resource.describe(children, context)
         
     | 
| 
       98 
102 
     | 
    
         
             
                  end
         
     | 
| 
       99 
103 
     | 
    
         | 
| 
      
 104 
     | 
    
         
            +
                  context.resource_path = orig_resource_path
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
       100 
106 
     | 
    
         
             
                  ret
         
     | 
| 
       101 
107 
     | 
    
         
             
                end
         
     | 
| 
       102 
108 
     | 
    
         |