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,211 @@
1
+ require 'dbagile/core/repository/yaml_methods'
2
+ require 'dbagile/core/repository/builder'
3
+ module DbAgile
4
+ module Core
5
+ class Repository
6
+ include Enumerable
7
+ extend Repository::YAMLClassMethods
8
+ include Repository::YAMLInstanceMethods
9
+
10
+ # Index file name
11
+ REPOSITORY_INDEX_FILE_NAME = "dbagile.idx"
12
+
13
+ # Path to the root path of the repository
14
+ attr_reader :root_path
15
+
16
+ # Repository version
17
+ attr_accessor :version
18
+ private :version=
19
+
20
+ # Databases as an array of Database instances
21
+ attr_reader :databases
22
+
23
+ # Current database (its name, i.e. a Symbol)
24
+ attr_accessor :current_db_name
25
+
26
+ #############################################################################################
27
+ ### Initialization and parsing
28
+ #############################################################################################
29
+
30
+ # Creates a repository instance
31
+ def initialize(root_path, version = DbAgile::VERSION)
32
+ DbAgile::Robustness::valid_rw_directory!(root_path)
33
+ DbAgile::Robustness::valid_rw_file!(File.join(root_path, REPOSITORY_INDEX_FILE_NAME))
34
+ @root_path = root_path
35
+ @version = version
36
+ @databases = []
37
+ end
38
+
39
+ #############################################################################################
40
+ ### File management
41
+ #############################################################################################
42
+
43
+ # Returns a friendly path to be printed to user
44
+ def friendly_path
45
+ DbAgile::FileSystemTools::friendly_path!(root_path)
46
+ end
47
+
48
+ # Returns the path to the index file
49
+ def index_file
50
+ File.join(root_path, REPOSITORY_INDEX_FILE_NAME)
51
+ end
52
+
53
+ # Returns a file resolver Proc instance
54
+ def file_resolver
55
+ @file_resolver ||= lambda{|f|
56
+ if f[0, 1] == '/'
57
+ f
58
+ else
59
+ File.join(File.expand_path(self.root_path), f)
60
+ end
61
+ }
62
+ end
63
+
64
+ # Resolves a file which could be relative to repository root path
65
+ def resolve_file(f)
66
+ file_resolve.call(f)
67
+ end
68
+
69
+ #############################################################################################
70
+ ### About databases
71
+ #############################################################################################
72
+
73
+ # Checks if at least one database exists
74
+ def empty?
75
+ databases.empty?
76
+ end
77
+
78
+ # Checks if a database exists
79
+ def has_database?(name)
80
+ !database(name).nil?
81
+ end
82
+
83
+ # Checks if a name/database is the current one
84
+ def current?(name_or_db)
85
+ case name_or_db
86
+ when Symbol
87
+ return nil unless has_database?(name_or_db)
88
+ self.current_db_name == name_or_db
89
+ when Core::Database
90
+ self.current_db_name == name_or_db.name
91
+ else
92
+ raise ArgumentError, "Symbol or Database expected, #{name_or_db.inspect} found."
93
+ end
94
+ end
95
+
96
+ # Yields the block with each database in turn
97
+ def each(*args, &block)
98
+ databases.each(*args, &block)
99
+ end
100
+
101
+ # Returns a database by match. Returns nil if no such database
102
+ def database(match)
103
+ return match if match.kind_of?(::DbAgile::Core::Database)
104
+ databases.find{|c|
105
+ case match
106
+ when Symbol
107
+ c.name == match
108
+ when String
109
+ c.uri == match
110
+ when Regexp
111
+ match =~ c.uri
112
+ end
113
+ }
114
+ end
115
+
116
+ # Returns the current database
117
+ def current_database
118
+ database(current_db_name)
119
+ end
120
+
121
+ #############################################################################################
122
+ ### Updates
123
+ #############################################################################################
124
+
125
+ # Adds a database instance
126
+ def <<(db)
127
+ db.file_resolver = file_resolver
128
+ self.databases << db
129
+ end
130
+
131
+ # Removes a database from this repository
132
+ def remove(db)
133
+ db = self.database(db)
134
+ db.nil? ? nil : databases.delete(db)
135
+ end
136
+
137
+ #############################################################################################
138
+ ### Input/output
139
+ #############################################################################################
140
+
141
+ #
142
+ # Loads a repository from a root path
143
+ #
144
+ # @param [String] root_path path to a repository folder
145
+ # @raise IOError if required repository files do not exists or access is denied
146
+ # @raise DbAgile::CorruptedRepositoryError if anything goes wrong
147
+ #
148
+ def self.load(root_path)
149
+ # some checks first
150
+ DbAgile::Robustness::valid_rw_directory!(root_path)
151
+ index_file = File.join(root_path, REPOSITORY_INDEX_FILE_NAME)
152
+ msg = "Not a dbagile repository, missing or access denied on #{index_file}"
153
+ DbAgile::Robustness::valid_rw_file!(index_file, msg)
154
+
155
+ # loading
156
+ begin
157
+ from_yaml_file(index_file, root_path)
158
+ rescue StandardError => ex
159
+ msg = "Repository corruped: #{ex.message}"
160
+ raise DbAgile::CorruptedRepositoryError, msg, ex.backtrace
161
+ end
162
+ end
163
+
164
+ #
165
+ # Creates a fresh new repository somewhere
166
+ #
167
+ # @param [String] root_path path to an unexisting repository folder
168
+ # @return [Repository] the created repository instance
169
+ # @raise IOError is the repository already exists or cannot be created.
170
+ #
171
+ def self.create!(root_path)
172
+ DbAgile::Robustness::unexisting_directory!(root_path)
173
+ index_file = File.join(root_path, REPOSITORY_INDEX_FILE_NAME)
174
+ FileUtils.mkdir_p(root_path)
175
+ FileUtils.touch(index_file)
176
+ repo = Repository.new(root_path, DbAgile::VERSION)
177
+ repo.save!
178
+ end
179
+
180
+ #
181
+ # Saves the repository
182
+ #
183
+ # @return [Repository] the repository itself
184
+ # @raise IOError if something goes wrong when saving the repository
185
+ #
186
+ def save!
187
+ DbAgile::Robustness::valid_rw_file!(self.index_file)
188
+ flush(self.index_file)
189
+ self
190
+ end
191
+
192
+ #
193
+ # Flushes the repository into a given file or IO
194
+ #
195
+ # This method is not robust to dir/file errors and should be protected
196
+ # upstream.
197
+ #
198
+ def flush(output_file)
199
+ if output_file.kind_of?(::IO)
200
+ output_file << to_yaml
201
+ else
202
+ ::File.open(output_file, 'w'){|io| flush(io)}
203
+ end
204
+ self
205
+ end
206
+
207
+ private :flush
208
+ private_class_method :from_yaml_file, :from_yaml
209
+ end # class Repository
210
+ end # module Core
211
+ end # module DbAgile
@@ -0,0 +1,210 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ class Builder
5
+ module Coercion
6
+
7
+ ###############################################################################
8
+ ### Tools
9
+ ###############################################################################
10
+ def unsymbolize_hash(h)
11
+ unsymbolized = {}
12
+ h.each_pair{|k,v| unsymbolized[k.to_s] = v}
13
+ unsymbolized
14
+ end
15
+ module_function :unsymbolize_hash
16
+
17
+ # Unsymbolizes an array of names
18
+ def unsymbolize_array(array)
19
+ array.collect{|c| c.to_s}
20
+ end
21
+ module_function :unsymbolize_array
22
+
23
+ ###############################################################################
24
+ ### Validity
25
+ ###############################################################################
26
+
27
+ # Raises a DbAgile::SchemaSyntaxError
28
+ def invalid!(msg)
29
+ raise DbAgile::SchemaSyntaxError, msg, caller
30
+ end
31
+
32
+ # Raises a coercion error
33
+ def coercion_error!(msg = "")
34
+ raise ::SByC::TypeSystem::CoercionError, msg, caller
35
+ end
36
+
37
+ # Asserts that all args are not nil
38
+ def not_nil!(*args)
39
+ if args.any?{|arg| arg.nil?}
40
+ coercion_error!
41
+ end
42
+ args
43
+ end
44
+
45
+ # Asserts that all args are not empty
46
+ def not_empty!(*args)
47
+ if args.any?{|arg| arg.nil? or arg.to_s.empty?}
48
+ coercion_error!
49
+ end
50
+ args
51
+ end
52
+
53
+ # Asserts that a hash is not nil and a hash
54
+ def not_nil_hash!(hash)
55
+ not_nil!(hash)
56
+ unless hash.kind_of?(Hash)
57
+ coercion_error!("Hash expected, #{hash.class} received")
58
+ end
59
+ hash
60
+ end
61
+
62
+ def has_exactly_hash_keys!(hash, *keys)
63
+ not_nil!(hash)
64
+ unless keys.all?{|k| hash.key?(k)}
65
+ coercion_error!("Expected #{keys.inspect}, found #{hash.keys.inspect}")
66
+ end
67
+ hash
68
+ end
69
+
70
+ # Asserts that a name is valid
71
+ def valid_name!(name)
72
+ not_empty!(name)
73
+ unless [String, Symbol].include?(name.class)
74
+ coercion_error!
75
+ end
76
+ name
77
+ end
78
+
79
+ ###############################################################################
80
+ ### Data types (coercion error)
81
+ ###############################################################################
82
+
83
+ # Coerces an array
84
+ def coerce_array(array, non_empty)
85
+ unless array.kind_of?(Array)
86
+ coercion_error!
87
+ end
88
+ if non_empty and array.empty?
89
+ coercion_error!
90
+ end
91
+ array
92
+ end
93
+
94
+ # Coerces a symbolized hash
95
+ def coerce_symbolized_hash(hash, recursive = false)
96
+ hash = not_nil_hash!(hash)
97
+ symbolized = {}
98
+ hash.each_pair{|k,v|
99
+ if recursive and v.kind_of?(Hash)
100
+ v = coerce_symbolized_hash(v)
101
+ end
102
+ symbolized[coerce_name(k)] = v
103
+ }
104
+ symbolized
105
+ end
106
+
107
+ ###############################################################################
108
+ ### Sub-concepts (coercion error)
109
+ ###############################################################################
110
+
111
+ # Coerces a name
112
+ def coerce_name(name)
113
+ valid_name!(name)
114
+ name.to_s.to_sym
115
+ end
116
+ alias :coerce_relvar_name :coerce_name
117
+ alias :coerce_attribute_name :coerce_name
118
+ alias :coerce_constraint_name :coerce_name
119
+ alias :coerce_index_name :coerce_name
120
+
121
+ # Coerces a default value
122
+ def coerce_default_value(value, domain)
123
+ not_nil!(value, domain)
124
+ if value.kind_of?(Symbol)
125
+ case value
126
+ when :autonumber
127
+ :autonumber
128
+ else
129
+ invalid!("unknown default value handler #{value}")
130
+ end
131
+ else
132
+ SByC::TypeSystem::Ruby::coerce(value, domain)
133
+ end
134
+ end
135
+
136
+ # Coerces a domain
137
+ def coerce_domain(domain)
138
+ not_nil!(domain)
139
+ domain = SByC::TypeSystem::Ruby::coerce(domain, Module)
140
+ unless DbAgile::RECOGNIZED_DOMAINS.include?(domain)
141
+ invalid!("unable to use #{domain} for attribute domain")
142
+ end
143
+ domain
144
+ end
145
+
146
+ # Coerces a mandatory value
147
+ def coerce_mandatory(mandatory)
148
+ mandatory.nil? ? true : SByC::TypeSystem::Ruby::coerce(mandatory, SByC::TypeSystem::Ruby::Boolean)
149
+ end
150
+
151
+ # Coerces attributes names
152
+ def coerce_attribute_names(defn, non_empty = true)
153
+ defn = coerce_array(defn, non_empty)
154
+ defn.collect{|c| coerce_name(c)}
155
+ end
156
+
157
+ ###############################################################################
158
+ ### Concepts (coercion error)
159
+ ###############################################################################
160
+
161
+ # Coerces an attribute definition
162
+ def coerce_attribute_definition(defn)
163
+ defn = coerce_symbolized_hash(defn)
164
+ defn[:domain] = coerce_domain(defn[:domain])
165
+ if defn.key?(:default)
166
+ defn[:default] = coerce_default_value(defn[:default], defn[:domain])
167
+ end
168
+ defn[:mandatory] = coerce_mandatory(defn[:mandatory])
169
+ defn
170
+ end
171
+
172
+ # Coerces a constraint definition
173
+ def coerce_constraint_definition(defn)
174
+ defn = coerce_symbolized_hash(defn)
175
+ defn[:type] = coerce_name(defn[:type])
176
+
177
+ case type = defn[:type]
178
+ when :primary_key, :candidate_key
179
+ has_exactly_hash_keys!(defn, :type, :attributes)
180
+ defn[:attributes] = coerce_attribute_names(defn[:attributes], true)
181
+ when :foreign_key
182
+ if defn.key?(:key)
183
+ has_exactly_hash_keys!(defn, :type, :attributes, :references, :key)
184
+ defn[:attributes] = coerce_attribute_names(defn[:attributes], true)
185
+ defn[:references] = coerce_name(defn[:references])
186
+ defn[:key] = coerce_name(defn[:key])
187
+ else
188
+ has_exactly_hash_keys!(defn, :type, :attributes, :references)
189
+ defn[:attributes] = coerce_attribute_names(defn[:attributes], true)
190
+ defn[:references] = coerce_name(defn[:references])
191
+ end
192
+ else
193
+ invalid!("unknown constraint type #{type}")
194
+ end
195
+ defn
196
+ end
197
+
198
+ # Coerces an index definition
199
+ def coerce_index_definition(defn)
200
+ defn = coerce_symbolized_hash(defn)
201
+ defn[:relvar] = coerce_name(defn[:relvar])
202
+ defn[:attributes] = coerce_attribute_names(defn[:attributes], true)
203
+ defn
204
+ end
205
+
206
+ end # module Coercion
207
+ end # class Builder
208
+ end # module Schema
209
+ end # module Core
210
+ end # module DbAgile
@@ -0,0 +1,61 @@
1
+ module DbAgile
2
+ module Core
3
+ module Schema
4
+ class Builder
5
+ module ConceptFactory
6
+
7
+ # Builds a logical schema
8
+ def build_schema(identifier)
9
+ Schema.new(identifier)
10
+ end
11
+
12
+ # Builds a logical schema
13
+ def build_logical
14
+ Schema::Logical.new
15
+ end
16
+
17
+ # Builds a relvar
18
+ def build_relvar(name)
19
+ Schema::Logical::Relvar.new(name)
20
+ end
21
+
22
+ # Builds a heading
23
+ def build_heading
24
+ Schema::Logical::Heading.new
25
+ end
26
+
27
+ # Builds an attribute
28
+ def build_attribute(name, definition)
29
+ Schema::Logical::Attribute.new(name, definition)
30
+ end
31
+
32
+ # Builds a constraint collection
33
+ def build_constraints
34
+ Schema::Logical::Constraint.new
35
+ end
36
+
37
+ # Builds a constraint
38
+ def build_constraint(name, definition)
39
+ Schema::Logical::Constraint::factor(name, definition)
40
+ end
41
+
42
+ # Builds a physical schema
43
+ def build_physical
44
+ Schema::Physical.new
45
+ end
46
+
47
+ # Builds an index collection
48
+ def build_indexes
49
+ Schema::Physical::Indexes.new
50
+ end
51
+
52
+ # Builds an index
53
+ def build_index(name, definition)
54
+ Schema::Physical::Index.new(name, definition)
55
+ end
56
+
57
+ end # module ConceptFactory
58
+ end # class Builder
59
+ end # module Schema
60
+ end # module Core
61
+ end # module DbAgile
@@ -0,0 +1,187 @@
1
+ require 'dbagile/core/schema/builder/coercion'
2
+ require 'dbagile/core/schema/builder/concept_factory'
3
+ module DbAgile
4
+ module Core
5
+ module Schema
6
+ class Builder
7
+ include Builder::Coercion
8
+ include Builder::ConceptFactory
9
+
10
+ # Call stack
11
+ attr_accessor :stack
12
+
13
+ # Creates a builder instance
14
+ def initialize(schema = Schema.new)
15
+ if schema
16
+ @stack = [ [:schema, schema ] ]
17
+ else
18
+ @stack = [ [:root, {}] ]
19
+ end
20
+ end
21
+
22
+ ############################################################################
23
+ ### About result
24
+ ############################################################################
25
+
26
+ # Dumps as a Schema instance
27
+ def _dump
28
+ if stack.last[0] == :schema
29
+ _peek(:schema)
30
+ else
31
+ _peek(:root)[:schema]
32
+ end
33
+ end
34
+
35
+ ############################################################################
36
+ ### About the call stack
37
+ ############################################################################
38
+
39
+ # Push a hash on the stack
40
+ def _push(section, object, &block)
41
+ stack.push([section, object])
42
+ if block
43
+ DbAgile::RubyTools::optional_args_block_call(block, [ object ])
44
+ _pop
45
+ end
46
+ object
47
+ end
48
+
49
+ # Pops a hash from the stack
50
+ def _pop
51
+ stack.pop
52
+ end
53
+
54
+ # Returns top value of the stack
55
+ def _peek(section = nil)
56
+ unless section.nil?
57
+ if stack.empty? or not(stack.last[0] == section)
58
+ invalid!("expected to be in #{section}, but was #{stack.last[0]}")
59
+ end
60
+ end
61
+ stack.last[1]
62
+ end
63
+
64
+ # Applies natural rules according to current section
65
+ def _natural(hash)
66
+ case section = stack.last[0]
67
+ when :schema
68
+ coerce_symbolized_hash(hash).each_pair{|name, defn|
69
+ self.send(name, defn)
70
+ }
71
+ when :logical
72
+ coerce_symbolized_hash(hash).each_pair{|relvar_name, relvar_def|
73
+ relvar(relvar_name, relvar_def)
74
+ }
75
+ when :heading
76
+ coerce_symbolized_hash(hash).each_pair{|attr_name, attr_def|
77
+ attribute(attr_name, attr_def)
78
+ }
79
+ when :constraints
80
+ coerce_symbolized_hash(hash).each_pair{|c_name, c_def|
81
+ constraint(c_name, c_def)
82
+ }
83
+ when :physical
84
+ coerce_symbolized_hash(hash).each_pair{|name, defn|
85
+ self.send(name, defn)
86
+ }
87
+ when :indexes
88
+ coerce_symbolized_hash(hash).each_pair{|index_name, index_def|
89
+ index(index_name, index_def)
90
+ }
91
+ else
92
+ coerce_symbolized_hash(hash).each_pair{|k, v|
93
+ invalid!("No such section #{k}") unless self.respond_to?(k)
94
+ self.send(k, v)
95
+ }
96
+ end
97
+ end
98
+
99
+ ############################################################################
100
+ ### Logical sections
101
+ ############################################################################
102
+
103
+ # Starts the logical section and yields
104
+ def database_schema(identifier = nil, hash = nil, &block)
105
+ block = lambda{ _natural(hash) } unless block
106
+ schema = (_peek(:root)[:schema] ||= build_schema(identifier))
107
+ _push(:schema, schema, &block)
108
+ end
109
+ alias :schema :database_schema
110
+
111
+ # Starts the logical section and yields
112
+ def logical(hash = nil, &block)
113
+ block = lambda{ _natural(hash) } unless block
114
+ logical = (_peek(:schema)[:logical] ||= build_logical)
115
+ _push(:logical, logical, &block)
116
+ end
117
+
118
+ # Starts a relvar section
119
+ def relvar(name, hash = nil, &block)
120
+ block = lambda{ _natural(hash) } unless block
121
+ name = coerce_relvar_name(name)
122
+ relvar = (_peek(:logical)[name] ||= build_relvar(name))
123
+ _push(:relvar, relvar, &block)
124
+ rescue SByC::TypeSystem::CoercionError => ex
125
+ invalid!("Invalid relvar definition (#{name}): #{ex.message}")
126
+ end
127
+
128
+ # Starts a heading section
129
+ def heading(hash = nil, &block)
130
+ block = lambda{ _natural(hash) } unless block
131
+ heading = (_peek(:relvar)[:heading] ||= build_heading)
132
+ _push(:heading, heading, &block)
133
+ end
134
+
135
+ # Starts a constraints section
136
+ def constraints(hash = nil, &block)
137
+ block = lambda{ _natural(hash) } unless block
138
+ cs = (_peek(:relvar)[:constraints] ||= build_constraints)
139
+ _push(:constraints, cs, &block)
140
+ end
141
+
142
+ ###
143
+
144
+ # Adds an attribute to current heading
145
+ def attribute(name, definition)
146
+ name, defn = coerce_attribute_name(name), coerce_attribute_definition(definition)
147
+ _peek(:heading)[name] = build_attribute(name, defn)
148
+ end
149
+
150
+ # Adds a constraint to current relvar
151
+ def constraint(name, definition)
152
+ name, defn = coerce_constraint_name(name), coerce_constraint_definition(definition)
153
+ _peek(:constraints)[name] = build_constraint(name, defn)
154
+ end
155
+
156
+ ############################################################################
157
+ ### Physical sections
158
+ ############################################################################
159
+
160
+ # Starts the physical section and yields
161
+ def physical(hash = nil, &block)
162
+ block = lambda{ _natural(hash) } unless block
163
+ physical = (_peek(:schema)[:physical] ||= build_physical)
164
+ _push(:physical, physical, &block)
165
+ end
166
+
167
+ # Starts the indexes section and yields
168
+ def indexes(hash = nil, &block)
169
+ block = lambda{ _natural(hash) } unless block
170
+ indexes = (_peek(:physical)[:indexes] ||= build_indexes)
171
+ _push(:indexes, indexes, &block)
172
+ end
173
+
174
+ ###
175
+
176
+ # Adds an index to indexes
177
+ def index(name, definition)
178
+ name, defn = coerce_index_name(name), coerce_index_definition(definition)
179
+ _peek(:indexes)[name] = build_index(name, defn)
180
+ rescue SByC::TypeSystem::CoercionError => ex
181
+ invalid!("Invalid index definition (#{name}, #{definition.inspect}): #{ex.message}")
182
+ end
183
+
184
+ end # class Builder
185
+ end # module Schema
186
+ end # module Core
187
+ end # module DbAgile