mongify 0.0.4

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 (63) hide show
  1. data/.gitignore +20 -0
  2. data/CHANGELOG.rdoc +22 -0
  3. data/Gemfile +4 -0
  4. data/Gemfile.lock +68 -0
  5. data/LICENSE +20 -0
  6. data/README.rdoc +86 -0
  7. data/Rakefile +73 -0
  8. data/bin/mongify +14 -0
  9. data/features/options.feature +46 -0
  10. data/features/print.feature +10 -0
  11. data/features/process.feature +19 -0
  12. data/features/step_definitions/mongify_steps.rb +42 -0
  13. data/features/step_definitions/mongo_steps.rb +7 -0
  14. data/features/support/env.rb +28 -0
  15. data/lib/mongify.rb +22 -0
  16. data/lib/mongify/cli.rb +8 -0
  17. data/lib/mongify/cli/application.rb +43 -0
  18. data/lib/mongify/cli/help_command.rb +16 -0
  19. data/lib/mongify/cli/options.rb +94 -0
  20. data/lib/mongify/cli/report.rb +11 -0
  21. data/lib/mongify/cli/version_command.rb +18 -0
  22. data/lib/mongify/cli/worker_command.rb +93 -0
  23. data/lib/mongify/configuration.rb +46 -0
  24. data/lib/mongify/database.rb +5 -0
  25. data/lib/mongify/database/base_connection.rb +78 -0
  26. data/lib/mongify/database/column.rb +81 -0
  27. data/lib/mongify/database/no_sql_connection.rb +62 -0
  28. data/lib/mongify/database/sql_connection.rb +61 -0
  29. data/lib/mongify/database/table.rb +74 -0
  30. data/lib/mongify/exceptions.rb +13 -0
  31. data/lib/mongify/translation.rb +55 -0
  32. data/lib/mongify/translation/printer.rb +20 -0
  33. data/lib/mongify/translation/process.rb +61 -0
  34. data/lib/mongify/ui.rb +45 -0
  35. data/lib/mongify/version.rb +3 -0
  36. data/mongify.gemspec +42 -0
  37. data/spec/default.watch +199 -0
  38. data/spec/files/base_configuration.rb +9 -0
  39. data/spec/files/empty_translation.rb +0 -0
  40. data/spec/files/simple_translation.rb +26 -0
  41. data/spec/mongify/cli/application_spec.rb +19 -0
  42. data/spec/mongify/cli/help_command_spec.rb +18 -0
  43. data/spec/mongify/cli/options_spec.rb +62 -0
  44. data/spec/mongify/cli/version_command_spec.rb +24 -0
  45. data/spec/mongify/cli/worker_command_spec.rb +115 -0
  46. data/spec/mongify/configuration_spec.rb +25 -0
  47. data/spec/mongify/database/base_connection_spec.rb +59 -0
  48. data/spec/mongify/database/column_spec.rb +103 -0
  49. data/spec/mongify/database/no_sql_connection_spec.rb +131 -0
  50. data/spec/mongify/database/sql_connection_spec.rb +91 -0
  51. data/spec/mongify/database/table_spec.rb +120 -0
  52. data/spec/mongify/translation/printer_spec.rb +34 -0
  53. data/spec/mongify/translation/process_spec.rb +68 -0
  54. data/spec/mongify/translation_spec.rb +59 -0
  55. data/spec/mongify/ui_spec.rb +73 -0
  56. data/spec/mongify_spec.rb +15 -0
  57. data/spec/spec.opts +1 -0
  58. data/spec/spec_helper.rb +22 -0
  59. data/spec/support/config_reader.rb +21 -0
  60. data/spec/support/database.example +17 -0
  61. data/spec/support/database_output.txt +27 -0
  62. data/spec/support/generate_database.rb +91 -0
  63. metadata +370 -0
@@ -0,0 +1,28 @@
1
+ $:.unshift 'lib'
2
+
3
+ require 'rubygems'
4
+ require 'tempfile'
5
+ require 'fileutils'
6
+ require 'mongify/cli/application'
7
+ require 'mongify'
8
+ require 'spec/support/generate_database'
9
+ require 'spec/support/config_reader'
10
+
11
+ ::CONNECTION_CONFIG = ConfigReader.new(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))) + '/spec/support/database.yml')
12
+ ::DATABASE_PRINT = File.read(File.dirname(File.dirname(File.dirname(File.expand_path(__FILE__)))) + '/spec/support/database_output.txt')
13
+
14
+ module MongifyWorld
15
+ def run(cmd)
16
+ stderr_file = Tempfile.new('mongify-world')
17
+ stderr_file.close
18
+ @last_stdout = `#{cmd} 2> #{stderr_file.path}`
19
+ @last_exit_status = $?.exitstatus
20
+ @last_stderr = IO.read(stderr_file.path)
21
+ end
22
+
23
+ def mongify(args)
24
+ run("ruby -Ilib -rubygems bin/mongify #{args}")
25
+ end
26
+ end
27
+
28
+ World(MongifyWorld)
@@ -0,0 +1,22 @@
1
+ #
2
+ # Mongify's core functionality
3
+ #
4
+ require 'active_support/core_ext'
5
+
6
+ require 'mongify/ui'
7
+ require 'mongify/exceptions'
8
+ require 'mongify/translation'
9
+ require 'mongify/configuration'
10
+ require 'mongify/database'
11
+
12
+ module Mongify
13
+ class << self
14
+ def root=(value)
15
+ @root = value
16
+ end
17
+ def root
18
+ raise RootMissing, "Root not configured" if @root.nil?
19
+ @root
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,8 @@
1
+ require 'mongify'
2
+
3
+ require 'mongify/cli/options'
4
+ require 'mongify/cli/report'
5
+ require 'mongify/cli/version_command'
6
+ require 'mongify/cli/help_command'
7
+ require 'mongify/cli/worker_command'
8
+ require 'mongify/cli/application'
@@ -0,0 +1,43 @@
1
+ module Mongify
2
+ module CLI
3
+ #
4
+ # Represents an instance of a Mongify application.
5
+ # This is the entry point for all invocations of Mongify from the
6
+ # command line.
7
+ #
8
+ class Application
9
+
10
+ STATUS_SUCCESS = 0
11
+ STATUS_ERROR = 1
12
+
13
+ def initialize(arguments=["--help"], stdin=$stdin, stdout=$stdout)
14
+ @options = Options.new(arguments)
15
+ @status = STATUS_SUCCESS
16
+ Mongify::Configuration.in_stream = stdin
17
+ Mongify::Configuration.out_stream = stdout
18
+ end
19
+
20
+ def execute!
21
+ begin
22
+ cmd = @options.parse
23
+ cmd.execute(self)
24
+ rescue Exception => error
25
+ $stderr.puts "Error: #{error}"
26
+ report_error
27
+ end
28
+ return @status
29
+ end
30
+
31
+ def output(message)
32
+ UI.puts(message)
33
+ end
34
+
35
+ def report_success
36
+ @status = STATUS_SUCCESS
37
+ end
38
+ def report_error
39
+ @status = STATUS_ERROR
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,16 @@
1
+ module Mongify
2
+ module CLI
3
+ #
4
+ # A command to display usage information for this application.
5
+ #
6
+ class HelpCommand
7
+ def initialize(parser)
8
+ @parser = parser
9
+ end
10
+ def execute(view)
11
+ view.output(@parser.to_s)
12
+ view.report_success
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,94 @@
1
+ require 'optparse'
2
+ module Mongify
3
+ module CLI
4
+ #
5
+ # Used to parse the options for an application
6
+ #
7
+ class Options
8
+ def initialize(argv)
9
+ @parsed = false
10
+ @argv = argv
11
+ @parser = OptionParser.new
12
+ @report_class = VerboseReport
13
+ #@command_class = ReekCommand
14
+ set_options
15
+
16
+ end
17
+
18
+ def banner
19
+ progname = @parser.program_name
20
+ return <<EOB
21
+ Usage: #{progname} command [database_translation.rb] [-c database.config]
22
+
23
+ Commands:
24
+ #{Mongify::CLI::WorkerCommand.list_commands.join("\n")}
25
+
26
+ Examples:
27
+
28
+ #{progname} translate -c datbase.config
29
+ #{progname} tr -c database.config
30
+ #{progname} check -c database.config
31
+ #{progname} process database_translation.rb -c database.config
32
+
33
+ See http://github.com/anlek/mongify for more details
34
+
35
+ EOB
36
+ end
37
+
38
+
39
+ def set_options
40
+ @parser.banner = banner
41
+ @parser.separator "Common options:"
42
+ @parser.on("-h", "--help", "Show this message") do
43
+ @command_class = HelpCommand
44
+ end
45
+ @parser.on("-v", "--version", "Show version") do
46
+ @command_class = VersionCommand
47
+ end
48
+ @parser.on('-c', '--config FILE', "Configuration File to use") do |file|
49
+ @config_file = file
50
+ end
51
+
52
+ @parser.separator "\nReport formatting:"
53
+ @parser.on("-q", "--[no-]quiet", "Suppress extra output") do |opt|
54
+ @report_class = opt ? QuietReport : VerboseReport
55
+ end
56
+ end
57
+
58
+ def parse
59
+ parse_options
60
+
61
+ if @command_class == HelpCommand
62
+ HelpCommand.new(@parser)
63
+ elsif @command_class == VersionCommand
64
+ VersionCommand.new(@parser.program_name)
65
+ else
66
+ raise ConfigurationFileNotFound unless @config_file
67
+ #TODO: In the future, request sql_connection and nosql_connection from user input
68
+ config = Configuration.parse(@config_file)
69
+
70
+ WorkerCommand.new(action, config, translation_file, @parser)
71
+ end
72
+ end
73
+
74
+ private
75
+
76
+ def translation_file(argv=@argv)
77
+ parse_options
78
+ return nil if argv.length < 2
79
+ argv[1]
80
+ end
81
+
82
+ def action(argv=@argv)
83
+ parse_options
84
+ @argv.try(:[],0) || ''
85
+ end
86
+
87
+ def parse_options
88
+ @parsed = true && @parser.parse!(@argv) unless @parsed
89
+ end
90
+
91
+
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,11 @@
1
+ module Mongify
2
+ module CLI
3
+ class QuietReport
4
+
5
+ end
6
+
7
+ class VerboseReport
8
+
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,18 @@
1
+ require 'mongify/version'
2
+ module Mongify
3
+ module CLI
4
+
5
+ #
6
+ # A command to report the application's current version number.
7
+ #
8
+ class VersionCommand
9
+ def initialize(progname)
10
+ @progname = progname
11
+ end
12
+ def execute(view)
13
+ view.output("#{@progname} #{Mongify::VERSION}\n")
14
+ view.report_success
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,93 @@
1
+ module Mongify
2
+ module CLI
3
+
4
+ #
5
+ # A command to run the different commands in the application (related to Mongifying).
6
+ #
7
+ class WorkerCommand
8
+ attr_accessor :view
9
+
10
+ AVAILABLE_COMMANDS = {
11
+ :check => {:commands => ['check', 'ck'], :description => "Checks connection for sql and no_sql databases", :required => [:configuration_file]},
12
+ :translate => {:commands => ['translate', 'tr'], :description => "Spits out translation from a sql connection", :required => [:configuration_file]},
13
+ :process => {:commands => ['process', 'pr'], :description => "Takes a translation and process it to mongodb", :required => [:configuration_file, :translation_file]}
14
+ }
15
+ def self.list_commands
16
+ [].tap do |commands|
17
+ AVAILABLE_COMMANDS.each do |key, obj|
18
+ commands << "#{obj[:commands].map{|w| %["#{w}"]}.to_sentence(:two_words_connector => ' or ', :last_word_connector => ', or ').ljust(25)} >> #{obj[:description]}#{ " [#{obj[:required].join(', ')}]" if obj[:required]}"
19
+ end
20
+ end.sort
21
+ end
22
+
23
+ def self.find_command(command)
24
+ AVAILABLE_COMMANDS.each do |key, options|
25
+ return [key, options] if(options[:commands].include?(command.to_s.downcase))
26
+ end
27
+ 'unknown'
28
+ end
29
+
30
+ def initialize(command, config=nil, translation_file=nil, parser="")
31
+ @command = command.to_s.downcase
32
+ @config = config
33
+ @translation_file = translation_file
34
+ @parser = parser
35
+ end
36
+
37
+ def execute(view)
38
+ self.view = view
39
+
40
+ current_command, command_options = find_command
41
+
42
+ if command_options
43
+ #FIXME: Should parse configuration file in this action, (when I know it's required)
44
+ raise ConfigurationFileNotFound, "Configuration file is required" if command_options[:required] && command_options[:required].include?(:configuration_file) && @config.nil?
45
+ if command_options[:required] && command_options[:required].include?(:translation_file)
46
+ raise TranslationFileNotFound, "Translation file is required for command '#{current_command}'" unless @translation_file
47
+ raise TranslationFileNotFound, "Unable to find Translation File at #{@translation_file}" unless File.exists?(@translation_file)
48
+ @translation = Translation.parse(@translation_file)
49
+ end
50
+ end
51
+
52
+ case current_command
53
+ when :translate
54
+ check_connections
55
+ view.output(Mongify::Translation.load(@config.sql_connection).print)
56
+ when :check
57
+ view.output("SQL connection works") if check_sql_connection
58
+ view.output("NoSQL connection works") if check_nosql_connection
59
+ when :process
60
+ check_connections
61
+ @translation.process(@config.sql_connection, @config.no_sql_connection)
62
+ else
63
+ view.output("Unknown action #{@command}\n\n#{@parser}")
64
+ view.report_error
65
+ return
66
+ end
67
+ view.report_success
68
+ end
69
+
70
+ def find_command(command=@command)
71
+ self.class.find_command(command)
72
+ end
73
+
74
+ #######
75
+ private
76
+ #######
77
+
78
+ def check_connections(sql_only = false)
79
+ check_sql_connection && (sql_only || check_nosql_connection)
80
+ end
81
+
82
+ def check_sql_connection
83
+ @config.sql_connection.valid? && @config.sql_connection.has_connection?
84
+ end
85
+
86
+ def check_nosql_connection
87
+ @config.no_sql_connection.valid? && @config.no_sql_connection.has_connection?
88
+ end
89
+
90
+
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,46 @@
1
+ module Mongify
2
+ #
3
+ # This extracts the configuration for sql and no sql
4
+ #
5
+ class Configuration
6
+ class << self
7
+ attr_accessor :in_stream, :out_stream
8
+
9
+ def parse_translation(file_name)
10
+ raise Mongify::TranslationFileNotFound, "File #{file_name} is missing" unless File.exists?(file_name)
11
+ Mongify::Translation.parse(file_name)
12
+ end
13
+
14
+ def parse_configuration(file_name)
15
+ raise Mongify::ConfigurationFileNotFound, "File #{file_name} is missing" unless File.exists?(file_name)
16
+ Mongify::Configuration.parse(file_name)
17
+ end
18
+
19
+ def parse(file_name)
20
+ config = self.new
21
+ config.instance_eval(File.read(file_name))
22
+ config
23
+ end
24
+
25
+ end #self
26
+
27
+ def mongodb_connection(options={}, &block)
28
+ options.stringify_keys!
29
+ options['adapter'] ||= 'mongodb'
30
+ no_sql_connection(options, &block)
31
+ end
32
+
33
+ def sql_connection(options={}, &block)
34
+ @sql_connection ||= Mongify::Database::SqlConnection.new(options)
35
+ @sql_connection.instance_exec(&block) if block_given?
36
+ @sql_connection
37
+ end
38
+
39
+ def no_sql_connection(options={}, &block)
40
+ @no_sql_connection ||= Mongify::Database::NoSqlConnection.new(options)
41
+ @no_sql_connection.instance_exec(&block) if block_given?
42
+ @no_sql_connection
43
+ end
44
+
45
+ end
46
+ end
@@ -0,0 +1,5 @@
1
+ require 'mongify/database/base_connection'
2
+ require 'mongify/database/no_sql_connection'
3
+ require 'mongify/database/sql_connection'
4
+ require 'mongify/database/table'
5
+ require 'mongify/database/column'
@@ -0,0 +1,78 @@
1
+ require 'active_record'
2
+
3
+ module Mongify
4
+ module Database
5
+ #
6
+ # Basic configuration for any sql or non sql database
7
+ #
8
+ class BaseConnection
9
+
10
+ REQUIRED_FIELDS = %w{host}
11
+ AVAILABLE_FIELDS = %w{adapter host username password database socket port}
12
+
13
+ def initialize(options=nil)
14
+ if options
15
+ options.stringify_keys!
16
+ options.each do |key, value|
17
+ instance_variable_set "@#{key}", value
18
+ end
19
+ end
20
+ end
21
+
22
+ def to_hash
23
+ hash = {}
24
+ instance_variables.each do |variable|
25
+ value = self.instance_variable_get variable
26
+ hash[variable.gsub('@','').to_sym] = value unless value.nil?
27
+ end
28
+ hash
29
+ end
30
+
31
+ def valid?
32
+ REQUIRED_FIELDS.each do |require_field|
33
+ return false unless instance_variables.include?("@#{require_field}") and
34
+ !instance_variable_get("@#{require_field}").to_s.empty?
35
+ end
36
+ true
37
+ end
38
+
39
+
40
+ def setup_connection_adapter
41
+ raise NotImplementedError
42
+ end
43
+
44
+ def has_connection?
45
+ raise NotImplementedError
46
+ end
47
+
48
+ def adapter(value=nil)
49
+ @adapter = value.to_s unless value.nil?
50
+ @adapter
51
+ end
52
+
53
+
54
+ def respond_to?(method, *args)
55
+ return true if AVAILABLE_FIELDS.include?(method.to_s)
56
+ super(method)
57
+ end
58
+
59
+ def method_missing(method, *args)
60
+ method_name = method.to_s #.gsub("=", '')
61
+ if AVAILABLE_FIELDS.include?(method_name.to_s)
62
+ class_eval <<-EOF
63
+ def #{method_name}(value=nil)
64
+ @#{method_name} = value unless value.nil?
65
+ @#{method_name}
66
+ end
67
+ EOF
68
+ value = args.first if args.size > 0
69
+ send(method,value)
70
+ else
71
+ super(method, args)
72
+ end
73
+
74
+ end
75
+
76
+ end
77
+ end
78
+ end