dbagile 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (305) hide show
  1. data/LICENCE.textile +12 -0
  2. data/README.textile +89 -0
  3. data/bin/dba +22 -0
  4. data/lib/dbagile/adapter/sequel/class_methods.rb +18 -0
  5. data/lib/dbagile/adapter/sequel/connection.rb +38 -0
  6. data/lib/dbagile/adapter/sequel/data/table_driven.rb +30 -0
  7. data/lib/dbagile/adapter/sequel/data/transaction_driven.rb +43 -0
  8. data/lib/dbagile/adapter/sequel/schema/concrete_script.rb +135 -0
  9. data/lib/dbagile/adapter/sequel/schema/physical_dump.rb +106 -0
  10. data/lib/dbagile/adapter/sequel/schema/schema2sequel_args.rb +71 -0
  11. data/lib/dbagile/adapter/sequel/schema/table_driven.rb +52 -0
  12. data/lib/dbagile/adapter/sequel/schema/transaction_driven.rb +46 -0
  13. data/lib/dbagile/adapter/sequel/sequel_tracer.rb +144 -0
  14. data/lib/dbagile/adapter/sequel.rb +46 -0
  15. data/lib/dbagile/adapter.rb +15 -0
  16. data/lib/dbagile/command/api.rb +49 -0
  17. data/lib/dbagile/command/bulk/commons.rb +130 -0
  18. data/lib/dbagile/command/bulk/export.rb +99 -0
  19. data/lib/dbagile/command/bulk/import.rb +147 -0
  20. data/lib/dbagile/command/bulk.rb +3 -0
  21. data/lib/dbagile/command/class_methods.rb +103 -0
  22. data/lib/dbagile/command/db/add.rb +94 -0
  23. data/lib/dbagile/command/db/list.rb +40 -0
  24. data/lib/dbagile/command/db/ping.rb +49 -0
  25. data/lib/dbagile/command/db/rm.rb +52 -0
  26. data/lib/dbagile/command/db/stage.rb +81 -0
  27. data/lib/dbagile/command/db/use.rb +48 -0
  28. data/lib/dbagile/command/db.rb +6 -0
  29. data/lib/dbagile/command/dba.rb +121 -0
  30. data/lib/dbagile/command/help.rb +50 -0
  31. data/lib/dbagile/command/repo/create.rb +54 -0
  32. data/lib/dbagile/command/repo.rb +1 -0
  33. data/lib/dbagile/command/robust.rb +86 -0
  34. data/lib/dbagile/command/schema/check.rb +59 -0
  35. data/lib/dbagile/command/schema/commons.rb +118 -0
  36. data/lib/dbagile/command/schema/diff.rb +101 -0
  37. data/lib/dbagile/command/schema/dump.rb +48 -0
  38. data/lib/dbagile/command/schema/merge.rb +55 -0
  39. data/lib/dbagile/command/schema/sql_script.rb +124 -0
  40. data/lib/dbagile/command/schema.rb +6 -0
  41. data/lib/dbagile/command/sql/drop.rb +40 -0
  42. data/lib/dbagile/command/sql/heading.rb +34 -0
  43. data/lib/dbagile/command/sql/send.rb +67 -0
  44. data/lib/dbagile/command/sql/show.rb +42 -0
  45. data/lib/dbagile/command/sql.rb +4 -0
  46. data/lib/dbagile/command/web/tools.rb +23 -0
  47. data/lib/dbagile/command/web.rb +1 -0
  48. data/lib/dbagile/command.rb +158 -0
  49. data/lib/dbagile/contract/connection.rb +66 -0
  50. data/lib/dbagile/contract/data/dataset.rb +69 -0
  51. data/lib/dbagile/contract/data/table_driven.rb +49 -0
  52. data/lib/dbagile/contract/data/transaction_driven.rb +74 -0
  53. data/lib/dbagile/contract/data.rb +3 -0
  54. data/lib/dbagile/contract/robust/helpers.rb +19 -0
  55. data/lib/dbagile/contract/robust/optimistic/data/table_driven.rb +31 -0
  56. data/lib/dbagile/contract/robust/optimistic/data/transaction_driven.rb +45 -0
  57. data/lib/dbagile/contract/robust/optimistic/schema/table_driven.rb +53 -0
  58. data/lib/dbagile/contract/robust/optimistic/schema/transaction_driven.rb +45 -0
  59. data/lib/dbagile/contract/robust/optimistic.rb +18 -0
  60. data/lib/dbagile/contract/robust.rb +20 -0
  61. data/lib/dbagile/contract/schema/table_driven.rb +89 -0
  62. data/lib/dbagile/contract/schema/transaction_driven.rb +68 -0
  63. data/lib/dbagile/contract/schema.rb +2 -0
  64. data/lib/dbagile/contract/utils/delegate.rb +17 -0
  65. data/lib/dbagile/contract/utils/full.rb +13 -0
  66. data/lib/dbagile/contract/utils.rb +2 -0
  67. data/lib/dbagile/contract.rb +5 -0
  68. data/lib/dbagile/core/chain.rb +92 -0
  69. data/lib/dbagile/core/connection.rb +51 -0
  70. data/lib/dbagile/core/database.rb +221 -0
  71. data/lib/dbagile/core/io/dsl.rb +95 -0
  72. data/lib/dbagile/core/io/robustness.rb +82 -0
  73. data/lib/dbagile/core/io.rb +2 -0
  74. data/lib/dbagile/core/repository/builder.rb +67 -0
  75. data/lib/dbagile/core/repository/yaml_methods.rb +82 -0
  76. data/lib/dbagile/core/repository.rb +211 -0
  77. data/lib/dbagile/core/schema/builder/coercion.rb +210 -0
  78. data/lib/dbagile/core/schema/builder/concept_factory.rb +61 -0
  79. data/lib/dbagile/core/schema/builder.rb +187 -0
  80. data/lib/dbagile/core/schema/composite.rb +239 -0
  81. data/lib/dbagile/core/schema/computations/filter.rb +40 -0
  82. data/lib/dbagile/core/schema/computations/merge.rb +55 -0
  83. data/lib/dbagile/core/schema/computations/minus.rb +44 -0
  84. data/lib/dbagile/core/schema/computations/split.rb +37 -0
  85. data/lib/dbagile/core/schema/computations.rb +4 -0
  86. data/lib/dbagile/core/schema/database_schema.rb +129 -0
  87. data/lib/dbagile/core/schema/errors.rb +173 -0
  88. data/lib/dbagile/core/schema/logical/attribute.rb +68 -0
  89. data/lib/dbagile/core/schema/logical/constraint/candidate_key.rb +70 -0
  90. data/lib/dbagile/core/schema/logical/constraint/foreign_key.rb +121 -0
  91. data/lib/dbagile/core/schema/logical/constraint.rb +58 -0
  92. data/lib/dbagile/core/schema/logical/constraints.rb +28 -0
  93. data/lib/dbagile/core/schema/logical/heading.rb +47 -0
  94. data/lib/dbagile/core/schema/logical/relvar.rb +81 -0
  95. data/lib/dbagile/core/schema/logical.rb +24 -0
  96. data/lib/dbagile/core/schema/migrate/abstract_script.rb +35 -0
  97. data/lib/dbagile/core/schema/migrate/collapse_table.rb +15 -0
  98. data/lib/dbagile/core/schema/migrate/create_table.rb +15 -0
  99. data/lib/dbagile/core/schema/migrate/drop_table.rb +15 -0
  100. data/lib/dbagile/core/schema/migrate/expand_table.rb +15 -0
  101. data/lib/dbagile/core/schema/migrate/operation.rb +141 -0
  102. data/lib/dbagile/core/schema/migrate/stager.rb +282 -0
  103. data/lib/dbagile/core/schema/migrate.rb +2 -0
  104. data/lib/dbagile/core/schema/part.rb +114 -0
  105. data/lib/dbagile/core/schema/physical/index.rb +64 -0
  106. data/lib/dbagile/core/schema/physical/indexes.rb +12 -0
  107. data/lib/dbagile/core/schema/physical.rb +26 -0
  108. data/lib/dbagile/core/schema/robustness.rb +39 -0
  109. data/lib/dbagile/core/schema/schema_object.rb +94 -0
  110. data/lib/dbagile/core/schema.rb +254 -0
  111. data/lib/dbagile/core/transaction.rb +74 -0
  112. data/lib/dbagile/core.rb +7 -0
  113. data/lib/dbagile/environment/buffering.rb +45 -0
  114. data/lib/dbagile/environment/delegator.rb +59 -0
  115. data/lib/dbagile/environment/interactions.rb +208 -0
  116. data/lib/dbagile/environment/on_error.rb +47 -0
  117. data/lib/dbagile/environment/repository.rb +161 -0
  118. data/lib/dbagile/environment/robustness.rb +7 -0
  119. data/lib/dbagile/environment/testing.rb +23 -0
  120. data/lib/dbagile/environment.rb +122 -0
  121. data/lib/dbagile/errors.rb +30 -0
  122. data/lib/dbagile/io/csv.rb +99 -0
  123. data/lib/dbagile/io/json.rb +41 -0
  124. data/lib/dbagile/io/pretty_table.rb +128 -0
  125. data/lib/dbagile/io/ruby.rb +62 -0
  126. data/lib/dbagile/io/text.rb +18 -0
  127. data/lib/dbagile/io/type_safe.rb +65 -0
  128. data/lib/dbagile/io/xml.rb +35 -0
  129. data/lib/dbagile/io/yaml.rb +30 -0
  130. data/lib/dbagile/io.rb +94 -0
  131. data/lib/dbagile/loader.rb +16 -0
  132. data/lib/dbagile/plugin.rb +29 -0
  133. data/lib/dbagile/restful/client/delete.rb +22 -0
  134. data/lib/dbagile/restful/client/get.rb +24 -0
  135. data/lib/dbagile/restful/client/post.rb +22 -0
  136. data/lib/dbagile/restful/client/utils.rb +16 -0
  137. data/lib/dbagile/restful/client.rb +41 -0
  138. data/lib/dbagile/restful/middleware/delete.rb +22 -0
  139. data/lib/dbagile/restful/middleware/get.rb +27 -0
  140. data/lib/dbagile/restful/middleware/one_database.rb +82 -0
  141. data/lib/dbagile/restful/middleware/post.rb +23 -0
  142. data/lib/dbagile/restful/middleware/utils.rb +65 -0
  143. data/lib/dbagile/restful/middleware.rb +54 -0
  144. data/lib/dbagile/restful/server.rb +65 -0
  145. data/lib/dbagile/restful.rb +9 -0
  146. data/lib/dbagile/robustness/dependencies.rb +36 -0
  147. data/lib/dbagile/robustness/file_system.rb +53 -0
  148. data/lib/dbagile/robustness.rb +14 -0
  149. data/lib/dbagile/tools/file_system.rb +24 -0
  150. data/lib/dbagile/tools/math.rb +11 -0
  151. data/lib/dbagile/tools/ordered_hash.rb +39 -0
  152. data/lib/dbagile/tools/ruby.rb +78 -0
  153. data/lib/dbagile/tools/string.rb +6 -0
  154. data/lib/dbagile/tools/tuple.rb +49 -0
  155. data/lib/dbagile/tools.rb +6 -0
  156. data/lib/dbagile.rb +66 -0
  157. data/test/assumptions/equality.spec +11 -0
  158. data/test/assumptions/fixtures.rb +39 -0
  159. data/test/assumptions/inheritance.spec +17 -0
  160. data/test/assumptions/sequel/autonumber.spec +19 -0
  161. data/test/assumptions/sequel/connect.spec +29 -0
  162. data/test/assumptions/sequel/test.db +0 -0
  163. data/test/assumptions/stdlib/pathname.spec +13 -0
  164. data/test/assumptions/yaml/fixtures.rb +25 -0
  165. data/test/assumptions/yaml/to_yaml.spec +10 -0
  166. data/test/assumptions.spec +2 -0
  167. data/test/commands/bulk/export.spec +100 -0
  168. data/test/commands/bulk/import.spec +49 -0
  169. data/test/commands/db/add.spec +31 -0
  170. data/test/commands/db/list.spec +29 -0
  171. data/test/commands/db/ping.spec +21 -0
  172. data/test/commands/db/rm.spec +18 -0
  173. data/test/commands/db/use.spec +18 -0
  174. data/test/commands/dba.spec +54 -0
  175. data/test/commands/repo/create.spec +30 -0
  176. data/test/commands/schema/check.spec +25 -0
  177. data/test/commands/schema/diff.spec +30 -0
  178. data/test/commands/schema/dump.spec +23 -0
  179. data/test/commands/schema/fixtures/add_constraint.yaml +30 -0
  180. data/test/commands/schema/fixtures/announced.yaml +28 -0
  181. data/test/commands/schema/fixtures/effective.yaml +20 -0
  182. data/test/commands/schema/fixtures/invalid.yaml +6 -0
  183. data/test/commands/schema/sql_script.spec +56 -0
  184. data/test/commands/sql/drop.spec +25 -0
  185. data/test/commands/sql/heading.spec +7 -0
  186. data/test/commands/sql/scripts/delete.sql +1 -0
  187. data/test/commands/sql/scripts/insert.sql +2 -0
  188. data/test/commands/sql/send.spec +29 -0
  189. data/test/commands/sql/show.spec +17 -0
  190. data/test/commands.spec +138 -0
  191. data/test/contract/connection/transaction.ex +11 -0
  192. data/test/contract/connection.spec +9 -0
  193. data/test/contract/data/dataset/columns.ex +5 -0
  194. data/test/contract/data/dataset/count.ex +5 -0
  195. data/test/contract/data/dataset.spec +9 -0
  196. data/test/contract/data/table_driven/dataset.ex +31 -0
  197. data/test/contract/data/table_driven/exists_q.ex +27 -0
  198. data/test/contract/data/table_driven.spec +9 -0
  199. data/test/contract/data/transaction_driven/delete.ex +29 -0
  200. data/test/contract/data/transaction_driven/direct_sql.ex +19 -0
  201. data/test/contract/data/transaction_driven/insert.ex +8 -0
  202. data/test/contract/data/transaction_driven/update.ex +19 -0
  203. data/test/contract/data/transaction_driven.spec +18 -0
  204. data/test/contract/robust/data/table_driven.spec +15 -0
  205. data/test/contract/robust/data/transaction_driven.spec +21 -0
  206. data/test/contract/robust/schema/table_driven.spec +21 -0
  207. data/test/contract/robust/schema/transaction_driven.spec +19 -0
  208. data/test/contract/schema/table_driven/column_names.ex +5 -0
  209. data/test/contract/schema/table_driven/has_column_q.ex +13 -0
  210. data/test/contract/schema/table_driven/has_table_q.ex +11 -0
  211. data/test/contract/schema/table_driven/heading.ex +5 -0
  212. data/test/contract/schema/table_driven.spec +9 -0
  213. data/test/contract/schema/transaction_driven/create_table.ex +10 -0
  214. data/test/contract/schema/transaction_driven/drop_table.ex +10 -0
  215. data/test/contract/schema/transaction_driven.spec +18 -0
  216. data/test/contract.spec +66 -0
  217. data/test/fixtures/basics/data/basic_values.rb +13 -0
  218. data/test/fixtures/basics/data/empty_table.rb +3 -0
  219. data/test/fixtures/basics/data/non_empty_table.rb +4 -0
  220. data/test/fixtures/basics/data/parts.rb +8 -0
  221. data/test/fixtures/basics/data/suppliers.rb +7 -0
  222. data/test/fixtures/basics/data/supplies.rb +14 -0
  223. data/test/fixtures/basics/dbagile.idx +20 -0
  224. data/test/fixtures/basics/fixtures.yaml +28 -0
  225. data/test/fixtures/basics/robust.db +0 -0
  226. data/test/fixtures/basics/suppliers.yaml +30 -0
  227. data/test/fixtures/basics/test.db +0 -0
  228. data/test/fixtures/empty/dbagile.idx +5 -0
  229. data/test/fixtures.rb +152 -0
  230. data/test/restful/delete/no_format.ex +32 -0
  231. data/test/restful/delete.spec +8 -0
  232. data/test/restful/get/csv_format.ex +12 -0
  233. data/test/restful/get/json_format.ex +19 -0
  234. data/test/restful/get/query_string.ex +11 -0
  235. data/test/restful/get/text_format.ex +12 -0
  236. data/test/restful/get/yaml_format.ex +14 -0
  237. data/test/restful/get.spec +5 -0
  238. data/test/restful/post/no_format.ex +22 -0
  239. data/test/restful/post.spec +8 -0
  240. data/test/restful.spec +32 -0
  241. data/test/run_all_suite.rb +51 -0
  242. data/test/spec_helper.rb +26 -0
  243. data/test/support/be_a_valid_json_string.rb +19 -0
  244. data/test/support/be_a_valid_yaml_string.rb +18 -0
  245. data/test/unit/adapter/factor.spec +13 -0
  246. data/test/unit/adapter/sequel/new.spec +19 -0
  247. data/test/unit/command/api.spec +12 -0
  248. data/test/unit/command/command_for.spec +36 -0
  249. data/test/unit/command/command_name_of.spec +21 -0
  250. data/test/unit/command/ruby_method_for.spec +21 -0
  251. data/test/unit/command/sanity.spec +34 -0
  252. data/test/unit/contract/utils/delegate/delegate.spec +23 -0
  253. data/test/unit/core/chain/chain.spec +57 -0
  254. data/test/unit/core/chain/connect.spec +22 -0
  255. data/test/unit/core/chain/delegate_chain.spec +16 -0
  256. data/test/unit/core/chain/initialize.spec +19 -0
  257. data/test/unit/core/chain/plug.spec +31 -0
  258. data/test/unit/core/io/dsl/scope.spec +9 -0
  259. data/test/unit/core/repository/create_bang.spec +31 -0
  260. data/test/unit/core/repository/current.spec +31 -0
  261. data/test/unit/core/repository/database.spec +47 -0
  262. data/test/unit/core/repository/fixtures/corrupted/dbagile.idx +1 -0
  263. data/test/unit/core/repository/fixtures/test_and_prod/dbagile.idx +21 -0
  264. data/test/unit/core/repository/fixtures.rb +25 -0
  265. data/test/unit/core/repository/has_database_q.spec +16 -0
  266. data/test/unit/core/repository/load.spec +51 -0
  267. data/test/unit/core/repository/to_yaml.spec +17 -0
  268. data/test/unit/core/schema/check.spec +32 -0
  269. data/test/unit/core/schema/empty_q.spec +18 -0
  270. data/test/unit/core/schema/filter.spec +42 -0
  271. data/test/unit/core/schema/fixtures/dbagile.yaml +7 -0
  272. data/test/unit/core/schema/fixtures/empty.yaml +11 -0
  273. data/test/unit/core/schema/fixtures/invalid.yaml +54 -0
  274. data/test/unit/core/schema/fixtures/left.yaml +46 -0
  275. data/test/unit/core/schema/fixtures/left_minus_right.yaml +31 -0
  276. data/test/unit/core/schema/fixtures/right.yaml +46 -0
  277. data/test/unit/core/schema/fixtures/right_minus_left.yaml +31 -0
  278. data/test/unit/core/schema/fixtures/suppliers_and_parts.yaml +30 -0
  279. data/test/unit/core/schema/fixtures.rb +32 -0
  280. data/test/unit/core/schema/merge.spec +72 -0
  281. data/test/unit/core/schema/minus.spec +26 -0
  282. data/test/unit/core/schema/sanity.spec +39 -0
  283. data/test/unit/core/schema/split.spec +58 -0
  284. data/test/unit/core/schema/stage_script.spec +26 -0
  285. data/test/unit/core/schema/to_yaml.spec +13 -0
  286. data/test/unit/core/schema/yaml_display.spec +14 -0
  287. data/test/unit/core/schema/yaml_load.spec +20 -0
  288. data/test/unit/core/transaction/transaction.spec +10 -0
  289. data/test/unit/fixtures.rb +67 -0
  290. data/test/unit/io/to_xxx.spec +52 -0
  291. data/test/unit/plugin/options.spec +21 -0
  292. data/test/unit/plugin/tuple_heading.spec +11 -0
  293. data/test/unit/plugin/with_options.spec +12 -0
  294. data/test/unit/tools/ruby/class_unqualified_name.spec +26 -0
  295. data/test/unit/tools/ruby/extract_file_rdoc.spec +10 -0
  296. data/test/unit/tools/ruby/fixtures/rdoc.txt +12 -0
  297. data/test/unit/tools/ruby/fixtures.rb +19 -0
  298. data/test/unit/tools/ruby/optional_args_block_call.spec +35 -0
  299. data/test/unit/tools/ruby/parent_module.spec +21 -0
  300. data/test/unit/tools/ruby/rdoc_file_paragraphs.spec +13 -0
  301. data/test/unit/tools/tuple/tuple_heading.spec +11 -0
  302. data/test/unit/tools/tuple/tuple_key.spec +27 -0
  303. data/test/unit/tools/tuple/tuple_project.spec +23 -0
  304. data/test/unit.spec +3 -0
  305. metadata +422 -0
@@ -0,0 +1,239 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ class Composite < SchemaObject
5
+
6
+ # Creates a composite instance with parts
7
+ def initialize(composite_parts = _default_parts)
8
+ unless composite_parts.kind_of?(Hash)
9
+ raise ArgumentError, "Composite parts must be a hash, got #{composite_parts.inspect}"
10
+ end
11
+ _install_parts(composite_parts)
12
+ @insert_order = nil
13
+ end
14
+
15
+ ############################################################################
16
+ ### Private interface
17
+ ############################################################################
18
+ attr_reader :composite_parts
19
+ protected :composite_parts
20
+ private
21
+
22
+ # Creates defaut parts hash
23
+ def _default_parts
24
+ {}
25
+ end
26
+
27
+ # Returns prefered keys order
28
+ def _prefered_order
29
+ @insert_order || []
30
+ end
31
+
32
+ def _prefered_order=(order)
33
+ @insert_order = order
34
+ end
35
+
36
+ # Make installation of parts
37
+ def _install_parts(parts)
38
+ meth = _install_eigenclass_methods?
39
+ parts.each_pair{|k, v|
40
+ v.send(:parent=, self)
41
+ if meth
42
+ eigenclazz = (class << self; self; end)
43
+ eigenclazz.send(:define_method, k){ @composite_parts[k] }
44
+ end
45
+ }
46
+ @composite_parts = parts
47
+ end
48
+
49
+ # Installs eigenclass methods on parts provided at construction
50
+ def _install_eigenclass_methods?
51
+ false
52
+ end
53
+
54
+ # Duplicates parts
55
+ def _dup_parts
56
+ dup_parts = {}
57
+ @composite_parts.each_pair{|name, part| dup_parts[name] = part.dup}
58
+ dup_parts
59
+ end
60
+
61
+ protected
62
+
63
+ # Removes empty objects from parts
64
+ def _strip!
65
+ to_remove = part_keys.select{|k|
66
+ self[k].composite? and self[k].empty?
67
+ }
68
+ @composite_parts.delete_if{|k, v|
69
+ to_remove.include?(k)
70
+ }
71
+ if @insert_order
72
+ @insert_order -= to_remove
73
+ end
74
+ _sanity_check(schema)
75
+ self
76
+ end
77
+
78
+ # Makes a sanity check on the composite
79
+ def _sanity_check(schema)
80
+ if @insert_order
81
+ too_much = @insert_order - @composite_parts.keys
82
+ missing = @composite_parts.keys - @insert_order
83
+ unless too_much.empty? and missing.empty?
84
+ raise SchemaInternalError, "Key divergence"
85
+ end
86
+ end
87
+ parts.each{|p|
88
+ raise SchemaInternalError, "Invalid parent on #{self}" unless p.parent == self
89
+ raise SchemaInternalError, "Invalid schema on on #{self}" unless p.schema == schema
90
+ p._sanity_check(schema)
91
+ }
92
+ rescue StandardError => ex
93
+ raise SchemaInternalError, "Something goes wrong on #{self}: #{ex.message}", ex.backtrace
94
+ end
95
+
96
+ # Checks this composite's semantics and collect errors
97
+ def _semantics_check(clazz, buffer)
98
+ parts.collect{|p| p._semantics_check(clazz, buffer)}
99
+ end
100
+
101
+ ############################################################################
102
+ ### Public interface
103
+ ############################################################################
104
+ public
105
+
106
+ # Returns an array with part dependencies
107
+ def dependencies(include_parent = false)
108
+ deps = parts.collect{|p| p.dependencies(include_parent)}.flatten.uniq
109
+ deps += [ parent ] if include_parent and not(parent.nil?)
110
+ deps
111
+ end
112
+
113
+ # Yields the block with each part in turn
114
+ def each_part(&block)
115
+ parts.each(&block)
116
+ end
117
+
118
+ # @see DbAgile::Core::Schema
119
+ def visit(&block)
120
+ block.call(self, parent)
121
+ parts.each{|p| p.visit(&block)}
122
+ end
123
+
124
+ ############################################################################
125
+ ### SchemaObject
126
+ ############################################################################
127
+
128
+ # @see DbAgile::Core::SchemaObject
129
+ def empty?(recurse = true)
130
+ if recurse
131
+ parts.all?{|p| p.composite? && p.empty?(recurse)}
132
+ else
133
+ @composite_parts.empty?
134
+ end
135
+ end
136
+
137
+ # Returns number of parts
138
+ def size
139
+ @composite_parts.size
140
+ end
141
+
142
+ # @see DbAgile::Core::SchemaObject
143
+ def part_keys(sort = false)
144
+ if sort
145
+ @composite_parts.keys.sort{|k1,k2| k1.to_s <=> k2.to_s}
146
+ else
147
+ (_prefered_order + @composite_parts.keys).uniq
148
+ end
149
+ end
150
+
151
+ # @see DbAgile::Core::SchemaObject
152
+ def parts
153
+ part_keys(false).collect{|k| self[k]}
154
+ end
155
+
156
+ # @see DbAgile::Core::SchemaObject
157
+ def [](name)
158
+ @composite_parts[name]
159
+ end
160
+
161
+ # @see DbAgile::Core::SchemaObject
162
+ def []=(name, part, status = nil)
163
+ if @composite_parts.key?(name)
164
+ raise SchemaConflictError.new(self[name], part, name)
165
+ end
166
+ @composite_parts[name] = part
167
+ (@insert_order ||= []) << name
168
+ part.send(:parent=, self)
169
+ unless status.nil?
170
+ part.visit{|p, parent| p.status = status}
171
+ end
172
+ part
173
+ end
174
+
175
+ ############################################################################
176
+ ### About IO
177
+ ############################################################################
178
+
179
+ # @see DbAgile::Core::SchemaObject
180
+ def to_yaml(opts = {})
181
+ YAML::quick_emit(self, opts){|out|
182
+ out.map("tag:yaml.org,2002:map") do |map|
183
+ part_keys.each{|k|
184
+ map.add(k.to_s, self[k])
185
+ }
186
+ end
187
+ }
188
+ end
189
+
190
+ # Returns a yaml string
191
+ def yaml_display(env,
192
+ options = {},
193
+ colors = DbAgile::Core::Schema::STATUS_TO_COLOR,
194
+ indent = 0)
195
+ part_keys.each{|k|
196
+ part = self[k]
197
+ status = part.status.to_s.ljust(25)
198
+ show_it = !(part.status == Schema::NO_CHANGE and options[:skip_unchanged])
199
+ if show_it
200
+ mine = " "*indent + k.to_s + ":"
201
+ if part.composite?
202
+ env.display(mine, colors[part.status])
203
+ part.yaml_display(env, options, colors, indent+1)
204
+ else
205
+ part_str = part.to_yaml
206
+ part_str =~ /---\s*(.*)$/
207
+ part_str = $1
208
+ env.display(mine + " " + part_str, colors[part.status])
209
+ end
210
+ end
211
+ }
212
+ end
213
+
214
+ ############################################################################
215
+ ### Equality and hash code
216
+ ############################################################################
217
+
218
+ # @see DbAgile::Core::SchemaObject
219
+ def look_same_as?(other)
220
+ return nil unless other.kind_of?(self.class)
221
+ my_parts = part_keys(true)
222
+ return false unless (my_parts == other.part_keys(true))
223
+ my_parts.all?{|k| self[k].look_same_as?(other[k])}
224
+ end
225
+
226
+ # @see DbAgile::Core::SchemaObject
227
+ def dup
228
+ self.class.new(_dup_parts)
229
+ end
230
+
231
+ # Returns a string representation
232
+ def to_s
233
+ "#{DbAgile::RubyTools::unqualified_class_name(self.class)}"
234
+ end
235
+
236
+ end # class Composite
237
+ end # module Schema
238
+ end # module Core
239
+ end # module DbAgile
@@ -0,0 +1,40 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ module Computations
5
+ module Filter
6
+
7
+ # Default filtering options
8
+ DEFAULT_OPTIONS = {}
9
+
10
+ # Computes set difference between schemas.
11
+ def filter(object, options, builder, &block)
12
+ unless object.composite?
13
+ raise ArgumentError, "Filter called on a part object!"
14
+ end
15
+
16
+ # Make a deep filtered copy
17
+ keys = object.part_keys
18
+ copied = builder.send(object.builder_handler, *object.builder_args){|builder_object|
19
+ keys.each{|key|
20
+ sub_object = object[key]
21
+ if sub_object.composite?
22
+ filter(sub_object, options, builder, &block)
23
+ else
24
+ if block.call(sub_object)
25
+ builder_object.[]=(key, sub_object.dup)
26
+ end
27
+ end
28
+ }
29
+ builder_object
30
+ }
31
+
32
+ copied
33
+ end
34
+
35
+ end # module Filter
36
+ extend(Filter)
37
+ end # module Computations
38
+ end # module Schema
39
+ end # module Core
40
+ end # module DbAgile
@@ -0,0 +1,55 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ module Computations
5
+ module Merge
6
+
7
+ # Computes set difference between schemas.
8
+ def merge(left, right, builder, &block)
9
+ unless left.class == right.class
10
+ raise ArgumentError, "#{left.class} != #{right.class}"
11
+ end
12
+ unless left.composite?
13
+ raise ArgumentError, "Diff called on a part object!"
14
+ end
15
+
16
+ # Computes key differences
17
+ left_keys, right_keys = left.part_keys(true), right.part_keys(true)
18
+ left_only = left_keys - right_keys
19
+ right_only = right_keys - left_keys
20
+ commons = left_keys & right_keys
21
+
22
+ mine = Schema::NO_CHANGE
23
+ builder.send(left.builder_handler, *left.builder_args){|builder_object|
24
+ left_only.each {|key|
25
+ mine = Schema::TO_ALTER
26
+ builder_object.[]=(key, left[key].dup, Schema::TO_DROP)
27
+ }
28
+ right_only.each{|key|
29
+ mine = Schema::TO_ALTER
30
+ builder_object.[]=(key, right[key].dup, Schema::TO_CREATE)
31
+ }
32
+ commons.each{|key|
33
+ on_left, on_right = left[key], right[key]
34
+ if on_left.composite?
35
+ recursed = merge(on_left, on_right, builder, &block).status
36
+ mine = Schema::TO_ALTER if recursed != Schema::NO_CHANGE
37
+ elsif on_left.look_same_as?(on_right)
38
+ builder_object.[]=(key, left[key].dup, Schema::NO_CHANGE)
39
+ elsif block
40
+ resolved = block.call(on_left, on_right)
41
+ if resolved
42
+ builder_object.[]=(key, resolved, Schema::TO_ALTER)
43
+ end
44
+ end
45
+ }
46
+ builder_object.status = mine
47
+ }
48
+ end
49
+
50
+ end # module Merge
51
+ extend(Merge)
52
+ end # module Computations
53
+ end # module Schema
54
+ end # module Core
55
+ end # module DbAgile
@@ -0,0 +1,44 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ module Computations
5
+ module Minus
6
+
7
+ # Computes set difference between schemas.
8
+ def minus(left, right, builder)
9
+ unless left.class == right.class
10
+ raise ArgumentError, "#{left.class} != #{right.class}"
11
+ end
12
+ unless left.composite?
13
+ raise ArgumentError, "Minus called on a part object!"
14
+ end
15
+
16
+ result = builder.send(left.builder_handler, *left.builder_args){|builder_object|
17
+ left.part_keys.each{|name|
18
+ left_sub, right_sub = left[name], right[name]
19
+ if right_sub.nil?
20
+ # missing in right
21
+ builder_object[name] = left_sub.dup
22
+ elsif left_sub.composite?
23
+ # present in right, possibly the same
24
+ minus(left_sub, right_sub, builder)
25
+ elsif not(left_sub.look_same_as?(right_sub))
26
+ # present in right, conflicting
27
+ builder_object[name] = left_sub.dup
28
+ else
29
+ # present in right, same
30
+ # (following line otherwise, not counted by rcov)
31
+ 1
32
+ end
33
+ }
34
+ }
35
+
36
+ result
37
+ end
38
+
39
+ end # module Minus
40
+ extend(Minus)
41
+ end # module Computations
42
+ end # module Schema
43
+ end # module Core
44
+ end # module DbAgile
@@ -0,0 +1,37 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ module Computations
5
+ module Split
6
+
7
+ # Default split options
8
+ DEFAULT_OPTIONS = {}
9
+
10
+ # Computes set difference between schemas.
11
+ def split(schema, options, &block)
12
+
13
+ # Build the split hash
14
+ split_hash = Hash.new{|h,k| h[k] = []}
15
+ schema.visit{|object, parent|
16
+ if object.part?
17
+ kind = block.call(object)
18
+ split_hash[kind] << object
19
+ end
20
+ }
21
+
22
+ # Rebuild schemas now
23
+ schemas = {}
24
+ split_hash.keys.each{|kind|
25
+ split_proc = lambda{|obj| split_hash[kind].include?(obj)}
26
+ split_options = {:identifier => kind}
27
+ schemas[kind] = Schema::filter(schema, split_options, &split_proc)
28
+ }
29
+ schemas
30
+ end
31
+
32
+ end # module Split
33
+ extend(Split)
34
+ end # module Computations
35
+ end # module Schema
36
+ end # module Core
37
+ end # module DbAgile
@@ -0,0 +1,4 @@
1
+ require 'dbagile/core/schema/computations/minus'
2
+ require 'dbagile/core/schema/computations/merge'
3
+ require 'dbagile/core/schema/computations/filter'
4
+ require 'dbagile/core/schema/computations/split'
@@ -0,0 +1,129 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ class DatabaseSchema < Schema::Composite
5
+ include Enumerable
6
+ alias :each :visit
7
+
8
+ # Identifier of this schema
9
+ attr_accessor :schema_identifier
10
+
11
+ # Creates a schema instance
12
+ def initialize(schema_identifier = nil, parts = _default_parts)
13
+ @schema_identifier = schema_identifier
14
+ super(parts)
15
+ @insert_order = [:logical, :physical]
16
+ end
17
+
18
+
19
+ ############################################################################
20
+ ### Private interface
21
+ ############################################################################
22
+
23
+ # @see DbAgile::Core::Schema::Composite#_install_eigenclass_methods?
24
+ def _install_eigenclass_methods?
25
+ true
26
+ end
27
+
28
+ # @see DbAgile::Core::Schema::Composite#_default_parts
29
+ def _default_parts
30
+ {:logical => Schema::Logical.new,
31
+ :physical => Schema::Physical.new}
32
+ end
33
+
34
+ # Strips this schema
35
+ def _strip!
36
+ logical._strip!
37
+ self
38
+ end
39
+
40
+ ############################################################################
41
+ ### SchemaObject
42
+ ############################################################################
43
+
44
+ # Returns an array with part dependencies
45
+ def dependencies(include_parent = false)
46
+ []
47
+ end
48
+
49
+ # Overrided to return self.
50
+ def schema
51
+ self
52
+ end
53
+
54
+ # @see DbAgile::Core::Schema::SchemaObject
55
+ def dup
56
+ DatabaseSchema.new(schema_identifier, _dup_parts)
57
+ end
58
+
59
+
60
+ ############################################################################
61
+ ### IO
62
+ ############################################################################
63
+
64
+ # Dumps the schema to YAML
65
+ def to_yaml(opts = {})
66
+ YAML::dump_stream({'logical' => logical}, {'physical' => physical})
67
+ end
68
+ alias :inspect :to_yaml
69
+
70
+
71
+ # Returns a yaml string
72
+ def yaml_display(env,
73
+ options = {},
74
+ colors = DbAgile::Core::Schema::STATUS_TO_COLOR,
75
+ indent = 0)
76
+ env.display("---\nlogical:")
77
+ logical.yaml_display(env, options, colors, indent + 1)
78
+ env.display("\n---\nphysical:")
79
+ physical.yaml_display(env, options, colors, indent + 1)
80
+ end
81
+
82
+ ############################################################################
83
+ ### Computations
84
+ ############################################################################
85
+
86
+ # Applies schema checking and raises a SchemaSemanticsError if something is wrong.
87
+ def check!(raise_on_error = true)
88
+ errors = SchemaSemanticsError.new(self)
89
+ _semantics_check(SchemaSemanticsError, errors)
90
+ if raise_on_error and not(errors.empty?)
91
+ raise errors
92
+ else
93
+ errors
94
+ end
95
+ end
96
+
97
+ # Convenient method for <code>schema.check!(false).empty?</code>
98
+ def looks_valid?
99
+ check!(false).empty?
100
+ end
101
+
102
+ # Applies schema minus
103
+ def minus(other)
104
+ Schema::minus(self, other)
105
+ end
106
+ alias :- :minus
107
+
108
+ # Applies schema merging
109
+ def merge(other)
110
+ Schema::merge(self, other)
111
+ end
112
+ alias :+ :merge
113
+
114
+ # Applies schema filtering
115
+ def filter(options = {}, &filter_block)
116
+ Schema::filter(self, options, &filter_block)
117
+ end
118
+
119
+ # Applies schema splitting
120
+ def split(options = {}, &split_block)
121
+ Schema::split(self, options, &split_block)
122
+ end
123
+
124
+ end # class DatabaseSchema
125
+ end # module Schema
126
+ end # module Core
127
+ end # module DbAgile
128
+ require 'dbagile/core/schema/logical'
129
+ require 'dbagile/core/schema/physical'