dbagile 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,208 @@
1
+ module DbAgile
2
+ class Environment
3
+ #
4
+ # Environment's interactions contract.
5
+ #
6
+ module Interactions
7
+
8
+ # The buffer to use for displaying messages
9
+ attr_reader :message_buffer
10
+
11
+ # The buffer to use for asking questions
12
+ attr_reader :asking_buffer
13
+
14
+ ##############################################################################
15
+ ### Robustness
16
+ ##############################################################################
17
+
18
+ #
19
+ # Asserts that the interactive mode is enabled. Raises a
20
+ # InteractiveModeRequiredError otherwise.
21
+ #
22
+ def interactive!(msg = nil)
23
+ unless interactive?
24
+ msg = "The interactive mode is required, but disabled."
25
+ raise InteractiveModeRequiredError, msg
26
+ end
27
+ end
28
+
29
+ ##############################################################################
30
+ ### Configuration
31
+ ##############################################################################
32
+
33
+ #
34
+ # Returns true if the environment is interactive, false
35
+ # otherwise
36
+ #
37
+ def interactive?
38
+ @interactive
39
+ end
40
+
41
+ #
42
+ # Sets the interactive mode.
43
+ #
44
+ # In all cases, the highline instance for interaction is reset
45
+ # to nil.
46
+ #
47
+ def interactive=(value)
48
+ @interactive = value
49
+ @interaction_highline = nil
50
+ end
51
+
52
+ #
53
+ # Sets the message/output buffer to use for interactions.
54
+ #
55
+ # In all cases, the highline instance for interaction is reset
56
+ # to nil.
57
+ #
58
+ def message_buffer=(buffer)
59
+ @message_buffer = buffer
60
+ @interaction_highline = nil
61
+ end
62
+
63
+ #
64
+ # Sets the asking/input buffer to use for interactions.
65
+ #
66
+ # In all cases, the highline instance for interaction is reset
67
+ # to nil.
68
+ #
69
+ def asking_buffer=(buffer)
70
+ @asking_buffer = buffer
71
+ @interaction_highline = nil
72
+ end
73
+
74
+ ##############################################################################
75
+ ### HighLine delegation
76
+ ##############################################################################
77
+
78
+ #
79
+ # Returns an instance of HighLine for interactions, creating it
80
+ # if required.
81
+ #
82
+ # This method factors an HighLine instance even if interactive
83
+ # is set to false. Checking the interactive? flag should be made
84
+ # upstream.
85
+ #
86
+ def interaction_highline
87
+ if @interaction_highline.nil?
88
+ in_buffer = asking_buffer || input_buffer
89
+ out_buffer = message_buffer || output_buffer
90
+ @interaction_highline = HighLine.new(in_buffer, out_buffer)
91
+ end
92
+ @interaction_highline
93
+ end
94
+
95
+ #
96
+ # Asks something to the user/oracle and returns the result.
97
+ #
98
+ # This method is provided when something needs to be asked to the user.
99
+ # It simply returns non_interactive_value if the interaction flag is set
100
+ # to false. Otherwise, it delegates the call to the interaction highline
101
+ # instance.
102
+ #
103
+ # @param [String] question a prompt for the question
104
+ # @param [Proc] continuation a optional continuation procedure
105
+ # @param [...] non_interactive_value value to return in non-interactive
106
+ # mode
107
+ # @return [...] non_interactive_value in non-interactive mode, the result
108
+ # of continuation proc otherwise.
109
+ #
110
+ def ask(question, answer_type = String, non_interactive_value = nil, &continuation)
111
+ if interactive?
112
+ interaction_highline.ask(question, answer_type, &continuation)
113
+ else
114
+ nil
115
+ end
116
+ end
117
+
118
+ #
119
+ # Same specification as ask, but immediately raises an
120
+ # InteractiveModeRequiredError if the interactive mode is not set!
121
+ #
122
+ def ask!(question, answer_type = String, &continuation)
123
+ interactive!
124
+ ask(question, answer_type, nil, &continuation)
125
+ end
126
+
127
+ #
128
+ # Prints an information message on the appropriate buffer. An optional
129
+ # color may be provided if the environment supports colors.
130
+ #
131
+ # Does nothing when interactive mode is disabled. Delegates the call
132
+ # to highline otherwise.
133
+ #
134
+ # The something argument is expected to be a String. Use display to
135
+ # show complex objects, Enumerable in particular
136
+ #
137
+ # @param [String] something a message to print
138
+ # @param [Symbol] an optional color
139
+ # @return [void]
140
+ #
141
+ def say(something, color = nil)
142
+ if interactive?
143
+ h = interaction_highline
144
+ color.nil? ? h.say(something) : h.say(h.color(something, color))
145
+ end
146
+ end
147
+
148
+ #
149
+ # Displays something on the message buffer.
150
+ #
151
+ # Does nothing when interactive mode is disabled. Delegates the call
152
+ # to highline otherwise.
153
+ #
154
+ # @param [Object] something to write on environment output
155
+ # @return [void]
156
+ #
157
+ def display(something, color = nil)
158
+ if interactive?
159
+ if something.kind_of?(String)
160
+ say(something, color)
161
+ elsif something.kind_of?(Enumerable)
162
+ something.each{|v| display(v, color)}
163
+ else
164
+ display(something.to_s, color)
165
+ end
166
+ end
167
+ end
168
+
169
+ ##############################################################################
170
+ ### Console
171
+ ##############################################################################
172
+
173
+ #
174
+ # When interaction mode is set, colorizes a string with highline.
175
+ # Simply returns str otherwise.
176
+ #
177
+ def color(str, color)
178
+ interactive? ? interaction_highline.color(str, color) : str
179
+ end
180
+
181
+ #
182
+ # Forces the console width.
183
+ #
184
+ # When a width is set, it will always be returned by console_width, bypassing
185
+ # any attempt to use highline to infer it.
186
+ #
187
+ def console_width=(width)
188
+ @console_width = width
189
+ end
190
+
191
+ #
192
+ # Returns width of the console. Width set with console_width= is returned in
193
+ # priority. Otherwise, return highline's output_cols in interactive mode.
194
+ # Returns 80 in all other cases.
195
+ #
196
+ def console_width
197
+ @console_width ||= infer_console_width
198
+ end
199
+
200
+ # Tries to infer the console width
201
+ def infer_console_width
202
+ interactive? ? interaction_highline.output_cols-3 : 80
203
+ end
204
+
205
+ private :infer_console_width
206
+ end # module Interactions
207
+ end # class Environment
208
+ end # module DbAgile
@@ -0,0 +1,47 @@
1
+ module DbAgile
2
+ class Environment
3
+ module OnError
4
+
5
+ # Show backtrace on errors?
6
+ attr_writer :show_backtrace
7
+
8
+ # Shows the backtrace when an error occurs?
9
+ def show_backtrace?
10
+ @show_backtrace
11
+ end
12
+
13
+ #
14
+ # Handles an error that occured during command execution.
15
+ #
16
+ # @param [Exception] error the error that was raised
17
+ # @return nil to continue execution, an error to raise otherwise
18
+ #
19
+ def on_error(command, error)
20
+ case error
21
+ when SystemExit
22
+ when OptionParser::ParseError
23
+ say(error.message, :red)
24
+ display(command.options.to_s)
25
+ when Sequel::Error, IOError
26
+ say(error.message, :red)
27
+ when DbAgile::SchemaSemanticsError
28
+ say(error.message(true), :red)
29
+ when DbAgile::InternalError
30
+ say("DbAgile encountered an internal error.\n Please replay with dba --backtrace and report the error!", :red)
31
+ say(error.message, :red)
32
+ when DbAgile::Error
33
+ say(error.message, :red)
34
+ when Interrupt
35
+ say("Command interrupted by user", :magenta)
36
+ else
37
+ say("ERROR (#{error.class}): #{error.message}", :red)
38
+ end
39
+ if show_backtrace?
40
+ say(error.backtrace.join("\n"))
41
+ end
42
+ error
43
+ end
44
+
45
+ end # module OnError
46
+ end # class Environment
47
+ end # module DbAgile
@@ -0,0 +1,161 @@
1
+ module DbAgile
2
+ class Environment
3
+ module Repository
4
+
5
+ #
6
+ # Checks if the repository exists or should be created.
7
+ #
8
+ # If the repository is currently loaded, this method returns true.
9
+ # Otherwise it returns true if the repository path exists. Note that
10
+ # repository consistency itself (index file) is not checked. You have
11
+ # to load it and catch exception to have a finer grained info.
12
+ #
13
+ def repository_exists?
14
+ return true if @repository
15
+ File.exists?(repository_path)
16
+ end
17
+
18
+ #
19
+ # Returns a friendly path for the repository, to display to the user.
20
+ # If a repository is loaded, the call is delegated. Otherwise, the
21
+ # repository_path is displayed, without attempting to load repository.
22
+ #
23
+ def friendly_repository_path
24
+ if @repository
25
+ @repository.friendly_path
26
+ else
27
+ DbAgile::FileSystemTools::friendly_path!(repository_path)
28
+ end
29
+ end
30
+
31
+ #
32
+ # Returns path to the repository
33
+ #
34
+ def repository_path
35
+ @repository_path
36
+ end
37
+
38
+ #
39
+ # Sets the path to the .dbagile file
40
+ #
41
+ def repository_path=(path)
42
+ @repository = nil
43
+ @repository_path = path
44
+ end
45
+
46
+ #
47
+ # Ensures that repository is loaded and returns the Repository instance.
48
+ #
49
+ # ATTENTION: the Repository instance is kept in cache. It will not be
50
+ # synchronized with modifications of the underlying files made by another
51
+ # process/thread.
52
+ #
53
+ # @raise IOError if the repository does not exists or something goes wrong
54
+ # with repository dir/files
55
+ # @raise CorruptedRepositoryError if something goes wrong when parsing the
56
+ # repository index file
57
+ # @return [Repository] repository instance
58
+ # @see DbAgile::Core::Repository::load
59
+ #
60
+ def repository
61
+ @repository ||= DbAgile::Core::Repository::load(repository_path)
62
+ end
63
+
64
+ #
65
+ # Yields the block with each database in turn
66
+ #
67
+ # As this method is a wrapper on repository, it shares the specification
68
+ # about exceptions.
69
+ #
70
+ # @raise ArgumentError if no block is provided
71
+ #
72
+ def each_database(&block)
73
+ raise ArgumentError, "Missing block" unless block_given?
74
+ repository.each(&block)
75
+ end
76
+
77
+ #
78
+ # Yields the block with the Repository instance (loaded using repository)
79
+ #
80
+ # As this method is a wrapper on repository, it shares the specification
81
+ # about exceptions.
82
+ #
83
+ # @return [...] result of the block execution
84
+ # @raise ArgumentError if no block is provided
85
+ #
86
+ def with_repository
87
+ raise ArgumentError, "Missing block" unless block_given?
88
+ yield(repository)
89
+ end
90
+
91
+ #
92
+ # Yields the block with a Database instance found by name in the
93
+ # repository
94
+ #
95
+ # As this method relies on repository, it shares its exception contract.
96
+ #
97
+ # @raise ArgumentError if no block is provided
98
+ # @raise NoSuchDatabaseError if the database cannot be found.
99
+ # @return block execution result
100
+ #
101
+ def with_database(name)
102
+ raise ArgumentError, "Missing block" unless block_given?
103
+ db = repository.database(name)
104
+ raise NoSuchDatabaseError if db.nil?
105
+ yield(db)
106
+ end
107
+
108
+ #
109
+ # Yields the block with the Database instance for the current one
110
+ # in repository.
111
+ #
112
+ # As this method relies on repository, it shares its exception contract.
113
+ #
114
+ # @raise ArgumentError if no block is provided
115
+ # @raise NoDefaultDatabaseError if the database cannot be found.
116
+ # @return block execution result
117
+ #
118
+ def with_current_database
119
+ raise ArgumentError, "Missing block" unless block_given?
120
+ db = repository.current_database
121
+ raise NoDefaultDatabaseError if db.nil?
122
+ yield(db)
123
+ end
124
+
125
+ #
126
+ # Yields the block with a connection on a given database; diconnect after that.
127
+ #
128
+ # As this method relies on repository, it shares its exception contract.
129
+ #
130
+ # @raise ArgumentError if no block is provided
131
+ # @raise NoSuchDatabaseError if the database cannot be found.
132
+ # @return block execution result
133
+ #
134
+ def with_connection(db, conn_options = {}, &block)
135
+ case db
136
+ when Symbol
137
+ db = repository.database(db)
138
+ when DbAgile::Core::Database
139
+ else
140
+ raise ArgumentError, "Invalid database name #{db}"
141
+ end
142
+ raise NoSuchDatabaseError if db.nil?
143
+ db.with_connection(&block)
144
+ end
145
+
146
+ #
147
+ # Yields the block with a connection on the current database.
148
+ #
149
+ # Same contract as with_connection, expect for parameters.
150
+ #
151
+ # @raise NoDefaultDatabaseError if the database cannot be found.
152
+ #
153
+ def with_current_connection(conn_options = {}, &block)
154
+ with_current_database{|db|
155
+ with_connection(db, conn_options, &block)
156
+ }
157
+ end
158
+
159
+ end # module Repository
160
+ end # class Environment
161
+ end # module DbAgile
@@ -0,0 +1,7 @@
1
+ module DbAgile
2
+
3
+ # Raised when the interactive mode is definitely required,
4
+ # but set to false
5
+ class InteractiveModeRequiredError < DbAgile::Error; end
6
+
7
+ end # module DbAgile
@@ -0,0 +1,23 @@
1
+ module DbAgile
2
+ class Environment
3
+ module Testing
4
+
5
+ # Returns the output buffer as a string
6
+ def output_buffer_str
7
+ if self.output_buffer.kind_of?(StringIO)
8
+ output_buffer.string
9
+ else
10
+ raise DbAgile::AssumptionFailedError, "StringIO expected as output buffer"
11
+ end
12
+ end
13
+
14
+ #
15
+ # Returns true if the current output buffer matches a regular expression
16
+ #
17
+ def has_flushed?(rx)
18
+ (self.output_buffer_str =~ rx) ? true : false
19
+ end
20
+
21
+ end # module Testing
22
+ end # class Environment
23
+ end # module DbAgile
@@ -0,0 +1,122 @@
1
+ require 'dbagile/environment/robustness'
2
+ require 'dbagile/environment/on_error'
3
+ require 'dbagile/environment/buffering'
4
+ require 'dbagile/environment/interactions'
5
+ require 'dbagile/environment/repository'
6
+ require 'dbagile/environment/delegator'
7
+ module DbAgile
8
+ #
9
+ # Defines the contract to be an environment for dbagile.
10
+ #
11
+ class Environment
12
+ include DbAgile::Environment::OnError
13
+ include DbAgile::Environment::Buffering
14
+ include DbAgile::Environment::Interactions
15
+ include DbAgile::Environment::Repository
16
+
17
+ #
18
+ # Installs the testing methods, StringIO on output buffers then
19
+ # returns self
20
+ #
21
+ def with_testing_methods!(interactive = true)
22
+ require 'dbagile/environment/testing'
23
+ extend(DbAgile::Environment::Testing)
24
+ self.output_buffer = StringIO.new
25
+ self.message_buffer = StringIO.new
26
+ self.interactive = interactive
27
+ self
28
+ end
29
+
30
+ #
31
+ # Creates a default Environment instance with following options:
32
+ #
33
+ # - repository_path -> what Environment::default_repository_path returns
34
+ # - input_buffer -> STDIN
35
+ # - output_buffer -> STDOUT
36
+ # - interactive? -> true
37
+ # - asking_buffer -> STDIN
38
+ # - message_buffer -> STDERR
39
+ # - show_backtrace -> false
40
+ #
41
+ def initialize
42
+ @repository_path = Environment::default_repository_path
43
+ @input_buffer = STDIN
44
+ @output_buffer = STDOUT
45
+ @interactive = true
46
+ @asking_buffer = STDIN
47
+ @message_buffer = STDERR
48
+ @show_backtrace = false
49
+ end
50
+
51
+ #
52
+ # Duplicates the environment but removes any cached value (repository
53
+ # and so on)
54
+ #
55
+ def dup
56
+ env = Environment.new
57
+ env.repository_path = self.repository_path
58
+ env.input_buffer = self.input_buffer
59
+ env.output_buffer = self.output_buffer
60
+ env.interactive = self.interactive?
61
+ env.asking_buffer = self.asking_buffer
62
+ env.message_buffer = self.message_buffer
63
+ env.show_backtrace = self.show_backtrace?
64
+ env
65
+ end
66
+
67
+ #
68
+ # Returns the default path to use for a repository.
69
+ #
70
+ # The algorithm implemented tries to locate a repository folder in the
71
+ # following order:
72
+ #
73
+ # - dbagile.idx file in the current directory -> it's parent folder
74
+ # - dbagile folder in the current directory -> ./dbagile
75
+ # - .dbagile folder in user's home -> ~/.dbagile
76
+ #
77
+ # If none of those files exists, ~/.dbagile is returned
78
+ #
79
+ def self.default_repository_path
80
+ if File.exists?("./dbagile.idx")
81
+ "."
82
+ elsif File.exists?("dbagile")
83
+ "dbagile"
84
+ else
85
+ File.join(ENV['HOME'], '.dbagile')
86
+ end
87
+ end
88
+
89
+ #
90
+ # Returns the default environment to use.
91
+ #
92
+ # The algorithm implemented tries to locate a repository folder in the
93
+ # following order:
94
+ #
95
+ # - dbagile.idx file in the current directory -> it's parent folder
96
+ # - dbagile folder in the current directory -> ./dbagile
97
+ # - .dbagile folder in user's home -> ~/.dbagile
98
+ #
99
+ # If none of those files exists, a default environment will be created
100
+ # mapping to an unexisting repository folder in ~/.dbagile
101
+ #
102
+ # By default, the repository is not loaded at all, and errors can therefore
103
+ # occur later, when the repository will be first accessed. Set
104
+ # load_repository to true to force immediate load.
105
+ #
106
+ def self.default(load_repository = false)
107
+ env = Environment.new
108
+ if load_repository
109
+ env.repository
110
+ end
111
+ env
112
+ end
113
+
114
+ #
115
+ # Convenient method for Environment::default(true)
116
+ #
117
+ def self.default!
118
+ Environment::default(true)
119
+ end
120
+
121
+ end # class Environment
122
+ end # module DbAgile
@@ -0,0 +1,30 @@
1
+ module DbAgile
2
+
3
+ # Raised when a database name is not valid
4
+ class InvalidDatabaseName < DbAgile::Error; end
5
+
6
+ # Raised when a database URI is not valid
7
+ class InvalidDatabaseUri < DbAgile::Error; end
8
+
9
+ # Raised when a command does not exists (dba command line tool)
10
+ class NoSuchCommandError < DbAgile::Error; end
11
+
12
+ # Raised when the repository seems corrupted
13
+ class CorruptedRepositoryError < DbAgile::Error; end
14
+
15
+ # Raised when a database cannot be found
16
+ class NoSuchDatabaseError < DbAgile::Error; end
17
+
18
+ # Raised when a database name is already used (on add)
19
+ class DatabaseNameConflictError < DbAgile::Error; end
20
+
21
+ # Raised when no default database is set
22
+ class NoDefaultDatabaseError < DbAgile::Error; end
23
+
24
+ # Raised when input parsing of data fails for some reason
25
+ class InvalidFormatError < DbAgile::Error; end
26
+
27
+ # Raised when usage of schema files fails because they are not installed
28
+ class NoSchemaFilesError < DbAgile::Error; end
29
+
30
+ end # module DbAgile
@@ -0,0 +1,99 @@
1
+ module DbAgile
2
+ module IO
3
+ module CSV
4
+ extend IO::TypeSafe
5
+
6
+ # Normalizes CSV options from DBAgile options
7
+ def normalize_options(options)
8
+ if options[:type_system]
9
+ options[:headers] = true unless options.key?(:headers)
10
+ options[:quote_char] = "'" unless options.key?(:quote_char)
11
+ options[:force_quotes] = true unless options.key?(:force_quotes)
12
+ end
13
+ if options[:headers]
14
+ options[:write_headers] = true
15
+ options[:return_headers] = true
16
+ end
17
+ options
18
+ end
19
+ module_function :normalize_options
20
+
21
+ # Makes the CSV require, depending on Ruby version
22
+ def build_csv_instance(io, options)
23
+ if RUBY_VERSION >= "1.9.0"
24
+ require 'csv'
25
+ options = normalize_options(options)
26
+ csv_options = options.dup.delete_if{|key,value| !::CSV::DEFAULT_OPTIONS.key?(key)}
27
+ [::CSV.new(io, csv_options), options]
28
+ else
29
+ require 'faster_csv'
30
+ options = normalize_options(options)
31
+ csv_options = options.dup.delete_if{|key,value| !FasterCSV::DEFAULT_OPTIONS.key?(key)}
32
+ [FasterCSV.new(io, csv_options), options]
33
+ end
34
+ end
35
+ module_function :build_csv_instance
36
+
37
+ #
38
+ # Outputs some data as a CSV string.
39
+ #
40
+ # @return [...] the buffer itself
41
+ #
42
+ def to_csv(data, columns, buffer = "", options = {})
43
+ # Creates a CSV outputter with options
44
+ csv, options = build_csv_instance(buffer, options)
45
+
46
+ # Write header if required
47
+ if options[:headers]
48
+ if ts = options[:type_system]
49
+ csv << columns.collect{|c| ts.to_literal(c)}
50
+ else
51
+ csv << columns
52
+ end
53
+ end
54
+
55
+ # Write tuples now
56
+ with_type_safe_relation(data, options) do |tuple|
57
+ csv << columns.collect{|c| tuple[c]}
58
+ end
59
+
60
+ # Return buffer
61
+ buffer
62
+ end
63
+ module_function :to_csv
64
+
65
+ #
66
+ # If a block is given yields it with each tuple that can be loaded from
67
+ # the CSV input and returns nil. Otherwise, reads the CSV input and returns
68
+ # an array of tuples.
69
+ #
70
+ def from_csv(input, options = {})
71
+ # Creates a CSV inputer with options
72
+ csv, options = build_csv_instance(input, options)
73
+ if ts = options[:type_system]
74
+ converter = lambda{|field| ts.parse_literal(field)}
75
+ csv.header_convert(&converter)
76
+ csv.convert(&converter)
77
+ end
78
+
79
+ # Load data now
80
+ ts = options[:type_system]
81
+ if block_given?
82
+ csv.each do |row|
83
+ next if row.header_row?
84
+ yield(row.to_hash)
85
+ end
86
+ else
87
+ tuples = []
88
+ csv.each do |row|
89
+ next if row.header_row?
90
+ tuples << row.to_hash
91
+ end
92
+ tuples
93
+ end
94
+ end
95
+ module_function :from_csv
96
+
97
+ end # module CSVTools
98
+ end # module IO
99
+ end # module DbAgile