praxis 0.22.pre.2 → 2.0.pre.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/CHANGELOG.md +323 -324
- data/lib/praxis/action_definition.rb +7 -9
- data/lib/praxis/api_definition.rb +27 -44
- data/lib/praxis/api_general_info.rb +2 -3
- data/lib/praxis/application.rb +14 -141
- data/lib/praxis/bootloader.rb +1 -2
- data/lib/praxis/bootloader_stages/environment.rb +13 -0
- data/lib/praxis/controller.rb +0 -2
- data/lib/praxis/dispatcher.rb +4 -6
- data/lib/praxis/docs/generator.rb +8 -18
- data/lib/praxis/docs/link_builder.rb +1 -1
- data/lib/praxis/error_handler.rb +5 -5
- data/lib/praxis/extensions/attribute_filtering/active_record_filter_query_builder.rb +1 -1
- data/lib/praxis/extensions/attribute_filtering/sequel_filter_query_builder.rb +125 -0
- data/lib/praxis/extensions/field_selection/active_record_query_selector.rb +16 -18
- data/lib/praxis/extensions/field_selection/sequel_query_selector.rb +5 -5
- data/lib/praxis/extensions/field_selection.rb +1 -12
- data/lib/praxis/extensions/rendering.rb +1 -1
- data/lib/praxis/file_group.rb +1 -1
- data/lib/praxis/handlers/xml.rb +1 -1
- data/lib/praxis/mapper/active_model_compat.rb +63 -0
- data/lib/praxis/mapper/resource.rb +242 -0
- data/lib/praxis/mapper/selector_generator.rb +126 -0
- data/lib/praxis/mapper/sequel_compat.rb +37 -0
- data/lib/praxis/middleware_app.rb +13 -15
- data/lib/praxis/multipart/part.rb +3 -5
- data/lib/praxis/plugins/mapper_plugin.rb +50 -0
- data/lib/praxis/request.rb +14 -7
- data/lib/praxis/request_stages/response.rb +2 -3
- data/lib/praxis/resource_definition.rb +10 -14
- data/lib/praxis/response.rb +6 -5
- data/lib/praxis/response_definition.rb +5 -7
- data/lib/praxis/response_template.rb +3 -4
- data/lib/praxis/responses/http.rb +36 -0
- data/lib/praxis/responses/internal_server_error.rb +12 -3
- data/lib/praxis/responses/multipart_ok.rb +11 -4
- data/lib/praxis/responses/validation_error.rb +10 -1
- data/lib/praxis/router.rb +3 -3
- data/lib/praxis/tasks/api_docs.rb +2 -10
- data/lib/praxis/tasks/routes.rb +0 -1
- data/lib/praxis/version.rb +1 -1
- data/lib/praxis.rb +13 -9
- data/praxis.gemspec +2 -3
- data/spec/functional_spec.rb +0 -1
- data/spec/praxis/action_definition_spec.rb +15 -26
- data/spec/praxis/api_definition_spec.rb +8 -13
- data/spec/praxis/api_general_info_spec.rb +8 -3
- data/spec/praxis/application_spec.rb +7 -13
- data/spec/praxis/handlers/xml_spec.rb +2 -2
- data/spec/praxis/mapper/resource_spec.rb +169 -0
- data/spec/praxis/mapper/selector_generator_spec.rb +301 -0
- data/spec/praxis/middleware_app_spec.rb +15 -9
- data/spec/praxis/request_spec.rb +7 -17
- data/spec/praxis/request_stages/validate_spec.rb +1 -1
- data/spec/praxis/resource_definition_spec.rb +10 -12
- data/spec/praxis/response_definition_spec.rb +5 -22
- data/spec/praxis/response_spec.rb +5 -12
- data/spec/praxis/responses/internal_server_error_spec.rb +5 -2
- data/spec/praxis/router_spec.rb +4 -8
- data/spec/spec_app/app/models/person.rb +3 -3
- data/spec/spec_app/config/environment.rb +3 -21
- data/spec/spec_app/config.ru +6 -1
- data/spec/spec_helper.rb +2 -17
- data/spec/support/spec_resources.rb +131 -0
- metadata +19 -31
- data/lib/praxis/extensions/attribute_filtering/query_builder.rb +0 -39
- data/lib/praxis/extensions/attribute_filtering.rb +0 -28
- data/lib/praxis/extensions/mapper_selectors.rb +0 -16
- data/lib/praxis/media_type_collection.rb +0 -127
- data/lib/praxis/plugins/praxis_mapper_plugin.rb +0 -246
- data/spec/praxis/media_type_collection_spec.rb +0 -157
- data/spec/praxis/plugins/praxis_mapper_plugin_spec.rb +0 -142
| @@ -1,246 +0,0 @@ | |
| 1 | 
            -
            require 'praxis-mapper'
         | 
| 2 | 
            -
            require 'singleton'
         | 
| 3 | 
            -
             | 
| 4 | 
            -
            require 'terminal-table'
         | 
| 5 | 
            -
             | 
| 6 | 
            -
            # Plugin for applications which use the 'praxis-mapper' gem.
         | 
| 7 | 
            -
            #
         | 
| 8 | 
            -
            # This plugin provides the following features:
         | 
| 9 | 
            -
            #   1. Sets up the PraxisMapper::IdentityMap for your application and assigns
         | 
| 10 | 
            -
            #      it to the controller's request.identity_map for access from your
         | 
| 11 | 
            -
            #      application.
         | 
| 12 | 
            -
            #   2. Connects to your database and dumps a log of database interaction stats
         | 
| 13 | 
            -
            #      (if enabled via the :log_stats option).
         | 
| 14 | 
            -
            #
         | 
| 15 | 
            -
            # This plugin accepts one of the following options:
         | 
| 16 | 
            -
            #   1. config_file: A String indicating the path where this plugin's config
         | 
| 17 | 
            -
            #      file exists.
         | 
| 18 | 
            -
            #   2. config_data: A Hash of data that is merged into the YAML hash loaded
         | 
| 19 | 
            -
            #      from config_file.
         | 
| 20 | 
            -
            #
         | 
| 21 | 
            -
            # The config_data Hash contains the following keys:
         | 
| 22 | 
            -
            #   1. repositories: A Hash containing the configs for the database repositories
         | 
| 23 | 
            -
            #      queried through praxis-mapper. This parameter is a Hash where a key is
         | 
| 24 | 
            -
            #      the identifier for a repository and the value is the options one
         | 
| 25 | 
            -
            #      would give to the 'sequel' gem. For example:
         | 
| 26 | 
            -
            #           repositories: {
         | 
| 27 | 
            -
            #             default: {
         | 
| 28 | 
            -
            #               host: 127.0.0.1,
         | 
| 29 | 
            -
            #               username: root,
         | 
| 30 | 
            -
            #               password: nil,
         | 
| 31 | 
            -
            #               database: myapp_dev,
         | 
| 32 | 
            -
            #               adapter: mysql2
         | 
| 33 | 
            -
            #             }
         | 
| 34 | 
            -
            #           }
         | 
| 35 | 
            -
            #   2. log_stats: A String indicating what kind of DB stats you would like
         | 
| 36 | 
            -
            #      output into the Praxis::Application.current_instance.logger app log. Possible
         | 
| 37 | 
            -
            #      values are: "detailed", "short", and "skip" (i.e. do not print the stats
         | 
| 38 | 
            -
            #      at all).
         | 
| 39 | 
            -
            #   3. stats_log_level: the logging level with which the statistics should be logged.
         | 
| 40 | 
            -
            #
         | 
| 41 | 
            -
            # See http://praxis-framework.io/reference/plugins/ for further details on how
         | 
| 42 | 
            -
            # to use a plugin and pass it options.
         | 
| 43 | 
            -
            #
         | 
| 44 | 
            -
            module Praxis
         | 
| 45 | 
            -
              module Plugins
         | 
| 46 | 
            -
                module PraxisMapperPlugin
         | 
| 47 | 
            -
                  include Praxis::PluginConcern
         | 
| 48 | 
            -
             | 
| 49 | 
            -
                  class RepositoryConfig < Attributor::Hash
         | 
| 50 | 
            -
                    self.key_type = String
         | 
| 51 | 
            -
             | 
| 52 | 
            -
                    keys allow_extra: true do
         | 
| 53 | 
            -
                      key 'type', String, default: 'sequel'
         | 
| 54 | 
            -
                      extra 'connection_settings'
         | 
| 55 | 
            -
                    end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                  end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
                  class Plugin < Praxis::Plugin
         | 
| 61 | 
            -
                    include Singleton
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    def initialize
         | 
| 64 | 
            -
                      @options = {
         | 
| 65 | 
            -
                        config_file: 'config/praxis_mapper.yml',
         | 
| 66 | 
            -
                        config_data: {
         | 
| 67 | 
            -
                          repositories: {}
         | 
| 68 | 
            -
                        }
         | 
| 69 | 
            -
                      }
         | 
| 70 | 
            -
                    end
         | 
| 71 | 
            -
             | 
| 72 | 
            -
                    def config_key
         | 
| 73 | 
            -
                      :praxis_mapper
         | 
| 74 | 
            -
                    end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                    def prepare_config!(node)
         | 
| 77 | 
            -
                      node.attributes do
         | 
| 78 | 
            -
                        attribute :log_stats, String, values: ['detailed', 'short', 'skip'], default: 'detailed'
         | 
| 79 | 
            -
                        attribute :stats_log_level, Symbol, values: [:fatal,:error,:warn,:info,:debug], default: :info
         | 
| 80 | 
            -
                        attribute :repositories, Attributor::Hash.of(key: String, value: RepositoryConfig)
         | 
| 81 | 
            -
                      end
         | 
| 82 | 
            -
                    end
         | 
| 83 | 
            -
             | 
| 84 | 
            -
                    # Make our own custom load_config! method
         | 
| 85 | 
            -
                    def load_config!
         | 
| 86 | 
            -
                      config_file_path = application.root + options[:config_file]
         | 
| 87 | 
            -
                      result = config_file_path.exist? ? YAML.load_file(config_file_path) : {}
         | 
| 88 | 
            -
                      result.merge(@options[:config_data])
         | 
| 89 | 
            -
                    end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                    def setup!
         | 
| 92 | 
            -
                      self.config.repositories.each do |repository_name, repository_config|
         | 
| 93 | 
            -
                        type = repository_config['type']
         | 
| 94 | 
            -
                        connection_settings = repository_config['connection_settings']
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                        case type
         | 
| 97 | 
            -
                        when 'sequel'
         | 
| 98 | 
            -
                          self.setup_sequel_repository(repository_name, connection_settings)
         | 
| 99 | 
            -
                        else
         | 
| 100 | 
            -
                          raise "unsupported repository type: #{type}"
         | 
| 101 | 
            -
                        end
         | 
| 102 | 
            -
                      end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
                      log_stats = PraxisMapperPlugin::Plugin.instance.config.log_stats
         | 
| 105 | 
            -
                      unless log_stats == 'skip'
         | 
| 106 | 
            -
                        Praxis::Notifications.subscribe 'praxis.request.all' do |name, *junk, payload|
         | 
| 107 | 
            -
                          if (payload[:request].identity_map?)
         | 
| 108 | 
            -
                            identity_map = payload[:request].identity_map
         | 
| 109 | 
            -
                            PraxisMapperPlugin::Statistics.log(payload[:request], identity_map, log_stats)
         | 
| 110 | 
            -
                          end
         | 
| 111 | 
            -
                        end
         | 
| 112 | 
            -
                      end
         | 
| 113 | 
            -
                    end
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                    def setup_sequel_repository(name, settings)
         | 
| 116 | 
            -
                      db = Sequel.connect(settings.dump.symbolize_keys)
         | 
| 117 | 
            -
             | 
| 118 | 
            -
                      Praxis::Mapper::ConnectionManager.setup do
         | 
| 119 | 
            -
                        repository(name.to_sym) { db }
         | 
| 120 | 
            -
                      end
         | 
| 121 | 
            -
                    end
         | 
| 122 | 
            -
                  end
         | 
| 123 | 
            -
             | 
| 124 | 
            -
                  module Request
         | 
| 125 | 
            -
                    def identity_map
         | 
| 126 | 
            -
                      @identity_map ||= Praxis::Mapper::IdentityMap.new
         | 
| 127 | 
            -
                    end
         | 
| 128 | 
            -
             | 
| 129 | 
            -
                    def identity_map=(map)
         | 
| 130 | 
            -
                      @identity_map = map
         | 
| 131 | 
            -
                    end
         | 
| 132 | 
            -
             | 
| 133 | 
            -
                    def identity_map?
         | 
| 134 | 
            -
                      !@identity_map.nil?
         | 
| 135 | 
            -
                    end
         | 
| 136 | 
            -
             | 
| 137 | 
            -
                    def silence_mapper_stats
         | 
| 138 | 
            -
                      @silence_mapper_stats
         | 
| 139 | 
            -
                    end
         | 
| 140 | 
            -
             | 
| 141 | 
            -
                    def silence_mapper_stats=(value)
         | 
| 142 | 
            -
                      @silence_mapper_stats = value
         | 
| 143 | 
            -
                    end
         | 
| 144 | 
            -
             | 
| 145 | 
            -
                  end
         | 
| 146 | 
            -
             | 
| 147 | 
            -
                  module Controller
         | 
| 148 | 
            -
                    extend ActiveSupport::Concern
         | 
| 149 | 
            -
             | 
| 150 | 
            -
                    included do
         | 
| 151 | 
            -
                      # Ensure we call #release on any identity map
         | 
| 152 | 
            -
                      # that may be set by the controller after the action
         | 
| 153 | 
            -
                      # completes.
         | 
| 154 | 
            -
                      around :action do |controller, callee|
         | 
| 155 | 
            -
                        begin
         | 
| 156 | 
            -
                          callee.call
         | 
| 157 | 
            -
                        ensure
         | 
| 158 | 
            -
                          if controller.request.identity_map?
         | 
| 159 | 
            -
                            controller.request.identity_map.release
         | 
| 160 | 
            -
                          end
         | 
| 161 | 
            -
                        end
         | 
| 162 | 
            -
                      end
         | 
| 163 | 
            -
                    end
         | 
| 164 | 
            -
             | 
| 165 | 
            -
                    def identity_map
         | 
| 166 | 
            -
                      request.identity_map
         | 
| 167 | 
            -
                    end
         | 
| 168 | 
            -
             | 
| 169 | 
            -
                  end
         | 
| 170 | 
            -
             | 
| 171 | 
            -
                  module Statistics
         | 
| 172 | 
            -
             | 
| 173 | 
            -
                    def self.log(request, identity_map, log_stats)
         | 
| 174 | 
            -
                      return if identity_map.nil?
         | 
| 175 | 
            -
                      return if request.silence_mapper_stats == true
         | 
| 176 | 
            -
                      if identity_map.queries.empty?
         | 
| 177 | 
            -
                        self.to_logger "No database interactions observed."
         | 
| 178 | 
            -
                        return
         | 
| 179 | 
            -
                      end
         | 
| 180 | 
            -
             | 
| 181 | 
            -
             | 
| 182 | 
            -
                      case log_stats
         | 
| 183 | 
            -
                      when 'detailed'
         | 
| 184 | 
            -
                        self.detailed(identity_map)
         | 
| 185 | 
            -
                      when 'short'
         | 
| 186 | 
            -
                        self.short(identity_map)
         | 
| 187 | 
            -
                      when 'skip'
         | 
| 188 | 
            -
                        # Shouldn't receive this. But anyway...no-op.
         | 
| 189 | 
            -
                      end
         | 
| 190 | 
            -
                    end
         | 
| 191 | 
            -
             | 
| 192 | 
            -
                    def self.detailed(identity_map)
         | 
| 193 | 
            -
                      stats_by_model = identity_map.query_statistics.sum_totals_by_model
         | 
| 194 | 
            -
                      stats_total = identity_map.query_statistics.sum_totals
         | 
| 195 | 
            -
                      fields = [ :query_count, :records_loaded, :datastore_interactions, :datastore_interaction_time]
         | 
| 196 | 
            -
                      rows = []
         | 
| 197 | 
            -
             | 
| 198 | 
            -
                      total_models_loaded = 0
         | 
| 199 | 
            -
                      # stats per model
         | 
| 200 | 
            -
                      stats_by_model.each do |model, totals|
         | 
| 201 | 
            -
                        total_values = totals.values_at(*fields)
         | 
| 202 | 
            -
                        self.round_fields_at( total_values , [fields.index(:datastore_interaction_time)])
         | 
| 203 | 
            -
                        row = [ model ] + total_values
         | 
| 204 | 
            -
                        models_loaded = identity_map.all(model).size
         | 
| 205 | 
            -
                        total_models_loaded += models_loaded
         | 
| 206 | 
            -
                        row << models_loaded
         | 
| 207 | 
            -
                        rows << row
         | 
| 208 | 
            -
                      end
         | 
| 209 | 
            -
             | 
| 210 | 
            -
                      rows << :separator
         | 
| 211 | 
            -
             | 
| 212 | 
            -
                      # totals for all models
         | 
| 213 | 
            -
                      stats_total_values = stats_total.values_at(*fields)
         | 
| 214 | 
            -
                      self.round_fields_at(stats_total_values , [fields.index(:datastore_interaction_time)])
         | 
| 215 | 
            -
                      rows << [ "All Models" ] + stats_total_values + [total_models_loaded]
         | 
| 216 | 
            -
             | 
| 217 | 
            -
                      table = Terminal::Table.new \
         | 
| 218 | 
            -
                        :rows => rows,
         | 
| 219 | 
            -
                        :title => "Praxis::Mapper Statistics",
         | 
| 220 | 
            -
                        :headings => [ "Model", "# Queries", "Records Loaded", "Interactions", "Time(sec)", "Models Loaded" ]
         | 
| 221 | 
            -
             | 
| 222 | 
            -
                      table.align_column(1, :right)
         | 
| 223 | 
            -
                      table.align_column(2, :right)
         | 
| 224 | 
            -
                      table.align_column(3, :right)
         | 
| 225 | 
            -
                      table.align_column(4, :right)
         | 
| 226 | 
            -
                      table.align_column(5, :right)
         | 
| 227 | 
            -
                      self.to_logger "\n#{table.to_s}"
         | 
| 228 | 
            -
                    end
         | 
| 229 | 
            -
             | 
| 230 | 
            -
                    def self.round_fields_at(values, indices)
         | 
| 231 | 
            -
                      indices.each do |idx|
         | 
| 232 | 
            -
                        values[idx] = "%.3f" % values[idx]
         | 
| 233 | 
            -
                      end
         | 
| 234 | 
            -
                    end
         | 
| 235 | 
            -
             | 
| 236 | 
            -
                    def self.short(identity_map)
         | 
| 237 | 
            -
                      self.to_logger identity_map.query_statistics.sum_totals.to_s
         | 
| 238 | 
            -
                    end
         | 
| 239 | 
            -
             | 
| 240 | 
            -
                    def self.to_logger(message)
         | 
| 241 | 
            -
                        Praxis::Application.current_instance.logger.__send__(Plugin.instance.config.stats_log_level, "Praxis::Mapper Statistics: #{message}")
         | 
| 242 | 
            -
                    end
         | 
| 243 | 
            -
                  end
         | 
| 244 | 
            -
                end
         | 
| 245 | 
            -
              end
         | 
| 246 | 
            -
            end
         | 
| @@ -1,157 +0,0 @@ | |
| 1 | 
            -
            require "spec_helper"
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe Praxis::MediaTypeCollection do
         | 
| 4 | 
            -
             | 
| 5 | 
            -
              subject!(:media_type_collection) do
         | 
| 6 | 
            -
                silence_warnings do
         | 
| 7 | 
            -
                  klass = Class.new(Praxis::MediaTypeCollection) do
         | 
| 8 | 
            -
                    member_type VolumeSnapshot
         | 
| 9 | 
            -
                    description 'A container for a collection of Volumes'
         | 
| 10 | 
            -
                    display_name 'Volumes Collection'
         | 
| 11 | 
            -
             | 
| 12 | 
            -
                    attributes do
         | 
| 13 | 
            -
                      attribute :name, String, regexp: /snapshots-(\w+)/
         | 
| 14 | 
            -
                      attribute :size, Integer
         | 
| 15 | 
            -
                      attribute :href, String
         | 
| 16 | 
            -
                    end
         | 
| 17 | 
            -
             | 
| 18 | 
            -
                    view :link do
         | 
| 19 | 
            -
                      attribute :name
         | 
| 20 | 
            -
                      attribute :size
         | 
| 21 | 
            -
                      attribute :href
         | 
| 22 | 
            -
                    end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
                    member_view :default, using: :default
         | 
| 25 | 
            -
                  end
         | 
| 26 | 
            -
             | 
| 27 | 
            -
                  klass.finalize!
         | 
| 28 | 
            -
                  klass
         | 
| 29 | 
            -
                end
         | 
| 30 | 
            -
              end
         | 
| 31 | 
            -
             | 
| 32 | 
            -
              context '.member_type' do
         | 
| 33 | 
            -
                its(:member_type){ should be(VolumeSnapshot) }
         | 
| 34 | 
            -
                its(:member_attribute){ should be_kind_of(Attributor::Attribute) }
         | 
| 35 | 
            -
                its('member_attribute.type'){ should be(VolumeSnapshot) }
         | 
| 36 | 
            -
              end
         | 
| 37 | 
            -
             | 
| 38 | 
            -
              context '.load' do
         | 
| 39 | 
            -
                context 'with a hash' do
         | 
| 40 | 
            -
                  let(:snapshots_data) { {name: 'snapshots',   href: '/bob/snapshots' } }
         | 
| 41 | 
            -
                  subject(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 42 | 
            -
             | 
| 43 | 
            -
                   its(:name) { should eq(snapshots_data[:name]) }
         | 
| 44 | 
            -
                   its(:href) { should eq(snapshots_data[:href]) }
         | 
| 45 | 
            -
             | 
| 46 | 
            -
                   it 'has no members set' do
         | 
| 47 | 
            -
                     expect(snapshots.to_a).to eq([])
         | 
| 48 | 
            -
                   end
         | 
| 49 | 
            -
                 end
         | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
                context 'loading an array' do
         | 
| 53 | 
            -
                  let(:snapshots_data) do
         | 
| 54 | 
            -
                    [{id: 1, name: 'snapshot-1'},
         | 
| 55 | 
            -
                     {id: 2, name: 'snapshot-2'}]
         | 
| 56 | 
            -
                  end
         | 
| 57 | 
            -
             | 
| 58 | 
            -
                  let(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 59 | 
            -
                  subject(:members) { snapshots.to_a }
         | 
| 60 | 
            -
             | 
| 61 | 
            -
                  it 'sets the collection members' do
         | 
| 62 | 
            -
                    expect(members).to have(2).items
         | 
| 63 | 
            -
             | 
| 64 | 
            -
                    expect(members[0].id).to eq(1)
         | 
| 65 | 
            -
                    expect(members[0].name).to eq('snapshot-1')
         | 
| 66 | 
            -
                    expect(members[1].id).to eq(2)
         | 
| 67 | 
            -
                    expect(members[1].name).to eq('snapshot-2')
         | 
| 68 | 
            -
                  end
         | 
| 69 | 
            -
             | 
| 70 | 
            -
                  it 'has no attributes set' do
         | 
| 71 | 
            -
                    expect(snapshots.name).to be(nil)
         | 
| 72 | 
            -
                    expect(snapshots.size).to be(nil)
         | 
| 73 | 
            -
                    expect(snapshots.href).to be(nil)
         | 
| 74 | 
            -
                  end
         | 
| 75 | 
            -
             | 
| 76 | 
            -
                end
         | 
| 77 | 
            -
              end
         | 
| 78 | 
            -
             | 
| 79 | 
            -
              context '#render' do
         | 
| 80 | 
            -
                context 'for standard views' do
         | 
| 81 | 
            -
                  let(:snapshots_data) { {name: 'snapshots',   href: '/bob/snapshots' } }
         | 
| 82 | 
            -
                  let(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 83 | 
            -
                  subject(:output) { snapshots.render(view: :link) }
         | 
| 84 | 
            -
             | 
| 85 | 
            -
                  its([:name]) { should eq(snapshots.name)}
         | 
| 86 | 
            -
                  its([:size]) { should eq(snapshots.size)}
         | 
| 87 | 
            -
                  its([:href]) { should eq(snapshots.href)}
         | 
| 88 | 
            -
                end
         | 
| 89 | 
            -
             | 
| 90 | 
            -
                context 'for members' do
         | 
| 91 | 
            -
                  let(:snapshots_data) do
         | 
| 92 | 
            -
                    [{id: 1, name: 'snapshot-1'},
         | 
| 93 | 
            -
                     {id: 2, name: 'snapshot-2'}]
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
             | 
| 96 | 
            -
                  let(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                  subject(:output) { media_type_collection.dump(snapshots, view: :default) }
         | 
| 99 | 
            -
             | 
| 100 | 
            -
                  it { should eq(snapshots.collect(&:render)) }
         | 
| 101 | 
            -
                end
         | 
| 102 | 
            -
             | 
| 103 | 
            -
              end
         | 
| 104 | 
            -
             | 
| 105 | 
            -
              context '#validate' do
         | 
| 106 | 
            -
             | 
| 107 | 
            -
             | 
| 108 | 
            -
                context 'with a hash' do
         | 
| 109 | 
            -
                  let(:snapshots_data) { {name: 'snapshots-1',   href: '/bob/snapshots' } }
         | 
| 110 | 
            -
                  subject(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 111 | 
            -
             | 
| 112 | 
            -
             | 
| 113 | 
            -
                  it 'validates' do
         | 
| 114 | 
            -
                    expect(snapshots.validate).to be_empty
         | 
| 115 | 
            -
                  end
         | 
| 116 | 
            -
             | 
| 117 | 
            -
                  context 'with invalid attributes' do
         | 
| 118 | 
            -
                    let(:snapshots_data) { {name: 'notsnapshots',   href: '/bob/snapshots' } }
         | 
| 119 | 
            -
                    it 'returns the error' do
         | 
| 120 | 
            -
                      expect(snapshots.validate).to have(1).item
         | 
| 121 | 
            -
                      expect(snapshots.validate[0]).to match(/value \(notsnapshots\) does not match regexp/)
         | 
| 122 | 
            -
                    end
         | 
| 123 | 
            -
                  end
         | 
| 124 | 
            -
                end
         | 
| 125 | 
            -
             | 
| 126 | 
            -
                context 'for an array' do
         | 
| 127 | 
            -
                  let(:snapshots_data) do
         | 
| 128 | 
            -
                    [{id: 1, name: 'snapshot-1'},
         | 
| 129 | 
            -
                     {id: 2, name: 'snapshot-2'}]
         | 
| 130 | 
            -
                  end
         | 
| 131 | 
            -
             | 
| 132 | 
            -
                  subject(:snapshots) { media_type_collection.load(snapshots_data) }
         | 
| 133 | 
            -
             | 
| 134 | 
            -
                  it 'validates' do
         | 
| 135 | 
            -
                    expect(snapshots.validate).to be_empty
         | 
| 136 | 
            -
                  end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                  context 'with invalid members' do
         | 
| 139 | 
            -
                    let(:snapshots_data) do
         | 
| 140 | 
            -
                      [{id: 1, name: 'invalid-1'},
         | 
| 141 | 
            -
                       {id: 2, name: 'snapshot-2'}]
         | 
| 142 | 
            -
                    end
         | 
| 143 | 
            -
             | 
| 144 | 
            -
                    it 'returns the error' do
         | 
| 145 | 
            -
                      expect(snapshots.validate).to have(1).item
         | 
| 146 | 
            -
                      expect(snapshots.validate[0]).to match(/value \(invalid-1\) does not match regexp/)
         | 
| 147 | 
            -
                    end
         | 
| 148 | 
            -
                  end
         | 
| 149 | 
            -
                end
         | 
| 150 | 
            -
              end
         | 
| 151 | 
            -
             | 
| 152 | 
            -
              context '#describe' do
         | 
| 153 | 
            -
                subject(:described) { media_type_collection.describe }
         | 
| 154 | 
            -
                its([:description]){ should be(media_type_collection.description)}
         | 
| 155 | 
            -
                its([:display_name]){ should be(media_type_collection.display_name)}
         | 
| 156 | 
            -
              end
         | 
| 157 | 
            -
            end
         | 
| @@ -1,142 +0,0 @@ | |
| 1 | 
            -
            require 'spec_helper'
         | 
| 2 | 
            -
             | 
| 3 | 
            -
            describe Praxis::Plugins::PraxisMapperPlugin do
         | 
| 4 | 
            -
             | 
| 5 | 
            -
              subject(:plugin) { Praxis::Plugins::PraxisMapperPlugin::Plugin.instance }
         | 
| 6 | 
            -
              let(:config) { plugin.config }
         | 
| 7 | 
            -
              context 'Plugin' do
         | 
| 8 | 
            -
                context 'configuration' do
         | 
| 9 | 
            -
                  subject { config }
         | 
| 10 | 
            -
                  its(:log_stats) { should eq 'detailed' }
         | 
| 11 | 
            -
                  its(:stats_log_level) { should eq :info }
         | 
| 12 | 
            -
                  its(:repositories) { should have_key("default") }
         | 
| 13 | 
            -
             | 
| 14 | 
            -
                  context 'default repository' do
         | 
| 15 | 
            -
                    subject(:default) { config.repositories['default'] }
         | 
| 16 | 
            -
                    its(['type']) { should eq 'sequel' }
         | 
| 17 | 
            -
                    it 'has the right connection settings' do
         | 
| 18 | 
            -
                      if RUBY_PLATFORM !~ /java/
         | 
| 19 | 
            -
                        expect(subject['connection_settings'].dump).to eq( 'adapter' => 'sqlite','database' => ':memory:' )
         | 
| 20 | 
            -
                      else
         | 
| 21 | 
            -
                        expect(subject['connection_settings'].dump).to eq( 'adapter' => 'jdbc', 'uri' => 'jdbc:sqlite::memory:' )
         | 
| 22 | 
            -
                      end
         | 
| 23 | 
            -
             | 
| 24 | 
            -
             | 
| 25 | 
            -
                    end
         | 
| 26 | 
            -
                  end
         | 
| 27 | 
            -
             | 
| 28 | 
            -
                end
         | 
| 29 | 
            -
              end
         | 
| 30 | 
            -
             | 
| 31 | 
            -
              context 'Request' do
         | 
| 32 | 
            -
             | 
| 33 | 
            -
                it 'should have identity_map accessors' do
         | 
| 34 | 
            -
                  expect(Praxis::Plugins::PraxisMapperPlugin::Request.instance_methods).to include(:identity_map,:identity_map=)
         | 
| 35 | 
            -
                end
         | 
| 36 | 
            -
             | 
| 37 | 
            -
                it 'should have silence_mapper_stats accessors' do
         | 
| 38 | 
            -
                  expect(Praxis::Plugins::PraxisMapperPlugin::Request.instance_methods)
         | 
| 39 | 
            -
                        .to include(:silence_mapper_stats,:silence_mapper_stats=)
         | 
| 40 | 
            -
                end
         | 
| 41 | 
            -
              end
         | 
| 42 | 
            -
              context 'functional test' do
         | 
| 43 | 
            -
             | 
| 44 | 
            -
                def app
         | 
| 45 | 
            -
                  Praxis::Application.instance
         | 
| 46 | 
            -
                end
         | 
| 47 | 
            -
             | 
| 48 | 
            -
                let(:session) { double("session", valid?: true)}
         | 
| 49 | 
            -
             | 
| 50 | 
            -
                around(:each) do |example|
         | 
| 51 | 
            -
                  orig_level = Praxis::Application.instance.logger.level
         | 
| 52 | 
            -
                  Praxis::Application.instance.logger.level = 2
         | 
| 53 | 
            -
                  example.run
         | 
| 54 | 
            -
                  Praxis::Application.instance.logger.level = orig_level
         | 
| 55 | 
            -
                end
         | 
| 56 | 
            -
             | 
| 57 | 
            -
                context 'with no identity_map set in the request' do
         | 
| 58 | 
            -
                  it 'does not log stats' do
         | 
| 59 | 
            -
                    expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:log)
         | 
| 60 | 
            -
                    the_body = StringIO.new("{}") # This is a funny, GET request expecting a body
         | 
| 61 | 
            -
                    get '/api/clouds/1/instances/2?api_version=1.0', nil, 'rack.input' => the_body,'CONTENT_TYPE' => "application/json", 'global_session' => session
         | 
| 62 | 
            -
             | 
| 63 | 
            -
                    expect(last_response.status).to eq(200)
         | 
| 64 | 
            -
                  end
         | 
| 65 | 
            -
                end
         | 
| 66 | 
            -
                context 'with an identity_map set in the request' do
         | 
| 67 | 
            -
                  it 'logs stats' do
         | 
| 68 | 
            -
                    expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:log).
         | 
| 69 | 
            -
                      with(kind_of(Praxis::Request),kind_of(Praxis::Mapper::IdentityMap), 'detailed').
         | 
| 70 | 
            -
                      and_call_original
         | 
| 71 | 
            -
                    the_body = StringIO.new("{}") # This is a funny, GET request expecting a body
         | 
| 72 | 
            -
                    get '/api/clouds/1/instances/2?create_identity_map=true&api_version=1.0', nil, 'rack.input' => the_body,'CONTENT_TYPE' => "application/json", 'global_session' => session
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                    expect(last_response.status).to eq(200)
         | 
| 75 | 
            -
                  end
         | 
| 76 | 
            -
                end
         | 
| 77 | 
            -
             | 
| 78 | 
            -
              end
         | 
| 79 | 
            -
             | 
| 80 | 
            -
              context 'Statistics' do
         | 
| 81 | 
            -
                context '.log' do
         | 
| 82 | 
            -
                  let(:queries){ { some: :queries } }
         | 
| 83 | 
            -
                  let(:identity_map) { double('identity_map', queries: queries) }
         | 
| 84 | 
            -
                  let(:log_stats){ 'detailed' }
         | 
| 85 | 
            -
                  let(:request){ double('request', silence_mapper_stats: false ) }
         | 
| 86 | 
            -
             | 
| 87 | 
            -
                  after do
         | 
| 88 | 
            -
                    Praxis::Plugins::PraxisMapperPlugin::Statistics.log(request, identity_map, log_stats)
         | 
| 89 | 
            -
                  end
         | 
| 90 | 
            -
             | 
| 91 | 
            -
                  context 'when the request silences mapper stats' do
         | 
| 92 | 
            -
                    let(:request){ double('request', silence_mapper_stats: true ) }
         | 
| 93 | 
            -
                    it 'should not log anything' do
         | 
| 94 | 
            -
                      expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
         | 
| 95 | 
            -
                    end
         | 
| 96 | 
            -
                  end
         | 
| 97 | 
            -
             | 
| 98 | 
            -
                  context 'without the request silencing mapper stats' do
         | 
| 99 | 
            -
                    context 'when log_stats = detailed' do
         | 
| 100 | 
            -
                      it 'should call the detailed method' do
         | 
| 101 | 
            -
                         expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:detailed).with(identity_map)
         | 
| 102 | 
            -
                       end
         | 
| 103 | 
            -
                    end
         | 
| 104 | 
            -
             | 
| 105 | 
            -
                    context 'when log_stats = short' do
         | 
| 106 | 
            -
                      let(:log_stats){ 'short' }
         | 
| 107 | 
            -
                      it 'should call the short method' do
         | 
| 108 | 
            -
                         expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:short).with(identity_map)
         | 
| 109 | 
            -
                       end
         | 
| 110 | 
            -
                    end
         | 
| 111 | 
            -
             | 
| 112 | 
            -
                    context 'when log_stats = skip' do
         | 
| 113 | 
            -
                      let(:log_stats){ 'skip' }
         | 
| 114 | 
            -
             | 
| 115 | 
            -
                      it 'should not log anything' do
         | 
| 116 | 
            -
                        expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
         | 
| 117 | 
            -
                      end
         | 
| 118 | 
            -
                    end
         | 
| 119 | 
            -
             | 
| 120 | 
            -
                    context 'when there is no identity map' do
         | 
| 121 | 
            -
                      let(:identity_map) { nil }
         | 
| 122 | 
            -
                      it 'should not log anything' do
         | 
| 123 | 
            -
                        expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to_not receive(:to_logger)
         | 
| 124 | 
            -
                      end
         | 
| 125 | 
            -
                    end
         | 
| 126 | 
            -
             | 
| 127 | 
            -
                    context 'when no queries are logged in the identity map' do
         | 
| 128 | 
            -
                      let(:queries){ {} }
         | 
| 129 | 
            -
                      it 'should log a special message' do
         | 
| 130 | 
            -
                        expect(Praxis::Plugins::PraxisMapperPlugin::Statistics).to receive(:to_logger)
         | 
| 131 | 
            -
                                                                               .with("No database interactions observed.")
         | 
| 132 | 
            -
                      end
         | 
| 133 | 
            -
                    end
         | 
| 134 | 
            -
             | 
| 135 | 
            -
                  end
         | 
| 136 | 
            -
                end
         | 
| 137 | 
            -
             | 
| 138 | 
            -
                it 'has specs for testing the detailed log output'
         | 
| 139 | 
            -
                it 'has specs for testing the short log output'
         | 
| 140 | 
            -
              end
         | 
| 141 | 
            -
             | 
| 142 | 
            -
            end
         |