rom-sql 2.0.0.beta3 → 2.0.0.rc1
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 +9 -0
- data/lib/rom/plugins/relation/sql/auto_restrictions.rb +1 -0
- data/lib/rom/plugins/relation/sql/postgres/explain.rb +2 -0
- data/lib/rom/sql/attribute.rb +10 -3
- data/lib/rom/sql/commands/error_wrapper.rb +4 -0
- data/lib/rom/sql/dsl.rb +11 -0
- data/lib/rom/sql/extensions/postgres/types/array.rb +1 -1
- data/lib/rom/sql/extensions/sqlite/type_builder.rb +1 -0
- data/lib/rom/sql/function.rb +17 -2
- data/lib/rom/sql/gateway.rb +1 -1
- data/lib/rom/sql/migration.rb +3 -2
- data/lib/rom/sql/plugin/associates.rb +8 -0
- data/lib/rom/sql/plugin/pagination.rb +48 -4
- data/lib/rom/sql/plugin/timestamps.rb +6 -6
- data/lib/rom/sql/projection_dsl.rb +13 -1
- data/lib/rom/sql/relation.rb +22 -2
- data/lib/rom/sql/relation/reading.rb +16 -16
- data/lib/rom/sql/relation/writing.rb +13 -8
- data/lib/rom/sql/restriction_dsl.rb +1 -1
- data/lib/rom/sql/schema.rb +48 -7
- data/lib/rom/sql/schema/dsl.rb +12 -0
- data/lib/rom/sql/type_dsl.rb +7 -0
- data/lib/rom/sql/type_extensions.rb +1 -1
- data/lib/rom/sql/types.rb +22 -8
- data/lib/rom/sql/version.rb +1 -1
- data/lib/rom/sql/wrap.rb +17 -6
- metadata +11 -17
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 6220c48972476b2eb922c0520c59eab29768cd3d
         | 
| 4 | 
            +
              data.tar.gz: 4cc6885f74372fd464ffe85600350c6c64ba29ba
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 44814022262e8f88ae6e7def5049f3e18790c71bab8fcd5f0c1bc1a06934ebac57715f511ac0929ad276055d185ab229e29e3312c8c38f4269d2eaf9273d22d7
         | 
| 7 | 
            +
              data.tar.gz: 4e37a732f6291c6e37349262dccda67842441c3c34ead4076c9ca8a6ca8ed7d54d3c5bc6da92e76793bf2ad4714102a9406a6b6f2314d93860e872d565031618
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -66,6 +66,13 @@ | |
| 66 66 | 
             
                employees.select { [dep_no, salary, int::avg(salary).over(partition: dep_no, order: id).as(:avg_salary)] }
         | 
| 67 67 | 
             
              ```
         | 
| 68 68 |  | 
| 69 | 
            +
            * Function result can be negated, also `ROM::SQL::Function#not` was added (flash-gordon)
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              ```ruby
         | 
| 72 | 
            +
                 users.where { !lower(name).is('John') }
         | 
| 73 | 
            +
                 users.where { lower(name).not('John') }
         | 
| 74 | 
            +
              ```
         | 
| 75 | 
            +
             | 
| 69 76 |  | 
| 70 77 | 
             
            ### Changed
         | 
| 71 78 |  | 
| @@ -73,6 +80,7 @@ | |
| 73 80 | 
             
            * [BREAKING] `Associates` command plugin requires associations now (solnic)
         | 
| 74 81 | 
             
            * [BREAKING] `Command#transaction` is gone in favor of `Relation#transaction` (solnic)
         | 
| 75 82 | 
             
            * [BREAKING] `PG::JSONArray`, `PG::JSONBArray`, `PG::JSONHash`, and `PG::JSONBHash` types were dropped, use `PG::JSON` and `PG::JSONB` instead (flash-gordon)
         | 
| 83 | 
            +
            * [BREAKING] The `pg_hstore` extension now doesn't get loaded automatically, use the `:extension` option to load it on config initialization (flash-gordon)
         | 
| 76 84 | 
             
            * `ManyToOne` no longer uses a join (solnic)
         | 
| 77 85 | 
             
            * `AutoCombine` and `AutoWrap` plugins were removed as this functionality is provided by core API (solnic)
         | 
| 78 86 | 
             
            * Foreign keys are indexed by default (flash-gordon)
         | 
| @@ -84,6 +92,7 @@ | |
| 84 92 | 
             
            * Self-ref associations work correctly with custom FKs (solnic)
         | 
| 85 93 | 
             
            * Aliased associations with custom FKs work correctly (solnic)
         | 
| 86 94 | 
             
            * Defining a custom dataset block no longer prevents default views like `by_pk` to be defined (solnic)
         | 
| 95 | 
            +
            * `Relation#group` uses canonical schema now (solnic)
         | 
| 87 96 |  | 
| 88 97 | 
             
            [Compare v1.3.3...master](https://github.com/rom-rb/rom-sql/compare/v1.3.3...master)
         | 
| 89 98 |  | 
    
        data/lib/rom/sql/attribute.rb
    CHANGED
    
    | @@ -77,7 +77,7 @@ module ROM | |
| 77 77 | 
             
                  # Whenever you join two schemas, the right schema's attribute
         | 
| 78 78 | 
             
                  # will be marked as joined using this method
         | 
| 79 79 | 
             
                  #
         | 
| 80 | 
            -
                  # @return [SQL::Attribute]
         | 
| 80 | 
            +
                  # @return [SQL::Attribute] Original attribute marked as joined
         | 
| 81 81 | 
             
                  #
         | 
| 82 82 | 
             
                  # @api public
         | 
| 83 83 | 
             
                  def joined
         | 
| @@ -168,6 +168,13 @@ module ROM | |
| 168 168 | 
             
                    self =~ other
         | 
| 169 169 | 
             
                  end
         | 
| 170 170 |  | 
| 171 | 
            +
                  # Return a new attribute with an equality expression
         | 
| 172 | 
            +
                  #
         | 
| 173 | 
            +
                  # @example
         | 
| 174 | 
            +
                  #   users.where { email =~ 1 }
         | 
| 175 | 
            +
                  #
         | 
| 176 | 
            +
                  # @return [Attribute]
         | 
| 177 | 
            +
                  #
         | 
| 171 178 | 
             
                  # @api public
         | 
| 172 179 | 
             
                  def =~(other)
         | 
| 173 180 | 
             
                    meta(sql_expr: sql_expr =~ binary_operation_arg(other))
         | 
| @@ -214,7 +221,7 @@ module ROM | |
| 214 221 | 
             
                  #   users.where { id.in(1, 2, 3) }
         | 
| 215 222 | 
             
                  #   users.where(users[:id].in(1, 2, 3))
         | 
| 216 223 | 
             
                  #
         | 
| 217 | 
            -
                  # @param [Array<Object>]  | 
| 224 | 
            +
                  # @param [Array<Object>] args A range or a list of values for an inclusion check
         | 
| 218 225 | 
             
                  #
         | 
| 219 226 | 
             
                  # @api public
         | 
| 220 227 | 
             
                  def in(*args)
         | 
| @@ -260,7 +267,7 @@ module ROM | |
| 260 267 |  | 
| 261 268 | 
             
                  # Sequel calls this method to coerce an attribute into SQL string
         | 
| 262 269 | 
             
                  #
         | 
| 263 | 
            -
                  # @param [Sequel::Dataset]
         | 
| 270 | 
            +
                  # @param [Sequel::Dataset] ds
         | 
| 264 271 | 
             
                  #
         | 
| 265 272 | 
             
                  # @api private
         | 
| 266 273 | 
             
                  def sql_literal(ds)
         | 
    
        data/lib/rom/sql/dsl.rb
    CHANGED
    
    | @@ -21,6 +21,17 @@ module ROM | |
| 21 21 | 
             
                    end
         | 
| 22 22 | 
             
                  end
         | 
| 23 23 |  | 
| 24 | 
            +
                  # Return a string literal that will be used directly in an ORDER clause
         | 
| 25 | 
            +
                  #
         | 
| 26 | 
            +
                  # @param [String] value
         | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  # @return [Sequel::LiteralString]
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  # @api public
         | 
| 31 | 
            +
                  def `(value)
         | 
| 32 | 
            +
                    ::Sequel.lit(value)
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
             | 
| 24 35 | 
             
                  # @api private
         | 
| 25 36 | 
             
                  def respond_to_missing?(name, include_private = false)
         | 
| 26 37 | 
             
                    super || schema.key?(name)
         | 
    
        data/lib/rom/sql/function.rb
    CHANGED
    
    | @@ -2,6 +2,8 @@ require 'rom/attribute' | |
| 2 2 |  | 
| 3 3 | 
             
            module ROM
         | 
| 4 4 | 
             
              module SQL
         | 
| 5 | 
            +
                # Specialized attribute type for defining SQL functions
         | 
| 6 | 
            +
                #
         | 
| 5 7 | 
             
                # @api public
         | 
| 6 8 | 
             
                class Function < ROM::Attribute
         | 
| 7 9 | 
             
                  class << self
         | 
| @@ -49,6 +51,8 @@ module ROM | |
| 49 51 | 
             
                    meta[:alias] || super
         | 
| 50 52 | 
             
                  end
         | 
| 51 53 |  | 
| 54 | 
            +
                  # @see Attribute#qualified
         | 
| 55 | 
            +
                  #
         | 
| 52 56 | 
             
                  # @api private
         | 
| 53 57 | 
             
                  def qualified(table_alias = nil)
         | 
| 54 58 | 
             
                    meta(
         | 
| @@ -56,9 +60,20 @@ module ROM | |
| 56 60 | 
             
                    )
         | 
| 57 61 | 
             
                  end
         | 
| 58 62 |  | 
| 59 | 
            -
                  # @ | 
| 63 | 
            +
                  # @see ROM::SQL::Attribute#is
         | 
| 64 | 
            +
                  #
         | 
| 65 | 
            +
                  # @api public
         | 
| 60 66 | 
             
                  def is(other)
         | 
| 61 | 
            -
                    :: | 
| 67 | 
            +
                    ::ROM::SQL::Attribute[::ROM::SQL::Types::Bool].meta(
         | 
| 68 | 
            +
                      sql_expr: ::Sequel::SQL::BooleanExpression.new(:'=', func, other)
         | 
| 69 | 
            +
                    )
         | 
| 70 | 
            +
                  end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                  # @see ROM::SQL::Attribute#not
         | 
| 73 | 
            +
                  #
         | 
| 74 | 
            +
                  # @api public
         | 
| 75 | 
            +
                  def not(other)
         | 
| 76 | 
            +
                    !is(other)
         | 
| 62 77 | 
             
                  end
         | 
| 63 78 |  | 
| 64 79 | 
             
                  # Add an OVER clause making a window function call
         | 
    
        data/lib/rom/sql/gateway.rb
    CHANGED
    
    
    
        data/lib/rom/sql/migration.rb
    CHANGED
    
    | @@ -47,8 +47,9 @@ module ROM | |
| 47 47 | 
             
                  #     end
         | 
| 48 48 | 
             
                  #   end
         | 
| 49 49 | 
             
                  #
         | 
| 50 | 
            -
                  # @ | 
| 51 | 
            -
                  # | 
| 50 | 
            +
                  # @overload migration(container, gateway)
         | 
| 51 | 
            +
                  #   @param [ROM::Container] container The container instance used for accessing gateways
         | 
| 52 | 
            +
                  #   @param [Symbol] gateway The gateway name, :default by default
         | 
| 52 53 | 
             
                  #
         | 
| 53 54 | 
             
                  # @api public
         | 
| 54 55 | 
             
                  def migration(*args, &block)
         | 
| @@ -10,6 +10,7 @@ module ROM | |
| 10 10 | 
             
                    class AssociateOptions
         | 
| 11 11 | 
             
                      attr_reader :name, :assoc, :opts
         | 
| 12 12 |  | 
| 13 | 
            +
                      # @api private
         | 
| 13 14 | 
             
                      def initialize(name, relation, opts)
         | 
| 14 15 | 
             
                        @name = name
         | 
| 15 16 | 
             
                        @assoc = relation.associations[name]
         | 
| @@ -42,6 +43,7 @@ module ROM | |
| 42 43 | 
             
                      super
         | 
| 43 44 | 
             
                    end
         | 
| 44 45 |  | 
| 46 | 
            +
                    # @api public
         | 
| 45 47 | 
             
                    module ClassMethods
         | 
| 46 48 | 
             
                      # @see ROM::Command::ClassInterface.build
         | 
| 47 49 | 
             
                      #
         | 
| @@ -134,6 +136,12 @@ module ROM | |
| 134 136 | 
             
                        result_type == :one ? output_tuples[0] : output_tuples
         | 
| 135 137 | 
             
                      end
         | 
| 136 138 |  | 
| 139 | 
            +
                      # Return a new command with the provided association
         | 
| 140 | 
            +
                      #
         | 
| 141 | 
            +
                      # @param [Symbol, Relation::Name] name The name of the association
         | 
| 142 | 
            +
                      #
         | 
| 143 | 
            +
                      # @return [Command]
         | 
| 144 | 
            +
                      #
         | 
| 137 145 | 
             
                      # @api public
         | 
| 138 146 | 
             
                      def with_association(name, opts = EMPTY_HASH)
         | 
| 139 147 | 
             
                        self.class.build(
         | 
| @@ -3,34 +3,76 @@ require 'rom/initializer' | |
| 3 3 | 
             
            module ROM
         | 
| 4 4 | 
             
              module SQL
         | 
| 5 5 | 
             
                module Plugin
         | 
| 6 | 
            +
                  # Pagination plugin for Relations
         | 
| 7 | 
            +
                  #
         | 
| 8 | 
            +
                  # @api public
         | 
| 6 9 | 
             
                  module Pagination
         | 
| 10 | 
            +
                    # Pager object provides the underlying pagination API for relations
         | 
| 11 | 
            +
                    #
         | 
| 12 | 
            +
                    # @api public
         | 
| 7 13 | 
             
                    class Pager
         | 
| 8 14 | 
             
                      extend Initializer
         | 
| 9 15 | 
             
                      include Dry::Equalizer(:dataset, :options)
         | 
| 10 16 |  | 
| 17 | 
            +
                      # @!attribute [r] dataset
         | 
| 18 | 
            +
                      #   @return [Sequel::Dataset] Relation's dataset
         | 
| 11 19 | 
             
                      param :dataset
         | 
| 12 20 |  | 
| 21 | 
            +
                      # @!attribute [r] current_page
         | 
| 22 | 
            +
                      #   @return [Integer] Current page number
         | 
| 13 23 | 
             
                      option :current_page, default: -> { 1 }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                      # @!attribute [r] per_page
         | 
| 26 | 
            +
                      #   @return [Integer] Current per-page number
         | 
| 14 27 | 
             
                      option :per_page
         | 
| 15 28 |  | 
| 29 | 
            +
                      # Return next page number
         | 
| 30 | 
            +
                      #
         | 
| 31 | 
            +
                      # @example
         | 
| 32 | 
            +
                      #   users.page(2).pager.next_page
         | 
| 33 | 
            +
                      #   # => 3
         | 
| 34 | 
            +
                      #
         | 
| 35 | 
            +
                      # @return [Integer]
         | 
| 36 | 
            +
                      #
         | 
| 37 | 
            +
                      # @api public
         | 
| 16 38 | 
             
                      def next_page
         | 
| 17 39 | 
             
                        num = current_page + 1
         | 
| 18 40 | 
             
                        num if total_pages >= num
         | 
| 19 41 | 
             
                      end
         | 
| 20 42 |  | 
| 43 | 
            +
                      # Return previous page number
         | 
| 44 | 
            +
                      #
         | 
| 45 | 
            +
                      # @example
         | 
| 46 | 
            +
                      #   users.page(2).pager.prev_page
         | 
| 47 | 
            +
                      #   # => 1
         | 
| 48 | 
            +
                      #
         | 
| 49 | 
            +
                      # @return [Integer]
         | 
| 50 | 
            +
                      #
         | 
| 51 | 
            +
                      # @api public
         | 
| 21 52 | 
             
                      def prev_page
         | 
| 22 53 | 
             
                        num = current_page - 1
         | 
| 23 54 | 
             
                        num if num > 0
         | 
| 24 55 | 
             
                      end
         | 
| 25 56 |  | 
| 57 | 
            +
                      # Return total number of tuples
         | 
| 58 | 
            +
                      #
         | 
| 59 | 
            +
                      # @return [Integer]
         | 
| 60 | 
            +
                      #
         | 
| 61 | 
            +
                      # @api public
         | 
| 26 62 | 
             
                      def total
         | 
| 27 63 | 
             
                        dataset.unlimited.count
         | 
| 28 64 | 
             
                      end
         | 
| 29 65 |  | 
| 66 | 
            +
                      # Return total number of pages
         | 
| 67 | 
            +
                      #
         | 
| 68 | 
            +
                      # @return [Integer]
         | 
| 69 | 
            +
                      #
         | 
| 70 | 
            +
                      # @api public
         | 
| 30 71 | 
             
                      def total_pages
         | 
| 31 72 | 
             
                        (total / per_page.to_f).ceil
         | 
| 32 73 | 
             
                      end
         | 
| 33 74 |  | 
| 75 | 
            +
                      # @api private
         | 
| 34 76 | 
             
                      def at(dataset, current_page, per_page = self.per_page)
         | 
| 35 77 | 
             
                        current_page = current_page.to_i
         | 
| 36 78 | 
             
                        per_page = per_page.to_i
         | 
| @@ -44,6 +86,7 @@ module ROM | |
| 44 86 | 
             
                      alias_method :limit_value, :per_page
         | 
| 45 87 | 
             
                    end
         | 
| 46 88 |  | 
| 89 | 
            +
                    # @api private
         | 
| 47 90 | 
             
                    def self.included(klass)
         | 
| 48 91 | 
             
                      super
         | 
| 49 92 |  | 
| @@ -59,9 +102,8 @@ module ROM | |
| 59 102 | 
             
                    # Paginate a relation
         | 
| 60 103 | 
             
                    #
         | 
| 61 104 | 
             
                    # @example
         | 
| 62 | 
            -
                    #    | 
| 63 | 
            -
                    #    | 
| 64 | 
            -
                    #   rom.relations[:users].pager # => info about pagination
         | 
| 105 | 
            +
                    #   users.page(1)
         | 
| 106 | 
            +
                    #   users.pager # => info about pagination
         | 
| 65 107 | 
             
                    #
         | 
| 66 108 | 
             
                    # @return [Relation]
         | 
| 67 109 | 
             
                    #
         | 
| @@ -74,7 +116,9 @@ module ROM | |
| 74 116 | 
             
                    # Set limit for pagination
         | 
| 75 117 | 
             
                    #
         | 
| 76 118 | 
             
                    # @example
         | 
| 77 | 
            -
                    #    | 
| 119 | 
            +
                    #   users.per_page(10).page(2)
         | 
| 120 | 
            +
                    #
         | 
| 121 | 
            +
                    # @return [Relation]
         | 
| 78 122 | 
             
                    #
         | 
| 79 123 | 
             
                    # @api public
         | 
| 80 124 | 
             
                    def per_page(num)
         | 
| @@ -38,11 +38,11 @@ module ROM | |
| 38 38 | 
             
                      #   result = create_user.call
         | 
| 39 39 | 
             
                      #   result[:created_at]  #=> Time.now.utc
         | 
| 40 40 | 
             
                      #
         | 
| 41 | 
            -
                      # @param [Symbol]  | 
| 41 | 
            +
                      # @param [Array<Symbol>] names A list of attribute names
         | 
| 42 42 | 
             
                      #
         | 
| 43 43 | 
             
                      # @api public
         | 
| 44 | 
            -
                      def timestamps(* | 
| 45 | 
            -
                        timestamp_columns timestamp_columns.merge( | 
| 44 | 
            +
                      def timestamps(*names)
         | 
| 45 | 
            +
                        timestamp_columns timestamp_columns.merge(names)
         | 
| 46 46 |  | 
| 47 47 | 
             
                        include InstanceMethods
         | 
| 48 48 | 
             
                      end
         | 
| @@ -62,11 +62,11 @@ module ROM | |
| 62 62 | 
             
                      #   result = create_user.call
         | 
| 63 63 | 
             
                      #   result[:created_at]  #=> Date.today
         | 
| 64 64 | 
             
                      #
         | 
| 65 | 
            -
                      # @param [Symbol]  | 
| 65 | 
            +
                      # @param [Array<Symbol>] names A list of attribute names
         | 
| 66 66 | 
             
                      #
         | 
| 67 67 | 
             
                      # @api public
         | 
| 68 | 
            -
                      def datestamps(* | 
| 69 | 
            -
                        datestamp_columns datestamp_columns.merge( | 
| 68 | 
            +
                      def datestamps(*names)
         | 
| 69 | 
            +
                        datestamp_columns datestamp_columns.merge(names)
         | 
| 70 70 |  | 
| 71 71 | 
             
                        include InstanceMethods
         | 
| 72 72 | 
             
                      end
         | 
| @@ -3,8 +3,20 @@ require 'rom/sql/function' | |
| 3 3 |  | 
| 4 4 | 
             
            module ROM
         | 
| 5 5 | 
             
              module SQL
         | 
| 6 | 
            -
                #  | 
| 6 | 
            +
                # Projection DSL used in reading API (`select`, `select_append` etc.)
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # @api public
         | 
| 7 9 | 
             
                class ProjectionDSL < DSL
         | 
| 10 | 
            +
                  # Return a string literal that will be directly used in an SQL statement or query
         | 
| 11 | 
            +
                  #
         | 
| 12 | 
            +
                  # @example
         | 
| 13 | 
            +
                  #   users.select { `FOO`.as(:foo) }.first
         | 
| 14 | 
            +
                  #   # => { :foo => "FOO" }
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @param [String] value A string object
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @return [Attribute] An SQL attribute with a string literal expression
         | 
| 19 | 
            +
                  #
         | 
| 8 20 | 
             
                  # @api public
         | 
| 9 21 | 
             
                  def `(value)
         | 
| 10 22 | 
             
                    expr = ::Sequel.lit(value)
         | 
    
        data/lib/rom/sql/relation.rb
    CHANGED
    
    | @@ -61,7 +61,7 @@ module ROM | |
| 61 61 | 
             
                      #   @api public
         | 
| 62 62 | 
             
                      class_eval <<-RUBY, __FILE__, __LINE__ + 1
         | 
| 63 63 | 
             
                        def by_pk(#{schema.primary_key.map(&:name).join(', ')})
         | 
| 64 | 
            -
                          where(#{schema.primary_key.map { |attr| " | 
| 64 | 
            +
                          where(#{schema.primary_key.map { |attr| "schema.canonical[:#{attr.name}] => #{attr.name}" }.join(', ')})
         | 
| 65 65 | 
             
                        end
         | 
| 66 66 | 
             
                      RUBY
         | 
| 67 67 | 
             
                    else
         | 
| @@ -80,7 +80,7 @@ module ROM | |
| 80 80 | 
             
                              "Missing primary key for :\#{schema.name}"
         | 
| 81 81 | 
             
                            )
         | 
| 82 82 | 
             
                          end
         | 
| 83 | 
            -
                          where( | 
| 83 | 
            +
                          where(schema.canonical[schema.canonical.primary_key_name].qualified => pk)
         | 
| 84 84 | 
             
                        end
         | 
| 85 85 | 
             
                      RUBY
         | 
| 86 86 | 
             
                    end
         | 
| @@ -116,6 +116,26 @@ module ROM | |
| 116 116 | 
             
                    associations[name].()
         | 
| 117 117 | 
             
                  end
         | 
| 118 118 |  | 
| 119 | 
            +
                  # Open a database transaction
         | 
| 120 | 
            +
                  #
         | 
| 121 | 
            +
                  # @param [Hash] opts
         | 
| 122 | 
            +
                  # @option opts [Boolean] :auto_savepoint Automatically use a savepoint for Database#transaction calls inside this transaction block.
         | 
| 123 | 
            +
                  # @option opts [Symbol] :isolation The transaction isolation level to use for this transaction, should be :uncommitted, :committed, :repeatable, or :serializable, used if given and the database/adapter supports customizable transaction isolation levels.
         | 
| 124 | 
            +
                  # @option opts [Integer] :num_retries The number of times to retry if the :retry_on option is used. The default is 5 times. Can be set to nil to retry indefinitely, but that is not recommended.
         | 
| 125 | 
            +
                  # @option opts [Proc] :before_retry Proc to execute before rertrying if the :retry_on option is used. Called with two arguments: the number of retry attempts (counting the current one) and the error the last attempt failed with.
         | 
| 126 | 
            +
                  # @option opts [String] :prepare A string to use as the transaction identifier for a prepared transaction (two-phase commit), if the database/adapter supports prepared transactions.
         | 
| 127 | 
            +
                  # @option opts [Class] :retry_on An exception class or array of exception classes for which to automatically retry the transaction. Can only be set if not inside an existing transaction. Note that this should not be used unless the entire transaction block is idempotent, as otherwise it can cause non-idempotent behavior to execute multiple times.
         | 
| 128 | 
            +
                  # @option opts [Symbol] :rollback Can the set to :reraise to reraise any Sequel::Rollback exceptions raised, or :always to always rollback even if no exceptions occur (useful for testing).
         | 
| 129 | 
            +
                  # @option opts [Symbol] :server The server to use for the transaction. Set to :default, :read_only, or whatever symbol you used in the connect string when naming your servers.
         | 
| 130 | 
            +
                  # @option opts [Boolean] :savepoint Whether to create a new savepoint for this transaction, only respected if the database/adapter supports savepoints. By default Sequel will reuse an existing transaction, so if you want to use a savepoint you must use this option. If the surrounding transaction uses :auto_savepoint, you can set this to false to not use a savepoint. If the value given for this option is :only, it will only create a savepoint if it is inside a transacation.
         | 
| 131 | 
            +
                  # @option opts [Boolean] :deferrable **PG 9.1+ only** If present, set to DEFERRABLE if true or NOT DEFERRABLE if false.
         | 
| 132 | 
            +
                  # @option opts [Boolean] :read_only **PG only** If present, set to READ ONLY if true or READ WRITE if false.
         | 
| 133 | 
            +
                  # @option opts [Symbol] :synchronous **PG only** if non-nil, set synchronous_commit appropriately. Valid values true, :on, false, :off, :local (9.1+), and :remote_write (9.2+).
         | 
| 134 | 
            +
                  #
         | 
| 135 | 
            +
                  # @yield [t] Transaction
         | 
| 136 | 
            +
                  #
         | 
| 137 | 
            +
                  # @return [Mixed]
         | 
| 138 | 
            +
                  #
         | 
| 119 139 | 
             
                  # @api public
         | 
| 120 140 | 
             
                  def transaction(opts = EMPTY_HASH, &block)
         | 
| 121 141 | 
             
                    Transaction.new(dataset.db).run(opts, &block)
         | 
| @@ -241,7 +241,7 @@ module ROM | |
| 241 241 | 
             
                    #
         | 
| 242 242 | 
             
                    # @api public
         | 
| 243 243 | 
             
                    def select_append(*args, &block)
         | 
| 244 | 
            -
                      schema.merge( | 
| 244 | 
            +
                      schema.merge(schema.canonical.project(*args, &block)).(self)
         | 
| 245 245 | 
             
                    end
         | 
| 246 246 |  | 
| 247 247 | 
             
                    # Returns a copy of the relation with a SQL DISTINCT clause.
         | 
| @@ -273,7 +273,7 @@ module ROM | |
| 273 273 | 
             
                    # @example
         | 
| 274 274 | 
             
                    #   users.sum(:age)
         | 
| 275 275 | 
             
                    #
         | 
| 276 | 
            -
                    # @param [Array<Symbol>]  | 
| 276 | 
            +
                    # @param [Array<Symbol>] args A list with column names
         | 
| 277 277 | 
             
                    #
         | 
| 278 278 | 
             
                    # @return [Integer]
         | 
| 279 279 | 
             
                    #
         | 
| @@ -287,7 +287,7 @@ module ROM | |
| 287 287 | 
             
                    # @example
         | 
| 288 288 | 
             
                    #   users.min(:age)
         | 
| 289 289 | 
             
                    #
         | 
| 290 | 
            -
                    # @param [Array<Symbol>]  | 
| 290 | 
            +
                    # @param [Array<Symbol>] args A list with column names
         | 
| 291 291 | 
             
                    #
         | 
| 292 292 | 
             
                    # @return Number
         | 
| 293 293 | 
             
                    #
         | 
| @@ -301,7 +301,7 @@ module ROM | |
| 301 301 | 
             
                    # @example
         | 
| 302 302 | 
             
                    #   users.max(:age)
         | 
| 303 303 | 
             
                    #
         | 
| 304 | 
            -
                    # @param [Array<Symbol>]  | 
| 304 | 
            +
                    # @param [Array<Symbol>] args A list with column names
         | 
| 305 305 | 
             
                    #
         | 
| 306 306 | 
             
                    # @return Number
         | 
| 307 307 | 
             
                    #
         | 
| @@ -315,7 +315,7 @@ module ROM | |
| 315 315 | 
             
                    # @example
         | 
| 316 316 | 
             
                    #   users.avg(:age)
         | 
| 317 317 | 
             
                    #
         | 
| 318 | 
            -
                    # @param [Array<Symbol>]  | 
| 318 | 
            +
                    # @param [Array<Symbol>] args A list with column names
         | 
| 319 319 | 
             
                    #
         | 
| 320 320 | 
             
                    # @return Number
         | 
| 321 321 | 
             
                    #
         | 
| @@ -354,7 +354,7 @@ module ROM | |
| 354 354 | 
             
                    # @api public
         | 
| 355 355 | 
             
                    def where(*args, &block)
         | 
| 356 356 | 
             
                      if block
         | 
| 357 | 
            -
                        where(*args).where( | 
| 357 | 
            +
                        where(*args).where(schema.canonical.restriction(&block))
         | 
| 358 358 | 
             
                      elsif args.size == 1 && args[0].is_a?(Hash)
         | 
| 359 359 | 
             
                        new(dataset.where(coerce_conditions(args[0])))
         | 
| 360 360 | 
             
                      elsif !args.empty?
         | 
| @@ -369,7 +369,7 @@ module ROM | |
| 369 369 | 
             
                    # @example
         | 
| 370 370 | 
             
                    #   users.exclude(name: 'Jane')
         | 
| 371 371 | 
             
                    #
         | 
| 372 | 
            -
                    # @param [Hash]  | 
| 372 | 
            +
                    # @param [Hash] args A hash with conditions for exclusion
         | 
| 373 373 | 
             
                    #
         | 
| 374 374 | 
             
                    # @return [Relation]
         | 
| 375 375 | 
             
                    #
         | 
| @@ -413,7 +413,7 @@ module ROM | |
| 413 413 | 
             
                    # @api public
         | 
| 414 414 | 
             
                    def having(*args, &block)
         | 
| 415 415 | 
             
                      if block
         | 
| 416 | 
            -
                        new(dataset.having(*args, * | 
| 416 | 
            +
                        new(dataset.having(*args, *schema.canonical.restriction(&block)))
         | 
| 417 417 | 
             
                      else
         | 
| 418 418 | 
             
                        new(dataset.__send__(__method__, *args))
         | 
| 419 419 | 
             
                      end
         | 
| @@ -468,7 +468,7 @@ module ROM | |
| 468 468 | 
             
                    # @api public
         | 
| 469 469 | 
             
                    def order(*args, &block)
         | 
| 470 470 | 
             
                      if block
         | 
| 471 | 
            -
                        new(dataset.order(*args, * | 
| 471 | 
            +
                        new(dataset.order(*args, *schema.canonical.order(&block)))
         | 
| 472 472 | 
             
                      else
         | 
| 473 473 | 
             
                        new(dataset.__send__(__method__, *args, &block))
         | 
| 474 474 | 
             
                      end
         | 
| @@ -678,10 +678,10 @@ module ROM | |
| 678 678 | 
             
                        if args.size > 0
         | 
| 679 679 | 
             
                          group(*args).group_append(&block)
         | 
| 680 680 | 
             
                        else
         | 
| 681 | 
            -
                          new(dataset.__send__(__method__, *schema.group(&block)))
         | 
| 681 | 
            +
                          new(dataset.__send__(__method__, *schema.canonical.group(&block)))
         | 
| 682 682 | 
             
                        end
         | 
| 683 683 | 
             
                      else
         | 
| 684 | 
            -
                        new(dataset.__send__(__method__, *schema.project(*args) | 
| 684 | 
            +
                        new(dataset.__send__(__method__, *schema.canonical.project(*args)))
         | 
| 685 685 | 
             
                      end
         | 
| 686 686 | 
             
                    end
         | 
| 687 687 |  | 
| @@ -717,7 +717,7 @@ module ROM | |
| 717 717 | 
             
                        if args.size > 0
         | 
| 718 718 | 
             
                          group_append(*args).group_append(&block)
         | 
| 719 719 | 
             
                        else
         | 
| 720 | 
            -
                          new(dataset.group_append(*schema.group(&block)))
         | 
| 720 | 
            +
                          new(dataset.group_append(*schema.canonical.group(&block)))
         | 
| 721 721 | 
             
                        end
         | 
| 722 722 | 
             
                      else
         | 
| 723 723 | 
             
                        new(dataset.group_append(*args))
         | 
| @@ -730,7 +730,7 @@ module ROM | |
| 730 730 | 
             
                    #   tasks.group_and_count(:user_id)
         | 
| 731 731 | 
             
                    #   # => [{ user_id: 1, count: 2 }, { user_id: 2, count: 3 }]
         | 
| 732 732 | 
             
                    #
         | 
| 733 | 
            -
                    # @param [Array<Symbol>]  | 
| 733 | 
            +
                    # @param [Array<Symbol>] args A list of column names
         | 
| 734 734 | 
             
                    #
         | 
| 735 735 | 
             
                    # @return [Relation]
         | 
| 736 736 | 
             
                    #
         | 
| @@ -745,7 +745,7 @@ module ROM | |
| 745 745 | 
             
                    #   tasks.select_group(:user_id)
         | 
| 746 746 | 
             
                    #   # => [{ user_id: 1 }, { user_id: 2 }]
         | 
| 747 747 | 
             
                    #
         | 
| 748 | 
            -
                    # @param [Array<Symbol>]  | 
| 748 | 
            +
                    # @param [Array<Symbol>] args A list of column names
         | 
| 749 749 | 
             
                    #
         | 
| 750 750 | 
             
                    # @return [Relation]
         | 
| 751 751 | 
             
                    #
         | 
| @@ -949,8 +949,8 @@ module ROM | |
| 949 949 | 
             
                    # @api private
         | 
| 950 950 | 
             
                    def coerce_conditions(conditions)
         | 
| 951 951 | 
             
                      conditions.each_with_object({}) { |(k, v), h|
         | 
| 952 | 
            -
                        if k.is_a?(Symbol) &&  | 
| 953 | 
            -
                          type =  | 
| 952 | 
            +
                        if k.is_a?(Symbol) && schema.canonical.key?(k)
         | 
| 953 | 
            +
                          type = schema.canonical[k]
         | 
| 954 954 | 
             
                          h[k] = v.is_a?(Array) ? v.map { |e| type[e] } : type[v]
         | 
| 955 955 | 
             
                        else
         | 
| 956 956 | 
             
                          h[k] = v
         | 
| @@ -11,6 +11,8 @@ module ROM | |
| 11 11 | 
             
                    #   users.upsert({ name: 'Jane', email: 'jane@foo.com' },
         | 
| 12 12 | 
             
                    #                { target: :email, update: { name: :excluded__name } }
         | 
| 13 13 | 
             
                    #
         | 
| 14 | 
            +
                    # @return [Integer] Number of affected rows
         | 
| 15 | 
            +
                    #
         | 
| 14 16 | 
             
                    # @api public
         | 
| 15 17 | 
             
                    def upsert(*args, &block)
         | 
| 16 18 | 
             
                      if args.size > 1 && args[-1].is_a?(Hash)
         | 
| @@ -28,9 +30,9 @@ module ROM | |
| 28 30 | 
             
                    # @example
         | 
| 29 31 | 
             
                    #   users.insert(name: 'Jane')
         | 
| 30 32 | 
             
                    #
         | 
| 31 | 
            -
                    # @param [Hash]  | 
| 33 | 
            +
                    # @param [Hash] args
         | 
| 32 34 | 
             
                    #
         | 
| 33 | 
            -
                    # @return [ | 
| 35 | 
            +
                    # @return [Hash] Inserted tuple
         | 
| 34 36 | 
             
                    #
         | 
| 35 37 | 
             
                    # @api public
         | 
| 36 38 | 
             
                    def insert(*args, &block)
         | 
| @@ -42,9 +44,9 @@ module ROM | |
| 42 44 | 
             
                    # @example
         | 
| 43 45 | 
             
                    #   users.multi_insert([{name: 'Jane'}, {name: 'Jack'}])
         | 
| 44 46 | 
             
                    #
         | 
| 45 | 
            -
                    # @param [Array]  | 
| 47 | 
            +
                    # @param [Array<Hash>] args
         | 
| 46 48 | 
             
                    #
         | 
| 47 | 
            -
                    # @return [ | 
| 49 | 
            +
                    # @return [Array<String>] A list of executed SQL statements
         | 
| 48 50 | 
             
                    #
         | 
| 49 51 | 
             
                    # @api public
         | 
| 50 52 | 
             
                    def multi_insert(*args, &block)
         | 
| @@ -57,7 +59,7 @@ module ROM | |
| 57 59 | 
             
                    #   users.update(name: 'Jane')
         | 
| 58 60 | 
             
                    #   users.where(name: 'Jane').update(name: 'Jane Doe')
         | 
| 59 61 | 
             
                    #
         | 
| 60 | 
            -
                    # @return [ | 
| 62 | 
            +
                    # @return [Integer] Number of updated rows
         | 
| 61 63 | 
             
                    #
         | 
| 62 64 | 
             
                    # @api public
         | 
| 63 65 | 
             
                    def update(*args, &block)
         | 
| @@ -71,7 +73,7 @@ module ROM | |
| 71 73 | 
             
                    #   users.where(name: 'Jane').delete # delete tuples
         | 
| 72 74 | 
             
                    #                                      from restricted relation
         | 
| 73 75 | 
             
                    #
         | 
| 74 | 
            -
                    # @return [ | 
| 76 | 
            +
                    # @return [Integer] Number of deleted tuples
         | 
| 75 77 | 
             
                    #
         | 
| 76 78 | 
             
                    # @api public
         | 
| 77 79 | 
             
                    def delete(*args, &block)
         | 
| @@ -79,6 +81,7 @@ module ROM | |
| 79 81 | 
             
                    end
         | 
| 80 82 |  | 
| 81 83 | 
             
                    # Insert tuples from other relation
         | 
| 84 | 
            +
                    #
         | 
| 82 85 | 
             
                    # NOTE: The method implicitly uses a transaction
         | 
| 83 86 | 
             
                    #
         | 
| 84 87 | 
             
                    # @example
         | 
| @@ -89,7 +92,7 @@ module ROM | |
| 89 92 | 
             
                    #   the INSERT ... SELECT statement will
         | 
| 90 93 | 
             
                    #   be used for importing the data
         | 
| 91 94 | 
             
                    #
         | 
| 92 | 
            -
                    #   @ | 
| 95 | 
            +
                    #   @param [SQL::Relation] other_sql_relation
         | 
| 93 96 | 
             
                    #
         | 
| 94 97 | 
             
                    #   @option [Integer] :slice
         | 
| 95 98 | 
             
                    #     Split loading into batches of provided size,
         | 
| @@ -100,10 +103,12 @@ module ROM | |
| 100 103 | 
             
                    #   Import data from another relation. The source
         | 
| 101 104 | 
             
                    #   relation will be materialized before loading
         | 
| 102 105 | 
             
                    #
         | 
| 103 | 
            -
                    #   @ | 
| 106 | 
            +
                    #   @param [Relation] other
         | 
| 104 107 | 
             
                    #
         | 
| 105 108 | 
             
                    #   @option [Integer] :slice
         | 
| 106 109 | 
             
                    #
         | 
| 110 | 
            +
                    # @return [Integer] Number of imported tuples
         | 
| 111 | 
            +
                    #
         | 
| 107 112 | 
             
                    # @api public
         | 
| 108 113 | 
             
                    def import(other, options = EMPTY_HASH)
         | 
| 109 114 | 
             
                      if other.gateway.eql?(gateway)
         | 
    
        data/lib/rom/sql/schema.rb
    CHANGED
    
    | @@ -11,6 +11,9 @@ require 'rom/sql/schema/inferrer' | |
| 11 11 |  | 
| 12 12 | 
             
            module ROM
         | 
| 13 13 | 
             
              module SQL
         | 
| 14 | 
            +
                # Specialized schema for SQL databases
         | 
| 15 | 
            +
                #
         | 
| 16 | 
            +
                # @api public
         | 
| 14 17 | 
             
                class Schema < ROM::Schema
         | 
| 15 18 | 
             
                  # @!attribute [r] indexes
         | 
| 16 19 | 
             
                  #   @return [Array<Index>] Array with schema indexes
         | 
| @@ -20,16 +23,34 @@ module ROM | |
| 20 23 | 
             
                  #   @return [Array<ForeignKey>] Array with foreign keys
         | 
| 21 24 | 
             
                  option :foreign_keys, default: -> { EMPTY_SET }
         | 
| 22 25 |  | 
| 26 | 
            +
                  # Open restriction DSL for defining query conditions using schema attributes
         | 
| 27 | 
            +
                  #
         | 
| 28 | 
            +
                  # @see Relation#where
         | 
| 29 | 
            +
                  #
         | 
| 30 | 
            +
                  # @return [Mixed] Result of the block call
         | 
| 31 | 
            +
                  #
         | 
| 23 32 | 
             
                  # @api public
         | 
| 24 33 | 
             
                  def restriction(&block)
         | 
| 25 34 | 
             
                    RestrictionDSL.new(self).call(&block)
         | 
| 26 35 | 
             
                  end
         | 
| 27 36 |  | 
| 37 | 
            +
                  # Open Order DSL for setting ORDER clause in queries
         | 
| 38 | 
            +
                  #
         | 
| 39 | 
            +
                  # @see Relation#order
         | 
| 40 | 
            +
                  #
         | 
| 41 | 
            +
                  # @return [Mixed] Result of the block call
         | 
| 42 | 
            +
                  #
         | 
| 28 43 | 
             
                  # @api public
         | 
| 29 44 | 
             
                  def order(&block)
         | 
| 30 45 | 
             
                    OrderDSL.new(self).call(&block)
         | 
| 31 46 | 
             
                  end
         | 
| 32 47 |  | 
| 48 | 
            +
                  # Open Group DSL for setting GROUP BY clause in queries
         | 
| 49 | 
            +
                  #
         | 
| 50 | 
            +
                  # @see Relation#group
         | 
| 51 | 
            +
                  #
         | 
| 52 | 
            +
                  # @return [Mixed] Result of the block call
         | 
| 53 | 
            +
                  #
         | 
| 33 54 | 
             
                  # @api public
         | 
| 34 55 | 
             
                  def group(&block)
         | 
| 35 56 | 
             
                    GroupDSL.new(self).call(&block)
         | 
| @@ -44,15 +65,13 @@ module ROM | |
| 44 65 | 
             
                    new(map { |attr| attr.qualified(table_alias) })
         | 
| 45 66 | 
             
                  end
         | 
| 46 67 |  | 
| 47 | 
            -
                  #  | 
| 68 | 
            +
                  # Project a schema
         | 
| 48 69 | 
             
                  #
         | 
| 49 | 
            -
                  # @ | 
| 70 | 
            +
                  # @see ROM::Schema#project
         | 
| 71 | 
            +
                  # @see Relation#select
         | 
| 72 | 
            +
                  #
         | 
| 73 | 
            +
                  # @return [Schema] A new schema with projected attributes
         | 
| 50 74 | 
             
                  #
         | 
| 51 | 
            -
                  # @api public
         | 
| 52 | 
            -
                  def canonical
         | 
| 53 | 
            -
                    new(map(&:canonical))
         | 
| 54 | 
            -
                  end
         | 
| 55 | 
            -
             | 
| 56 75 | 
             
                  # @api public
         | 
| 57 76 | 
             
                  def project(*names, &block)
         | 
| 58 77 | 
             
                    if block
         | 
| @@ -62,21 +81,39 @@ module ROM | |
| 62 81 | 
             
                    end
         | 
| 63 82 | 
             
                  end
         | 
| 64 83 |  | 
| 84 | 
            +
                  # Project schema so that it only contains primary key
         | 
| 85 | 
            +
                  #
         | 
| 86 | 
            +
                  # @return [Schema]
         | 
| 87 | 
            +
                  #
         | 
| 65 88 | 
             
                  # @api private
         | 
| 66 89 | 
             
                  def project_pk
         | 
| 67 90 | 
             
                    project(*primary_key_names)
         | 
| 68 91 | 
             
                  end
         | 
| 69 92 |  | 
| 93 | 
            +
                  # Project schema so that it only contains renamed foreign key
         | 
| 94 | 
            +
                  #
         | 
| 95 | 
            +
                  # @return [Schema]
         | 
| 96 | 
            +
                  #
         | 
| 70 97 | 
             
                  # @api private
         | 
| 71 98 | 
             
                  def project_fk(mapping)
         | 
| 72 99 | 
             
                    new(rename(mapping).map(&:foreign_key))
         | 
| 73 100 | 
             
                  end
         | 
| 74 101 |  | 
| 102 | 
            +
                  # Join with another schema
         | 
| 103 | 
            +
                  #
         | 
| 104 | 
            +
                  # @param [Schema] other The other schema to join with
         | 
| 105 | 
            +
                  #
         | 
| 106 | 
            +
                  # @return [Schema]
         | 
| 107 | 
            +
                  #
         | 
| 75 108 | 
             
                  # @api public
         | 
| 76 109 | 
             
                  def join(other)
         | 
| 77 110 | 
             
                    merge(other.joined)
         | 
| 78 111 | 
             
                  end
         | 
| 79 112 |  | 
| 113 | 
            +
                  # Return a new schema with all attributes marked as joined
         | 
| 114 | 
            +
                  #
         | 
| 115 | 
            +
                  # @return [Schema]
         | 
| 116 | 
            +
                  #
         | 
| 80 117 | 
             
                  # @api public
         | 
| 81 118 | 
             
                  def joined
         | 
| 82 119 | 
             
                    new(map(&:joined))
         | 
| @@ -102,6 +139,8 @@ module ROM | |
| 102 139 | 
             
                    new(EMPTY_ARRAY)
         | 
| 103 140 | 
             
                  end
         | 
| 104 141 |  | 
| 142 | 
            +
                  # Finalize all attributes by qualifying them and initializing primary key names
         | 
| 143 | 
            +
                  #
         | 
| 105 144 | 
             
                  # @api private
         | 
| 106 145 | 
             
                  def finalize_attributes!(options = EMPTY_HASH)
         | 
| 107 146 | 
             
                    super do
         | 
| @@ -110,6 +149,8 @@ module ROM | |
| 110 149 | 
             
                    end
         | 
| 111 150 | 
             
                  end
         | 
| 112 151 |  | 
| 152 | 
            +
                  # Finalize associations
         | 
| 153 | 
            +
                  #
         | 
| 113 154 | 
             
                  # @api private
         | 
| 114 155 | 
             
                  def finalize_associations!(relations:)
         | 
| 115 156 | 
             
                    super do
         | 
    
        data/lib/rom/sql/schema/dsl.rb
    CHANGED
    
    | @@ -3,14 +3,26 @@ require 'rom/sql/schema/index_dsl' | |
| 3 3 | 
             
            module ROM
         | 
| 4 4 | 
             
              module SQL
         | 
| 5 5 | 
             
                class Schema < ROM::Schema
         | 
| 6 | 
            +
                  # Specialized schema DSL with SQL-specific features
         | 
| 7 | 
            +
                  #
         | 
| 6 8 | 
             
                  # @api public
         | 
| 7 9 | 
             
                  class DSL < ROM::Schema::DSL
         | 
| 10 | 
            +
                    # @!attribute [r] index_dsl
         | 
| 11 | 
            +
                    #   @return [IndexDSL] Index DSL instance (created only if indexes block is called)
         | 
| 8 12 | 
             
                    attr_reader :index_dsl
         | 
| 9 13 |  | 
| 14 | 
            +
                    # Define indexes within a block
         | 
| 15 | 
            +
                    #
         | 
| 16 | 
            +
                    # @api public
         | 
| 10 17 | 
             
                    def indexes(&block)
         | 
| 11 18 | 
             
                      @index_dsl = IndexDSL.new(options, &block)
         | 
| 12 19 | 
             
                    end
         | 
| 13 20 |  | 
| 21 | 
            +
                    private
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    # Return schema options
         | 
| 24 | 
            +
                    #
         | 
| 25 | 
            +
                    # @api private
         | 
| 14 26 | 
             
                    def opts
         | 
| 15 27 | 
             
                      if index_dsl
         | 
| 16 28 | 
             
                        opts = super
         | 
    
        data/lib/rom/sql/type_dsl.rb
    CHANGED
    
    | @@ -1,8 +1,12 @@ | |
| 1 1 | 
             
            module ROM
         | 
| 2 2 | 
             
              module SQL
         | 
| 3 | 
            +
                # Type DSL used by Types.define method
         | 
| 4 | 
            +
                #
         | 
| 5 | 
            +
                # @api public
         | 
| 3 6 | 
             
                class TypeDSL
         | 
| 4 7 | 
             
                  attr_reader :definition, :input_constructor, :output_constructor
         | 
| 5 8 |  | 
| 9 | 
            +
                  # @api private
         | 
| 6 10 | 
             
                  def initialize(value_type)
         | 
| 7 11 | 
             
                    if value_type.class < ::Dry::Types::Type
         | 
| 8 12 | 
             
                      @definition = value_type
         | 
| @@ -11,6 +15,7 @@ module ROM | |
| 11 15 | 
             
                    end
         | 
| 12 16 | 
             
                  end
         | 
| 13 17 |  | 
| 18 | 
            +
                  # @api private
         | 
| 14 19 | 
             
                  def call(&block)
         | 
| 15 20 | 
             
                    instance_exec(&block)
         | 
| 16 21 |  | 
| @@ -18,10 +23,12 @@ module ROM | |
| 18 23 | 
             
                      .meta(read: definition.constructor(output_constructor))
         | 
| 19 24 | 
             
                  end
         | 
| 20 25 |  | 
| 26 | 
            +
                  # @api private
         | 
| 21 27 | 
             
                  def input(&block)
         | 
| 22 28 | 
             
                    @input_constructor = block
         | 
| 23 29 | 
             
                  end
         | 
| 24 30 |  | 
| 31 | 
            +
                  # @api private
         | 
| 25 32 | 
             
                  def output(&block)
         | 
| 26 33 | 
             
                    @output_constructor = block
         | 
| 27 34 | 
             
                  end
         | 
    
        data/lib/rom/sql/types.rb
    CHANGED
    
    | @@ -9,18 +9,32 @@ module ROM | |
| 9 9 | 
             
                module Types
         | 
| 10 10 | 
             
                  include ROM::Types
         | 
| 11 11 |  | 
| 12 | 
            -
                   | 
| 13 | 
            -
             | 
| 14 | 
            -
                   | 
| 15 | 
            -
             | 
| 16 | 
            -
                   | 
| 17 | 
            -
             | 
| 18 | 
            -
                   | 
| 19 | 
            -
             | 
| 12 | 
            +
                  # Define a foreign key attribute type
         | 
| 13 | 
            +
                  #
         | 
| 14 | 
            +
                  # @example with default Int type
         | 
| 15 | 
            +
                  #   attribute :user_id, Types.ForeignKey(:users)
         | 
| 16 | 
            +
                  #
         | 
| 17 | 
            +
                  # @example with a custom type
         | 
| 18 | 
            +
                  #   attribute :user_id, Types.ForeignKey(:users, Types::UUID)
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # @return [Dry::Types::Definition]
         | 
| 21 | 
            +
                  #
         | 
| 22 | 
            +
                  # @api public
         | 
| 20 23 | 
             
                  def self.ForeignKey(relation, type = Types::Int.meta(index: true))
         | 
| 21 24 | 
             
                    super
         | 
| 22 25 | 
             
                  end
         | 
| 23 26 |  | 
| 27 | 
            +
                  # Define a complex attribute type using Type DSL
         | 
| 28 | 
            +
                  #
         | 
| 29 | 
            +
                  # @example
         | 
| 30 | 
            +
                  #   attribute :meta, Types.define(Types::JSON) do
         | 
| 31 | 
            +
                  #     input { Types::PG::JSON }
         | 
| 32 | 
            +
                  #     output { Types::Coercible::Hash }
         | 
| 33 | 
            +
                  #   end
         | 
| 34 | 
            +
                  #
         | 
| 35 | 
            +
                  # @return [Dry::Types::Definition]
         | 
| 36 | 
            +
                  #
         | 
| 37 | 
            +
                  # @api public
         | 
| 24 38 | 
             
                  def self.define(value_type, &block)
         | 
| 25 39 | 
             
                    TypeDSL.new(value_type).call(&block)
         | 
| 26 40 | 
             
                  end
         | 
    
        data/lib/rom/sql/version.rb
    CHANGED
    
    
    
        data/lib/rom/sql/wrap.rb
    CHANGED
    
    | @@ -2,21 +2,32 @@ require 'rom/relation/wrap' | |
| 2 2 |  | 
| 3 3 | 
             
            module ROM
         | 
| 4 4 | 
             
              module SQL
         | 
| 5 | 
            +
                # Specialized wrap relation for SQL
         | 
| 6 | 
            +
                #
         | 
| 7 | 
            +
                # This type of relations is returned when using `Relation#wrap` and it uses
         | 
| 8 | 
            +
                # a join, unlike `Relation#combine`, which means a relation is restricted
         | 
| 9 | 
            +
                # only to tuples which have associated tuples. It should be used in cases
         | 
| 10 | 
            +
                # where you want to rely on this restriction.
         | 
| 11 | 
            +
                #
         | 
| 12 | 
            +
                # @api public
         | 
| 5 13 | 
             
                class Wrap < Relation::Wrap
         | 
| 14 | 
            +
                  # Return a schema which includes attributes from wrapped relations
         | 
| 15 | 
            +
                  #
         | 
| 16 | 
            +
                  # @return [Schema]
         | 
| 17 | 
            +
                  #
         | 
| 6 18 | 
             
                  # @api public
         | 
| 7 19 | 
             
                  def schema
         | 
| 8 20 | 
             
                    root.schema.merge(nodes.map(&:schema).reduce(:merge)).qualified
         | 
| 9 21 | 
             
                  end
         | 
| 10 22 |  | 
| 23 | 
            +
                  # Internal method used by abstract `ROM::Relation::Wrap`
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  # @return [Relation]
         | 
| 26 | 
            +
                  #
         | 
| 11 27 | 
             
                  # @api private
         | 
| 12 28 | 
             
                  def relation
         | 
| 13 29 | 
             
                    relation = nodes.reduce(root) do |a, e|
         | 
| 14 | 
            -
                       | 
| 15 | 
            -
                        a.associations[e.name.key].join(:join, a, e)
         | 
| 16 | 
            -
                      else
         | 
| 17 | 
            -
                        # TODO: deprecate this before 2.0
         | 
| 18 | 
            -
                        a.qualified.join(e.name.dataset, e.meta[:keys])
         | 
| 19 | 
            -
                      end
         | 
| 30 | 
            +
                      a.associations[e.name.key].join(:join, a, e)
         | 
| 20 31 | 
             
                    end
         | 
| 21 32 | 
             
                    schema.(relation)
         | 
| 22 33 | 
             
                  end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,29 +1,29 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: rom-sql
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2.0.0. | 
| 4 | 
            +
              version: 2.0.0.rc1
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Piotr Solnica
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2017- | 
| 11 | 
            +
            date: 2017-09-15 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: sequel
         | 
| 15 15 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 16 | 
             
                requirements:
         | 
| 17 | 
            -
                - - " | 
| 17 | 
            +
                - - ">="
         | 
| 18 18 | 
             
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            -
                    version: '4. | 
| 19 | 
            +
                    version: '4.49'
         | 
| 20 20 | 
             
              type: :runtime
         | 
| 21 21 | 
             
              prerelease: false
         | 
| 22 22 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 23 | 
             
                requirements:
         | 
| 24 | 
            -
                - - " | 
| 24 | 
            +
                - - ">="
         | 
| 25 25 | 
             
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            -
                    version: '4. | 
| 26 | 
            +
                    version: '4.49'
         | 
| 27 27 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 28 28 | 
             
              name: dry-equalizer
         | 
| 29 29 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -44,20 +44,14 @@ dependencies: | |
| 44 44 | 
             
                requirements:
         | 
| 45 45 | 
             
                - - "~>"
         | 
| 46 46 | 
             
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            -
                    version: '0. | 
| 48 | 
            -
                - - ">="
         | 
| 49 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 50 | 
            -
                    version: 0.11.1
         | 
| 47 | 
            +
                    version: '0.12'
         | 
| 51 48 | 
             
              type: :runtime
         | 
| 52 49 | 
             
              prerelease: false
         | 
| 53 50 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 54 51 | 
             
                requirements:
         | 
| 55 52 | 
             
                - - "~>"
         | 
| 56 53 | 
             
                  - !ruby/object:Gem::Version
         | 
| 57 | 
            -
                    version: '0. | 
| 58 | 
            -
                - - ">="
         | 
| 59 | 
            -
                  - !ruby/object:Gem::Version
         | 
| 60 | 
            -
                    version: 0.11.1
         | 
| 54 | 
            +
                    version: '0.12'
         | 
| 61 55 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 62 56 | 
             
              name: dry-core
         | 
| 63 57 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -78,14 +72,14 @@ dependencies: | |
| 78 72 | 
             
                requirements:
         | 
| 79 73 | 
             
                - - "~>"
         | 
| 80 74 | 
             
                  - !ruby/object:Gem::Version
         | 
| 81 | 
            -
                    version: 4.0.0. | 
| 75 | 
            +
                    version: 4.0.0.rc
         | 
| 82 76 | 
             
              type: :runtime
         | 
| 83 77 | 
             
              prerelease: false
         | 
| 84 78 | 
             
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 85 79 | 
             
                requirements:
         | 
| 86 80 | 
             
                - - "~>"
         | 
| 87 81 | 
             
                  - !ruby/object:Gem::Version
         | 
| 88 | 
            -
                    version: 4.0.0. | 
| 82 | 
            +
                    version: 4.0.0.rc
         | 
| 89 83 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 90 84 | 
             
              name: bundler
         | 
| 91 85 | 
             
              requirement: !ruby/object:Gem::Requirement
         | 
| @@ -228,7 +222,7 @@ required_ruby_version: !ruby/object:Gem::Requirement | |
| 228 222 | 
             
              requirements:
         | 
| 229 223 | 
             
              - - ">="
         | 
| 230 224 | 
             
                - !ruby/object:Gem::Version
         | 
| 231 | 
            -
                  version:  | 
| 225 | 
            +
                  version: 2.3.0
         | 
| 232 226 | 
             
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 233 227 | 
             
              requirements:
         | 
| 234 228 | 
             
              - - ">"
         |