· 8 years ago · Mar 15, 2018, 12:16 AM
1#!/usr/bin/env ruby -Ku
2
3# encoding: utf-8
4
5require 'rubygems'
6require 'dm-core'
7require 'pp'
8
9module DataMapper
10 class Collection
11 def union(other)
12 assert_no_loaded_entries(:union)
13 new_collection(query.union(other.query))
14 end
15
16 alias | union
17 alias + union
18
19 def intersection(other)
20 assert_no_loaded_entries(:intersection)
21 new_collection(query.intersection(other.query))
22 end
23
24 alias & intersection
25
26 def difference(other)
27 assert_no_loaded_entries(:difference)
28 new_collection(query.difference(other.query))
29 end
30
31 alias - difference
32
33 private
34
35 def assert_no_loaded_entries(method)
36 unless loaded_entries.empty?
37 raise ArgumentError, "#{self.class}##{method} cannot be called on a loaded collection"
38 end
39 end
40 end
41
42 class Query
43 attr_accessor :conditions
44
45 def union(other)
46 assert_same_options(other)
47 query = dup
48 query.conditions = query.conditions.union(other.conditions)
49 query
50 end
51
52 alias | union
53 alias + union
54
55 def intersection(other)
56 assert_same_options(other)
57 query = dup
58 query.conditions = query.conditions.intersection(other.conditions)
59 query
60 end
61
62 alias & intersection
63
64 def difference(other)
65 assert_same_options(other)
66 query = dup
67 query.conditions = query.conditions.difference(other.conditions)
68 query
69 end
70
71 alias - difference
72
73 private
74
75 def assert_same_options(other)
76 # TODO: assert all the ivars, aside from @conditions, are identical
77 end
78
79 module Conditions
80 class AbstractOperation
81
82 def union(other)
83 Operation.new(:or, dup, other.dup)
84 end
85
86 alias | union
87 alias + union
88
89 def intersection(other)
90 Operation.new(:and, dup, other.dup)
91 end
92
93 alias & intersection
94
95 def difference(other)
96 Operation.new(:and, dup, Operation.new(:not, other.dup))
97 end
98
99 alias - difference
100 end
101 end
102 end
103end
104
105DataMapper::Logger.new($stdout, :debug)
106DataMapper.setup(:default, 'sqlite3:memory:')
107
108class Customer
109 include DataMapper::Resource
110
111 property :id, Serial
112 property :name, String
113 property :age, Integer
114
115 has n, :orders
116end
117
118class Order
119 include DataMapper::Resource
120
121 property :id, Serial
122 property :reference_code, String
123
124 belongs_to :customer
125end
126
127DataMapper.auto_migrate!
128
129customer1 = Customer.create(:name => 'Dan Kubb', :age => 34)
130customer2 = Customer.create(:name => 'Alex Kubb', :age => 2)
131
132customer1.orders.create(:reference_code => 'TEST123')
133customer2.orders.create(:reference_code => 'TEST321')
134
135puts '-' * 80, 'Union:'
136pp Customer.all(:name => 'Dan Kubb') | Customer.all(:name => 'Alex Kubb')
137pp Customer.all(:name => 'Dan Kubb') + Customer.all(:name => 'Alex Kubb')
138
139puts '-' * 80, 'Intersection:'
140pp Customer.all(:name => 'Dan Kubb') & Customer.all(:name => 'Alex Kubb')
141
142puts '-' * 80, 'Difference:'
143pp Customer.all(:name => 'Dan Kubb') - Customer.all(:name => 'Alex Kubb')
144
145__END__
146
147 ~ (0.000258) SELECT sqlite_version(*)
148 ~ (0.002850) DROP TABLE IF EXISTS "customers"
149 ~ (0.001784) DROP TABLE IF EXISTS "orders"
150 ~ (0.000038) PRAGMA table_info("customers")
151 ~ (0.001835) CREATE TABLE "customers" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "name" VARCHAR(50), "age" INTEGER)
152 ~ (0.000021) PRAGMA table_info("orders")
153 ~ (0.002111) CREATE TABLE "orders" ("id" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, "reference_code" VARCHAR(50), "customer_id" INTEGER NOT NULL)
154 ~ (0.002192) CREATE INDEX "index_orders_customer" ON "orders" ("customer_id")
155 ~ (0.001891) INSERT INTO "customers" ("name", "age") VALUES ('Dan Kubb', 34)
156 ~ (0.004047) INSERT INTO "customers" ("name", "age") VALUES ('Alex Kubb', 2)
157 ~ (0.003897) INSERT INTO "orders" ("reference_code", "customer_id") VALUES ('TEST123', 1)
158 ~ (0.002357) INSERT INTO "orders" ("reference_code", "customer_id") VALUES ('TEST321', 2)
159--------------------------------------------------------------------------------
160Union:
161 ~ (0.000104) SELECT "id", "name", "age" FROM "customers" WHERE ("name" = 'Dan Kubb' OR "name" = 'Alex Kubb') ORDER BY "id"
162[#<Customer @id=1 @name="Dan Kubb" @age=34>, #<Customer @id=2 @name="Alex Kubb" @age=2>]
163 ~ (0.000096) SELECT "id", "name", "age" FROM "customers" WHERE ("name" = 'Dan Kubb' OR "name" = 'Alex Kubb') ORDER BY "id"
164[#<Customer @id=1 @name="Dan Kubb" @age=34>, #<Customer @id=2 @name="Alex Kubb" @age=2>]
165--------------------------------------------------------------------------------
166Intersection:
167 ~ (0.000051) SELECT "id", "name", "age" FROM "customers" WHERE ("name" = 'Dan Kubb' AND "name" = 'Alex Kubb') ORDER BY "id"
168[]
169--------------------------------------------------------------------------------
170Difference:
171 ~ (0.000153) SELECT "id", "name", "age" FROM "customers" WHERE ("name" = 'Dan Kubb' AND "name" <> 'Alex Kubb') ORDER BY "id"
172[#<Customer @id=1 @name="Dan Kubb" @age=34>]