dm-core 0.10.2 → 1.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.
Files changed (183) hide show
  1. data/.gitignore +10 -1
  2. data/Gemfile +143 -0
  3. data/Rakefile +9 -5
  4. data/VERSION +1 -1
  5. data/dm-core.gemspec +160 -57
  6. data/lib/dm-core.rb +131 -56
  7. data/lib/dm-core/adapters.rb +98 -14
  8. data/lib/dm-core/adapters/abstract_adapter.rb +24 -4
  9. data/lib/dm-core/adapters/in_memory_adapter.rb +7 -2
  10. data/lib/dm-core/associations/many_to_many.rb +19 -30
  11. data/lib/dm-core/associations/many_to_one.rb +58 -42
  12. data/lib/dm-core/associations/one_to_many.rb +33 -23
  13. data/lib/dm-core/associations/one_to_one.rb +27 -11
  14. data/lib/dm-core/associations/relationship.rb +4 -4
  15. data/lib/dm-core/collection.rb +23 -16
  16. data/lib/dm-core/core_ext/array.rb +36 -0
  17. data/lib/dm-core/core_ext/hash.rb +30 -0
  18. data/lib/dm-core/core_ext/module.rb +46 -0
  19. data/lib/dm-core/core_ext/object.rb +31 -0
  20. data/lib/dm-core/core_ext/pathname.rb +20 -0
  21. data/lib/dm-core/core_ext/string.rb +22 -0
  22. data/lib/dm-core/core_ext/try_dup.rb +44 -0
  23. data/lib/dm-core/model.rb +88 -27
  24. data/lib/dm-core/model/hook.rb +75 -18
  25. data/lib/dm-core/model/property.rb +50 -9
  26. data/lib/dm-core/model/relationship.rb +31 -31
  27. data/lib/dm-core/model/scope.rb +3 -3
  28. data/lib/dm-core/property.rb +196 -516
  29. data/lib/dm-core/property/binary.rb +7 -0
  30. data/lib/dm-core/property/boolean.rb +35 -0
  31. data/lib/dm-core/property/class.rb +24 -0
  32. data/lib/dm-core/property/date.rb +47 -0
  33. data/lib/dm-core/property/date_time.rb +48 -0
  34. data/lib/dm-core/property/decimal.rb +43 -0
  35. data/lib/dm-core/property/discriminator.rb +48 -0
  36. data/lib/dm-core/property/float.rb +24 -0
  37. data/lib/dm-core/property/integer.rb +32 -0
  38. data/lib/dm-core/property/numeric.rb +43 -0
  39. data/lib/dm-core/property/object.rb +32 -0
  40. data/lib/dm-core/property/serial.rb +8 -0
  41. data/lib/dm-core/property/string.rb +49 -0
  42. data/lib/dm-core/property/text.rb +12 -0
  43. data/lib/dm-core/property/time.rb +48 -0
  44. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  45. data/lib/dm-core/property/typecast/time.rb +28 -0
  46. data/lib/dm-core/property_set.rb +10 -4
  47. data/lib/dm-core/query.rb +14 -37
  48. data/lib/dm-core/query/conditions/comparison.rb +8 -6
  49. data/lib/dm-core/query/conditions/operation.rb +33 -2
  50. data/lib/dm-core/query/operator.rb +2 -5
  51. data/lib/dm-core/query/path.rb +4 -6
  52. data/lib/dm-core/repository.rb +21 -6
  53. data/lib/dm-core/resource.rb +316 -133
  54. data/lib/dm-core/resource/state.rb +79 -0
  55. data/lib/dm-core/resource/state/clean.rb +40 -0
  56. data/lib/dm-core/resource/state/deleted.rb +30 -0
  57. data/lib/dm-core/resource/state/dirty.rb +86 -0
  58. data/lib/dm-core/resource/state/immutable.rb +34 -0
  59. data/lib/dm-core/resource/state/persisted.rb +29 -0
  60. data/lib/dm-core/resource/state/transient.rb +70 -0
  61. data/lib/dm-core/spec/lib/adapter_helpers.rb +52 -0
  62. data/lib/dm-core/spec/lib/collection_helpers.rb +20 -0
  63. data/{spec → lib/dm-core/spec}/lib/counter_adapter.rb +5 -1
  64. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  65. data/lib/dm-core/spec/lib/spec_helper.rb +68 -0
  66. data/lib/dm-core/spec/setup.rb +165 -0
  67. data/lib/dm-core/spec/{adapter_shared_spec.rb → shared/adapter_spec.rb} +21 -7
  68. data/{spec/public/shared/resource_shared_spec.rb → lib/dm-core/spec/shared/resource_spec.rb} +120 -83
  69. data/{spec/public/shared/sel_shared_spec.rb → lib/dm-core/spec/shared/sel_spec.rb} +5 -6
  70. data/lib/dm-core/support/assertions.rb +8 -0
  71. data/lib/dm-core/support/equalizer.rb +1 -0
  72. data/lib/dm-core/support/hook.rb +420 -0
  73. data/lib/dm-core/support/lazy_array.rb +453 -0
  74. data/lib/dm-core/support/local_object_space.rb +12 -0
  75. data/lib/dm-core/support/logger.rb +193 -6
  76. data/lib/dm-core/support/naming_conventions.rb +8 -8
  77. data/lib/dm-core/support/subject.rb +33 -0
  78. data/lib/dm-core/type.rb +4 -0
  79. data/lib/dm-core/types/boolean.rb +2 -0
  80. data/lib/dm-core/types/decimal.rb +9 -0
  81. data/lib/dm-core/types/discriminator.rb +2 -0
  82. data/lib/dm-core/types/object.rb +3 -0
  83. data/lib/dm-core/types/serial.rb +2 -0
  84. data/lib/dm-core/types/text.rb +2 -0
  85. data/lib/dm-core/version.rb +1 -1
  86. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +67 -0
  87. data/spec/public/model/hook_spec.rb +209 -0
  88. data/spec/public/model/property_spec.rb +35 -0
  89. data/spec/public/model/relationship_spec.rb +33 -20
  90. data/spec/public/model_spec.rb +142 -10
  91. data/spec/public/property/binary_spec.rb +14 -0
  92. data/spec/public/property/boolean_spec.rb +14 -0
  93. data/spec/public/property/class_spec.rb +20 -0
  94. data/spec/public/property/date_spec.rb +14 -0
  95. data/spec/public/property/date_time_spec.rb +14 -0
  96. data/spec/public/property/decimal_spec.rb +14 -0
  97. data/spec/public/{types → property}/discriminator_spec.rb +2 -12
  98. data/spec/public/property/float_spec.rb +14 -0
  99. data/spec/public/property/integer_spec.rb +14 -0
  100. data/spec/public/property/object_spec.rb +9 -17
  101. data/spec/public/property/serial_spec.rb +14 -0
  102. data/spec/public/property/string_spec.rb +14 -0
  103. data/spec/public/property/text_spec.rb +52 -0
  104. data/spec/public/property/time_spec.rb +14 -0
  105. data/spec/public/property_spec.rb +28 -87
  106. data/spec/public/resource_spec.rb +101 -0
  107. data/spec/public/sel_spec.rb +5 -15
  108. data/spec/public/shared/collection_shared_spec.rb +16 -30
  109. data/spec/public/shared/finder_shared_spec.rb +2 -4
  110. data/spec/public/shared/property_shared_spec.rb +176 -0
  111. data/spec/semipublic/adapters/abstract_adapter_spec.rb +1 -1
  112. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +2 -2
  113. data/spec/semipublic/associations/many_to_many_spec.rb +89 -0
  114. data/spec/semipublic/associations/many_to_one_spec.rb +24 -1
  115. data/spec/semipublic/associations/one_to_many_spec.rb +51 -0
  116. data/spec/semipublic/associations/one_to_one_spec.rb +49 -0
  117. data/spec/semipublic/associations/relationship_spec.rb +3 -3
  118. data/spec/semipublic/associations_spec.rb +1 -1
  119. data/spec/semipublic/property/binary_spec.rb +13 -0
  120. data/spec/semipublic/property/boolean_spec.rb +65 -0
  121. data/spec/semipublic/property/class_spec.rb +33 -0
  122. data/spec/semipublic/property/date_spec.rb +43 -0
  123. data/spec/semipublic/property/date_time_spec.rb +46 -0
  124. data/spec/semipublic/property/decimal_spec.rb +82 -0
  125. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  126. data/spec/semipublic/property/float_spec.rb +82 -0
  127. data/spec/semipublic/property/integer_spec.rb +82 -0
  128. data/spec/semipublic/property/serial_spec.rb +13 -0
  129. data/spec/semipublic/property/string_spec.rb +13 -0
  130. data/spec/semipublic/property/text_spec.rb +31 -0
  131. data/spec/semipublic/property/time_spec.rb +50 -0
  132. data/spec/semipublic/property_spec.rb +2 -532
  133. data/spec/semipublic/query/conditions/comparison_spec.rb +171 -169
  134. data/spec/semipublic/query/conditions/operation_spec.rb +53 -51
  135. data/spec/semipublic/query/path_spec.rb +17 -17
  136. data/spec/semipublic/query_spec.rb +47 -78
  137. data/spec/semipublic/resource/state/clean_spec.rb +88 -0
  138. data/spec/semipublic/resource/state/deleted_spec.rb +78 -0
  139. data/spec/semipublic/resource/state/dirty_spec.rb +133 -0
  140. data/spec/semipublic/resource/state/immutable_spec.rb +99 -0
  141. data/spec/semipublic/resource/state/transient_spec.rb +128 -0
  142. data/spec/semipublic/resource/state_spec.rb +226 -0
  143. data/spec/semipublic/shared/property_shared_spec.rb +143 -0
  144. data/spec/semipublic/shared/resource_shared_spec.rb +16 -15
  145. data/spec/semipublic/shared/resource_state_shared_spec.rb +78 -0
  146. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  147. data/spec/spec_helper.rb +21 -97
  148. data/spec/support/types/huge_integer.rb +17 -0
  149. data/spec/unit/array_spec.rb +48 -0
  150. data/spec/unit/hash_spec.rb +35 -0
  151. data/spec/unit/hook_spec.rb +1234 -0
  152. data/spec/unit/lazy_array_spec.rb +1959 -0
  153. data/spec/unit/module_spec.rb +70 -0
  154. data/spec/unit/object_spec.rb +37 -0
  155. data/spec/unit/try_dup_spec.rb +45 -0
  156. data/tasks/local_gemfile.rake +18 -0
  157. data/tasks/spec.rake +0 -3
  158. metadata +197 -71
  159. data/deps.rip +0 -2
  160. data/lib/dm-core/adapters/data_objects_adapter.rb +0 -712
  161. data/lib/dm-core/adapters/mysql_adapter.rb +0 -42
  162. data/lib/dm-core/adapters/oracle_adapter.rb +0 -229
  163. data/lib/dm-core/adapters/postgres_adapter.rb +0 -22
  164. data/lib/dm-core/adapters/sqlite3_adapter.rb +0 -17
  165. data/lib/dm-core/adapters/sqlserver_adapter.rb +0 -114
  166. data/lib/dm-core/adapters/yaml_adapter.rb +0 -111
  167. data/lib/dm-core/core_ext/enumerable.rb +0 -28
  168. data/lib/dm-core/migrations.rb +0 -1427
  169. data/lib/dm-core/spec/data_objects_adapter_shared_spec.rb +0 -366
  170. data/lib/dm-core/transaction.rb +0 -508
  171. data/lib/dm-core/types/paranoid_boolean.rb +0 -42
  172. data/lib/dm-core/types/paranoid_datetime.rb +0 -41
  173. data/spec/lib/adapter_helpers.rb +0 -105
  174. data/spec/lib/collection_helpers.rb +0 -18
  175. data/spec/lib/pending_helpers.rb +0 -46
  176. data/spec/public/migrations_spec.rb +0 -503
  177. data/spec/public/transaction_spec.rb +0 -153
  178. data/spec/semipublic/adapters/mysql_adapter_spec.rb +0 -17
  179. data/spec/semipublic/adapters/oracle_adapter_spec.rb +0 -194
  180. data/spec/semipublic/adapters/postgres_adapter_spec.rb +0 -17
  181. data/spec/semipublic/adapters/sqlite3_adapter_spec.rb +0 -17
  182. data/spec/semipublic/adapters/sqlserver_adapter_spec.rb +0 -17
  183. data/spec/semipublic/adapters/yaml_adapter_spec.rb +0 -12
@@ -1,42 +0,0 @@
1
- require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
2
-
3
- require 'do_mysql'
4
-
5
- module DataMapper
6
- module Adapters
7
- class MysqlAdapter < DataObjectsAdapter
8
- module SQL #:nodoc:
9
- IDENTIFIER_MAX_LENGTH = 64
10
-
11
- private
12
-
13
- # @api private
14
- def supports_default_values? #:nodoc:
15
- false
16
- end
17
-
18
- # @api private
19
- def supports_subquery?(query, source_key, target_key, qualify)
20
- # TODO: renable once query does not include target_model for deletes and updates
21
- # query.limit.nil?
22
-
23
- false
24
- end
25
-
26
- # @api private
27
- def regexp_operator(operand)
28
- 'REGEXP'
29
- end
30
-
31
- # @api private
32
- def quote_name(name)
33
- "`#{name[0, self.class::IDENTIFIER_MAX_LENGTH].gsub('`', '``')}`"
34
- end
35
- end #module SQL
36
-
37
- include SQL
38
- end # class MysqlAdapter
39
-
40
- const_added(:MysqlAdapter)
41
- end # module Adapters
42
- end # module DataMapper
@@ -1,229 +0,0 @@
1
- require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
2
-
3
- require 'do_oracle'
4
-
5
- module DataMapper
6
-
7
- class Property
8
- # for custom sequence names
9
- OPTIONS << :sequence
10
- end
11
-
12
- module Adapters
13
- class OracleAdapter < DataObjectsAdapter
14
- module SQL #:nodoc:
15
- IDENTIFIER_MAX_LENGTH = 30
16
-
17
- private
18
-
19
- # Constructs INSERT statement for given query,
20
- #
21
- # @return [String] INSERT statement as a string
22
- #
23
- # @api private
24
- def insert_statement(model, properties, serial)
25
- statement = "INSERT INTO #{quote_name(model.storage_name(name))} "
26
-
27
- no_properties = properties.empty?
28
- custom_sequence = serial && serial.options[:sequence]
29
- serial_field = serial && quote_name(serial.field)
30
-
31
- if supports_default_values? && no_properties && !custom_sequence
32
- statement << "(#{serial_field}) " if serial
33
- statement << default_values_clause
34
- else
35
- # do not use custom sequence if identity field was assigned a value
36
- if custom_sequence && properties.include?(serial)
37
- custom_sequence = nil
38
- end
39
- statement << "("
40
- if custom_sequence
41
- statement << "#{serial_field}"
42
- statement << ", " unless no_properties
43
- end
44
- statement << "#{properties.map { |property| quote_name(property.field) }.join(', ')}) "
45
- statement << "VALUES ("
46
- if custom_sequence
47
- statement << "#{quote_name(custom_sequence)}.NEXTVAL"
48
- statement << ", " unless no_properties
49
- end
50
- statement << "#{(['?'] * properties.size).join(', ')})"
51
- end
52
-
53
- if supports_returning? && serial
54
- statement << returning_clause(serial)
55
- end
56
-
57
- statement
58
- end
59
-
60
- # Oracle syntax for inserting default values
61
- def default_values_clause
62
- 'VALUES (DEFAULT)'
63
- end
64
-
65
- # @api private
66
- def supports_returning?
67
- true
68
- end
69
-
70
- # INTO :insert_id is recognized by Oracle DataObjects driver
71
- def returning_clause(serial)
72
- " RETURNING #{quote_name(serial.field)} INTO :insert_id"
73
- end
74
-
75
- # Constructs SELECT statement for given query,
76
- # Overrides DataObjects adapter implementation with using subquery instead of GROUP BY to get unique records
77
- #
78
- # @return [String] SELECT statement as a string
79
- #
80
- # @api private
81
- def select_statement(query)
82
- name = self.name
83
- model = query.model
84
- fields = query.fields
85
- conditions = query.conditions
86
- limit = query.limit
87
- offset = query.offset
88
- order = query.order
89
- group_by = nil
90
-
91
- # FIXME: using a boolean for qualify does not work in some cases,
92
- # such as when you have a self-referrential many to many association.
93
- # if you don't qualfiy the columns with a unique alias, then the
94
- # SQL query will fail. This may mean though, that it might not
95
- # be enough to pass in a Property, but we may need to know the
96
- # table and the alias we should use for the column.
97
-
98
- qualify = query.links.any?
99
-
100
- if query.unique?
101
- group_by = fields.select { |property| property.kind_of?(Property) }
102
- end
103
-
104
- # create subquery to find all valid keys and then use these keys to retrive all other columns
105
- use_subquery = qualify
106
- no_group_by = group_by.blank?
107
- no_order = order.blank?
108
-
109
- # when we can include ROWNUM condition in main WHERE clause
110
- use_simple_rownum_limit = limit && (offset||0 == 0) && no_group_by && no_order
111
-
112
- unless (limit && limit > 1) || offset > 0 || qualify
113
- # TODO: move this method to Query, so that it walks the conditions
114
- # and finds an OR operator
115
-
116
- # TODO: handle cases where two or more properties need to be
117
- # used together to be unique
118
-
119
- # if a unique property is used, and there is no OR operator, then an ORDER
120
- # and LIMIT are unecessary because it should only return a single row
121
- if conditions.respond_to?(:slug) && conditions.slug == :and &&
122
- conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :eql && operand.subject.respond_to?(:unique?) && operand.subject.unique? } &&
123
- !conditions.any? { |operand| operand.respond_to?(:slug) && operand.slug == :or }
124
- order = nil
125
- no_order = true
126
- limit = nil
127
- end
128
- end
129
-
130
- conditions_statement, bind_values = conditions_statement(conditions, qualify)
131
-
132
- model_key_column = columns_statement(model.key(name), qualify)
133
- from_statement = " FROM #{quote_name(model.storage_name(name))}"
134
-
135
- statement = "SELECT #{columns_statement(fields, qualify)}"
136
- if use_subquery
137
- statement << from_statement
138
- statement << " WHERE (#{model_key_column}) IN"
139
- statement << " (SELECT DISTINCT #{model_key_column}"
140
- # do not need to do group by for uniqueness as just one row per primary key will be returned
141
- no_group_by = true
142
- end
143
- statement << from_statement
144
- statement << join_statement(query, qualify) if qualify
145
- statement << " WHERE (#{conditions_statement})" unless conditions_statement.blank?
146
- if use_subquery
147
- statement << ")"
148
- end
149
- if use_simple_rownum_limit
150
- statement << " AND rownum <= ?"
151
- bind_values << limit
152
- end
153
- statement << " GROUP BY #{columns_statement(group_by, qualify)}" unless no_group_by
154
- statement << " ORDER BY #{order_statement(order, qualify)}" unless no_order
155
-
156
- add_limit_offset!(statement, limit, offset, bind_values) unless use_simple_rownum_limit
157
-
158
- return statement, bind_values
159
- end
160
-
161
- # Oracle does not support LIMIT and OFFSET
162
- # Functionality is mimiced through the use of nested selects.
163
- # See http://asktom.oracle.com/pls/ask/f?p=4950:8:::::F4950_P8_DISPLAYID:127412348064
164
- def add_limit_offset!(statement, limit, offset, bind_values)
165
- positive_offset = offset > 0
166
-
167
- if limit && positive_offset
168
- statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_ where rownum <= ?) where raw_rnum_ > ?"
169
- bind_values << offset + limit << offset
170
- elsif limit
171
- statement.replace "select raw_sql_.* from (#{statement}) raw_sql_ where rownum <= ?"
172
- bind_values << limit
173
- elsif positive_offset
174
- statement.replace "select * from (select raw_sql_.*, rownum raw_rnum_ from (#{statement}) raw_sql_) where raw_rnum_ > ?"
175
- bind_values << offset
176
- end
177
- end
178
-
179
- # @api private
180
- # Oracle does not allow " in table or column names therefore substitute them with underscore
181
- def quote_name(name)
182
- "\"#{oracle_upcase(name)[0, self.class::IDENTIFIER_MAX_LENGTH].gsub('"', '_')}\""
183
- end
184
-
185
- # If table or column name contains just lowercase characters then do uppercase
186
- # as uppercase version will be used in Oracle data dictionary tables
187
- def oracle_upcase(name)
188
- name =~ /[A-Z]/ ? name : name.upcase
189
- end
190
-
191
- # CLOB value should be compared using DBMS_LOB.SUBSTR function
192
- # NOTE: just first 32767 bytes will be compared!
193
- # @api private
194
- def equality_operator(property, operand)
195
- if operand.nil?
196
- 'IS'
197
- elsif property.type == Types::Text
198
- 'DBMS_LOB.SUBSTR(%s) = ?'
199
- else
200
- '='
201
- end
202
- end
203
-
204
- # @api private
205
- def include_operator(property, operand)
206
- operator = case operand
207
- when Array then 'IN'
208
- when Range then 'BETWEEN'
209
- end
210
- if property.type == Types::Text
211
- "DBMS_LOB.SUBSTR(%s) #{operator} ?"
212
- else
213
- operator
214
- end
215
- end
216
-
217
- # @api private
218
- def regexp_operator(operand)
219
- 'REGEXP_LIKE(%s, ?)'
220
- end
221
-
222
- end #module SQL
223
-
224
- include SQL
225
- end # class PostgresAdapter
226
-
227
- const_added(:OracleAdapter)
228
- end # module Adapters
229
- end # module DataMapper
@@ -1,22 +0,0 @@
1
- require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
2
-
3
- require 'do_postgres'
4
-
5
- module DataMapper
6
- module Adapters
7
- class PostgresAdapter < DataObjectsAdapter
8
- module SQL #:nodoc:
9
- private
10
-
11
- # @api private
12
- def supports_returning?
13
- true
14
- end
15
- end #module SQL
16
-
17
- include SQL
18
- end # class PostgresAdapter
19
-
20
- const_added(:PostgresAdapter)
21
- end # module Adapters
22
- end # module DataMapper
@@ -1,17 +0,0 @@
1
- require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
2
-
3
- require 'do_sqlite3'
4
-
5
- module DataMapper
6
- module Adapters
7
- class Sqlite3Adapter < DataObjectsAdapter
8
- # @api private
9
- def supports_subquery?(query, source_key, target_key, qualify)
10
- # SQLite3 cannot match a subquery against more than one column
11
- source_key.size == 1 && target_key.size == 1
12
- end
13
- end # class Sqlite3Adapter
14
-
15
- const_added(:Sqlite3Adapter)
16
- end # module Adapters
17
- end # module DataMapper
@@ -1,114 +0,0 @@
1
- require DataMapper.root / 'lib' / 'dm-core' / 'adapters' / 'data_objects_adapter'
2
-
3
- require 'do_sqlserver'
4
-
5
- DataObjects::Sqlserver = DataObjects::SqlServer
6
-
7
- module DataMapper
8
- module Adapters
9
- class SqlserverAdapter < DataObjectsAdapter
10
- module SQL #:nodoc:
11
- private
12
-
13
- # Constructs INSERT statement for given query,
14
- #
15
- # @return [String] INSERT statement as a string
16
- #
17
- # @api private
18
- def insert_statement(model, properties, serial)
19
- statement = ""
20
- # Check if there is a serial property being set directly
21
- require_identity_insert = !properties.empty? && properties.any? { |property| property.serial? }
22
- set_identity_insert(model, statement, true) if require_identity_insert
23
- statement << super
24
- set_identity_insert(model, statement, false) if require_identity_insert
25
- statement
26
- end
27
-
28
- def set_identity_insert(model, statement, enable = true)
29
- statement << " SET IDENTITY_INSERT #{quote_name(model.storage_name(name))} #{enable ? 'ON' : 'OFF'} "
30
- end
31
-
32
- def select_statement(query)
33
- name = self.name
34
- qualify = query.links.any?
35
- fields = query.fields
36
- offset = query.offset
37
- limit = query.limit
38
- order_by = query.order
39
- group_by = if qualify || query.unique?
40
- fields.select { |property| property.kind_of?(Property) }
41
- end
42
-
43
- conditions_statement, bind_values = conditions_statement(query.conditions, qualify)
44
-
45
- use_limit_offset_subquery = limit && offset > 0
46
-
47
- columns_statement = columns_statement(fields, qualify)
48
- from_statement = " FROM #{quote_name(query.model.storage_name(name))}"
49
- where_statement = " WHERE #{conditions_statement}" unless conditions_statement.blank?
50
- join_statement = join_statement(query, qualify)
51
- order_statement = order_statement(order_by, qualify)
52
- no_group_by = group_by ? group_by.empty? : true
53
- no_order_by = order_by ? order_by.empty? : true
54
-
55
- if use_limit_offset_subquery
56
- # If using qualifiers, we must qualify elements outside the subquery
57
- # with 'RowResults' -- this is a different scope to the subquery.
58
- # Otherwise, we hit upon "multi-part identifier cannot be bound"
59
- # error from SQL Server.
60
- statement = "SELECT #{columns_statement(fields, qualify, 'RowResults')}"
61
- statement << " FROM ( SELECT Row_Number() OVER (ORDER BY #{order_statement}) AS RowID,"
62
- statement << " #{columns_statement}"
63
- statement << from_statement
64
- statement << join_statement if qualify
65
- statement << where_statement if where_statement
66
- statement << ") AS RowResults"
67
- statement << " WHERE RowId > #{offset} AND RowId <= #{offset + limit}"
68
- statement << " GROUP BY #{columns_statement(group_by, qualify, 'RowResults')}" unless no_group_by
69
- statement << " ORDER BY #{order_statement(order_by, qualify, 'RowResults')}" unless no_order_by
70
- else
71
- statement = "SELECT #{columns_statement}"
72
- statement << from_statement
73
- statement << join_statement if qualify
74
- statement << where_statement if where_statement
75
- statement << " GROUP BY #{columns_statement(group_by, qualify)}" unless no_group_by
76
- statement << " ORDER BY #{order_statement}" unless no_order_by
77
- end
78
-
79
- add_limit_offset!(statement, limit, offset, bind_values) unless use_limit_offset_subquery
80
-
81
- return statement, bind_values
82
- end
83
-
84
- # SQL Server does not support LIMIT and OFFSET
85
- # Functionality therefore must be mimicked through the use of nested selects.
86
- # See also:
87
- # - http://stackoverflow.com/questions/2840/paging-sql-server-2005-results
88
- # - http://stackoverflow.com/questions/216673/emulate-mysql-limit-clause-in-microsoft-sql-server-2000
89
- #
90
- def add_limit_offset!(statement, limit, offset, bind_values)
91
- # Limit and offset is handled by subqueries (see #select_statement).
92
- if limit
93
- # If there is just a limit on rows to return, but no offset, then we
94
- # can use TOP clause.
95
- statement.sub!(/^\s*SELECT(\s+DISTINCT)?/i) { "SELECT#{$1} TOP #{limit}" }
96
- # bind_values << limit
97
- end
98
- end
99
-
100
- # @api private
101
- # TODO: Not actually supported out of the box. Is theoretically possible
102
- # via CLR integration, custom functions.
103
- def regexp_operator(operand)
104
- 'REGEXP'
105
- end
106
-
107
- end #module SQL
108
-
109
- include SQL
110
- end # class SqlserverAdapter
111
-
112
- const_added(:SqlserverAdapter)
113
- end # module Adapters
114
- end # module DataMapper
@@ -1,111 +0,0 @@
1
- require 'pathname'
2
- require 'yaml'
3
-
4
- module DataMapper
5
- module Adapters
6
- class YamlAdapter < AbstractAdapter
7
- # @api semipublic
8
- def create(resources)
9
- update_records(resources.first.model) do |records|
10
- resources.each do |resource|
11
- initialize_serial(resource, records.size.succ)
12
- records << resource.attributes(:field)
13
- end
14
- end
15
- end
16
-
17
- # @api semipublic
18
- def read(query)
19
- query.filter_records(records_for(query.model).dup)
20
- end
21
-
22
- # @api semipublic
23
- def update(attributes, collection)
24
- attributes = attributes_as_fields(attributes)
25
-
26
- update_records(collection.model) do |records|
27
- records_to_update = collection.query.filter_records(records.dup)
28
- records_to_update.each { |resource| resource.update(attributes) }.size
29
- end
30
- end
31
-
32
- # @api semipublic
33
- def delete(collection)
34
- update_records(collection.model) do |records|
35
- records_to_delete = collection.query.filter_records(records.dup)
36
- records.replace(records - records_to_delete)
37
- records_to_delete.size
38
- end
39
- end
40
-
41
- private
42
-
43
- # @api semipublic
44
- def initialize(name, options = {})
45
- super
46
- (@path = Pathname(@options[:path]).freeze).mkpath
47
- end
48
-
49
- # Retrieves all records for a model and yeilds them to a block.
50
- #
51
- # The block should make any changes to the records in-place. After
52
- # the block executes all the records are dumped back to the file.
53
- #
54
- # @param [Model, #to_s] model
55
- # Used to determine which file to read/write to
56
- #
57
- # @yieldparam [Hash]
58
- # A hash of record.key => record pairs retrieved from the file
59
- #
60
- # @api private
61
- def update_records(model)
62
- records = records_for(model)
63
- result = yield records
64
- write_records(model, records)
65
- result
66
- end
67
-
68
- # Read all records from a file for a model
69
- #
70
- # @param [#storage_name] model
71
- # The model/name to retieve records for
72
- #
73
- # @api private
74
- def records_for(model)
75
- file = yaml_file(model)
76
- file.readable? && YAML.load_file(file) || []
77
- end
78
-
79
- # Writes all records to a file
80
- #
81
- # @param [#storage_name] model
82
- # The model/name to write the records for
83
- #
84
- # @param [Hash] records
85
- # A hash of record.key => record pairs to be written
86
- #
87
- # @api private
88
- def write_records(model, records)
89
- yaml_file(model).open('w') do |fh|
90
- YAML.dump(records, fh)
91
- end
92
- end
93
-
94
- # Given a model, gives the filename to be used for record storage
95
- #
96
- # @example
97
- # yaml_file(Article) #=> "/path/to/files/articles.yml"
98
- #
99
- # @param [#storage_name] model
100
- # The model to be used to determine the file name.
101
- #
102
- # @api private
103
- def yaml_file(model)
104
- @path / "#{model.storage_name(name)}.yml"
105
- end
106
-
107
- end # class YamlAdapter
108
-
109
- const_added(:YamlAdapter)
110
- end # module Adapters
111
- end # module DataMapper