mongify 0.0.9 → 0.1.0

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 (40) hide show
  1. data/CHANGELOG.rdoc +8 -0
  2. data/Gemfile.lock +1 -1
  3. data/README.rdoc +40 -16
  4. data/features/options.feature +3 -7
  5. data/features/print.feature +1 -1
  6. data/features/process.feature +0 -1
  7. data/features/support/env.rb +4 -1
  8. data/lib/mongify.rb +3 -1
  9. data/lib/mongify/cli.rb +0 -1
  10. data/lib/mongify/cli/application.rb +10 -1
  11. data/lib/mongify/cli/help_command.rb +1 -0
  12. data/lib/mongify/cli/options.rb +13 -14
  13. data/lib/mongify/cli/version_command.rb +1 -0
  14. data/lib/mongify/cli/worker_command.rb +15 -7
  15. data/lib/mongify/configuration.rb +14 -12
  16. data/lib/mongify/database.rb +2 -1
  17. data/lib/mongify/database/base_connection.rb +25 -15
  18. data/lib/mongify/database/column.rb +99 -21
  19. data/lib/mongify/database/data_row.rb +67 -0
  20. data/lib/mongify/database/no_sql_connection.rb +48 -10
  21. data/lib/mongify/database/sql_connection.rb +34 -4
  22. data/lib/mongify/database/table.rb +69 -7
  23. data/lib/mongify/exceptions.rb +7 -0
  24. data/lib/mongify/translation.rb +45 -2
  25. data/lib/mongify/translation/printer.rb +4 -3
  26. data/lib/mongify/translation/process.rb +11 -4
  27. data/lib/mongify/ui.rb +10 -1
  28. data/lib/mongify/version.rb +2 -1
  29. data/spec/mongify/cli/worker_command_spec.rb +3 -4
  30. data/spec/mongify/configuration_spec.rb +6 -12
  31. data/spec/mongify/database/base_connection_spec.rb +7 -3
  32. data/spec/mongify/database/column_spec.rb +171 -28
  33. data/spec/mongify/database/data_row_spec.rb +102 -0
  34. data/spec/mongify/database/no_sql_connection_spec.rb +4 -6
  35. data/spec/mongify/database/table_spec.rb +23 -2
  36. data/spec/mongify/translation/printer_spec.rb +3 -3
  37. data/spec/support/config_reader.rb +3 -1
  38. data/spec/support/generate_database.rb +7 -0
  39. metadata +7 -5
  40. data/lib/mongify/cli/report.rb +0 -11
@@ -1,22 +1,15 @@
1
1
  module Mongify
2
2
  #
3
- # This extracts the configuration for sql and no sql
3
+ # Handles all tasks regarding the connection to sql and no-sql database
4
+ # It also handles the parsing of the data
4
5
  #
5
6
  class Configuration
6
7
  class << self
7
8
  attr_accessor :in_stream, :out_stream
8
9
 
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
-
10
+ # Parses a external configuration file and evaluates it and returns a instence of a configuration class
19
11
  def parse(file_name)
12
+ raise Mongify::ConfigurationFileNotFound, "File #{file_name} is missing" unless File.exists?(file_name)
20
13
  config = self.new
21
14
  config.instance_eval(File.read(file_name))
22
15
  config
@@ -24,18 +17,27 @@ module Mongify
24
17
 
25
18
  end #self
26
19
 
20
+ # Returns a no_sql_connection which is bound to a mongodb adapter
21
+ # or builds a new no_sql_connection if block is given
27
22
  def mongodb_connection(options={}, &block)
23
+ return @mongodb_conncetion if @mongodb_connection and !block_given?
28
24
  options.stringify_keys!
29
25
  options['adapter'] ||= 'mongodb'
30
- no_sql_connection(options, &block)
26
+ @mongodb_connection = no_sql_connection(options, &block)
31
27
  end
32
28
 
29
+ # Returns a sql_connection
30
+ # If a block is given, it will be executed on the connection
31
+ # For more information, see {Mongify::Database::SqlConnection}
33
32
  def sql_connection(options={}, &block)
34
33
  @sql_connection ||= Mongify::Database::SqlConnection.new(options)
35
34
  @sql_connection.instance_exec(&block) if block_given?
36
35
  @sql_connection
37
36
  end
38
37
 
38
+ # Returns a sql_connection
39
+ # If a block is given, it will be executed on the connection
40
+ # For more information, see {Mongify::Database::NoSqlConnection}
39
41
  def no_sql_connection(options={}, &block)
40
42
  @no_sql_connection ||= Mongify::Database::NoSqlConnection.new(options)
41
43
  @no_sql_connection.instance_exec(&block) if block_given?
@@ -1,5 +1,6 @@
1
1
  require 'mongify/database/base_connection'
2
2
  require 'mongify/database/no_sql_connection'
3
3
  require 'mongify/database/sql_connection'
4
+ require 'mongify/database/data_row'
4
5
  require 'mongify/database/table'
5
- require 'mongify/database/column'
6
+ require 'mongify/database/column'
@@ -1,22 +1,25 @@
1
1
  module Mongify
2
+ # Module that deals with all database related items
2
3
  module Database
3
4
  #
4
- # Basic configuration for any sql or non sql database
5
+ # This is a Basic configuration for any sql or non sql database
5
6
  #
6
7
  class BaseConnection
7
-
8
+ # List of required fields to make a valid base connection
8
9
  REQUIRED_FIELDS = %w{host}
10
+ # List of all the available fields to make up a connection
9
11
  AVAILABLE_FIELDS = %w{adapter host username password database socket port encoding}
10
-
12
+
11
13
  def initialize(options=nil)
12
14
  if options
13
15
  options.stringify_keys!
14
16
  options.each do |key, value|
15
- instance_variable_set "@#{key}", value
17
+ instance_variable_set "@#{key.downcase}", value if AVAILABLE_FIELDS.include?(key.downcase)
16
18
  end
17
19
  end
18
20
  end
19
-
21
+
22
+ # Returns all settings as a hash, this is used mainly in building ActiveRecord::Base.establish_connection
20
23
  def to_hash
21
24
  hash = {}
22
25
  instance_variables.each do |variable|
@@ -26,7 +29,9 @@ module Mongify
26
29
  hash
27
30
  end
28
31
 
32
+ # Ensures the required fields are filled
29
33
  def valid?
34
+ #TODO: Improve this to create an errors array with detailed errors (or maybe just use activemodel)
30
35
  REQUIRED_FIELDS.each do |require_field|
31
36
  return false unless instance_variables.include?("@#{require_field}") and
32
37
  !instance_variable_get("@#{require_field}").to_s.empty?
@@ -34,32 +39,37 @@ module Mongify
34
39
  true
35
40
  end
36
41
 
37
-
42
+ # Used to setup connection, Raises NotImplementedError because it needs to be setup in BaseConnection's children
38
43
  def setup_connection_adapter
39
44
  raise NotImplementedError
40
45
  end
41
46
 
47
+ # Used to test connection, Raises NotImplementedError because it needs to be setup in BaseConnection's children
42
48
  def has_connection?
43
49
  raise NotImplementedError
44
50
  end
45
51
 
46
- def adapter(value=nil)
47
- @adapter = value.to_s unless value.nil?
48
- @adapter
49
- end
50
-
51
52
 
52
- def respond_to?(method, *args)
53
+ # Returns true if we are trying to respond_to AVAILABLE_FIELDS functions
54
+ def respond_to?(method, *args)
53
55
  return true if AVAILABLE_FIELDS.include?(method.to_s)
54
56
  super(method)
55
57
  end
56
-
58
+
59
+ # Building set and/or return functions for AVAILABLE_FIELDS
60
+ # Example:
61
+ #
62
+ # def host(value=nil)
63
+ # @host = value.to_s unless value.nil?
64
+ # @host
65
+ # end
66
+ #
57
67
  def method_missing(method, *args)
58
- method_name = method.to_s #.gsub("=", '')
68
+ method_name = method.to_s
59
69
  if AVAILABLE_FIELDS.include?(method_name.to_s)
60
70
  class_eval <<-EOF
61
71
  def #{method_name}(value=nil)
62
- @#{method_name} = value unless value.nil?
72
+ @#{method_name} = value.to_s unless value.nil?
63
73
  @#{method_name}
64
74
  end
65
75
  EOF
@@ -2,28 +2,88 @@ require 'active_record/connection_adapters/abstract/schema_definitions'
2
2
  module Mongify
3
3
  module Database
4
4
  #
5
- # A column in the sql table
5
+ # A column that is used to access sql data and transform it into the no sql database
6
6
  #
7
+ #
8
+ # ==== Structure
9
+ #
10
+ # Structure for defining a column is as follows:
11
+ # column "name", :type, {options}
12
+ # <em>Columns with no type given will be set to <tt>:string</tt></em>
13
+ #
14
+ # ==== Notes
15
+ # Leaving a column out when defining a table will result in a copy of the information (as a string).
16
+ # ==== Types
17
+ #
18
+ # Types of columns are supported:
19
+ # :key # Columns that are primary keys need to be marked as :key type
20
+ # :integer # Will be converted to a integer
21
+ # :float # Will be converted to a float
22
+ # :decimal # Will be converted to a BigDecimal
23
+ # :string # Will be converted to a string
24
+ # :text # Will be converted to a string
25
+ # :datetime # Will be converted to a Time format (DateTime is not currently supported in the Mongo ruby driver)
26
+ # :date # Will be converted to a Time format (Date is not currently supported in the Mongo ruby driver)
27
+ # :timestamps # Will be converted to a Time format
28
+ # :time # Will be converted to a Time format (the date portion of the Time object will be 2000-01-01)
29
+ # :binary # Will be converted to a string
30
+ # :boolean # Will be converted to a true or false values
31
+ #
32
+ # ==== Options
33
+ #
34
+ # column "post_id", :integer, :referneces => :posts # Referenced columns need to be marked as such, this will mean that they will be updated
35
+ # # with the new BSON::ObjectID.
36
+ # <b>NOTE: if you rename the table 'posts', you should set the :references to the new name</b>
37
+ #
38
+ # column "name", :string, :ignore => true # Ignoring a column will make the column NOT copy over to the new database
39
+ #
40
+ # column "surname", :string, :rename_to => 'last_name'# Rename_to allows you to rename the column
41
+ #
42
+ # column "post_id", :integer, :auto_detect => true # Will run auto detect and make this column a :references => 'posts', :on => 'post_id' for you
43
+ # # More used when reading a sql database, NOT recommended for use during processing of translation
44
+ #
7
45
  class Column
8
46
  attr_reader :sql_name, :type, :options
9
47
 
48
+ #List of available options for a column
10
49
  AVAILABLE_OPTIONS = ['references', 'ignore', 'rename_to']
11
-
50
+
51
+ # Auto detects if a column is an :key column or is a reference column
52
+ def self.auto_detect(column)
53
+ case column.sql_name.downcase
54
+ when 'id'
55
+ column.type = :key if column.type == :integer
56
+ when /(.*)_id/
57
+ column.references = $1.to_s.pluralize unless column.referenced? || column.type != :integer
58
+ end
59
+ end
60
+
12
61
  def initialize(sql_name, type=:string, options={})
13
62
  @sql_name = sql_name
14
- type = :string if type.nil?
15
- @type = type.is_a?(Symbol) ? type : type.to_sym
63
+ options, type = type, nil if type.is_a?(Hash)
64
+ self.type = type
16
65
  @options = options.stringify_keys
17
-
18
- auto_detect!
66
+ @auto_detect = @options.delete('auto_detect')
67
+ run_auto_detect!
19
68
 
20
69
  self
21
70
  end
22
71
 
72
+ # Allows you to set a type
73
+ def type=(value=:string)
74
+ value = :string if value.nil?
75
+ @type = value.is_a?(Symbol) ? value : value.to_sym
76
+ end
77
+
78
+ # Returns the no_sql record name
23
79
  def name
24
80
  @name ||= rename_to || sql_name
25
81
  end
26
82
 
83
+ # Returns a translated hash from a given value
84
+ # Example:
85
+ # @column = Column.new("surname", :string, :rename_to => 'last_name')
86
+ # @column.translate("Smith") # => {"last_name" => "Smith"}
27
87
  def translate(value)
28
88
  return {} if ignored?
29
89
  case type
@@ -34,13 +94,24 @@ module Mongify
34
94
  end
35
95
  end
36
96
 
97
+ # Returns a string representation of the column as it would show in a translation file.
98
+ # Mainly used during print out of translation file
37
99
  def to_print
38
100
  "column \"#{name}\", :#{type}".tap do |output|
39
101
  output_options = options.map{|k, v| (v == nil) ? nil : ":#{k} => \"#{v}\""}.compact
40
102
  output << ", #{output_options.join(', ')}" unless output_options.blank?
41
103
  end
42
104
  end
105
+ alias :to_s :to_print
43
106
 
107
+ # Sets up a accessor method for an option
108
+ #
109
+ # def rename_to=(value)
110
+ # options['rename_to'] = value
111
+ # end
112
+ # def rename_to
113
+ # options['rename_to']
114
+ # end
44
115
  def method_missing(meth, *args, &blk)
45
116
  method_name = meth.to_s.gsub("=", '')
46
117
  if AVAILABLE_OPTIONS.include?(method_name)
@@ -58,18 +129,36 @@ module Mongify
58
129
  end
59
130
  end
60
131
 
132
+ # Returns true if the column is a reference column
61
133
  def referenced?
62
134
  !self.options['references'].nil?
63
135
  end
64
136
 
137
+ # Returns true if column is being renamed
65
138
  def renamed?
66
139
  self.name != self.sql_name
67
140
  end
68
141
 
142
+ # Returns true if column is a :key type column
143
+ def key?
144
+ self.type == :key
145
+ end
146
+
147
+ # Returns true if column should be auto_detected (passed via options)
148
+ def auto_detect?
149
+ !!@auto_detect
150
+ end
151
+
152
+ # Returns true if column is ignored
153
+ def ignored?
154
+ !!self.ignore
155
+ end
156
+
69
157
  #######
70
158
  private
71
159
  #######
72
160
 
161
+ # Casts the value to a given type
73
162
  def type_cast(value)
74
163
  return nil if value.nil?
75
164
  case type
@@ -81,28 +170,17 @@ module Mongify
81
170
  when :datetime then ActiveRecord::ConnectionAdapters::Column.string_to_time(value)
82
171
  when :timestamp then ActiveRecord::ConnectionAdapters::Column.string_to_time(value)
83
172
  when :time then ActiveRecord::ConnectionAdapters::Column.string_to_dummy_time(value)
84
- when :date then ActiveRecord::ConnectionAdapters::Column.string_to_date(value)
173
+ when :date then ActiveRecord::ConnectionAdapters::Column.string_to_time(value)
85
174
  when :binary then ActiveRecord::ConnectionAdapters::Column.binary_to_string(value)
86
175
  when :boolean then ActiveRecord::ConnectionAdapters::Column.value_to_boolean(value)
87
176
  else value
88
177
  end
89
178
  end
90
179
 
91
- def key?
92
- self.type == :key
93
- end
94
-
95
- def ignored?
96
- !!self.ignore
97
- end
98
180
 
99
- def auto_detect!
100
- case name.downcase
101
- when 'id'
102
- @type = :key if self.type == :integer
103
- when /(.*)_id/
104
- self.references = $1.to_s.pluralize unless self.references
105
- end
181
+ # runs auto detect (see {Mongify::Database::Column.auto_detect})
182
+ def run_auto_detect!
183
+ self.class.auto_detect(self) if auto_detect?
106
184
  end
107
185
 
108
186
 
@@ -0,0 +1,67 @@
1
+ module Mongify
2
+ module Database
3
+ # Class that will be used to allow user to modify data for the given row
4
+ class DataRow
5
+ def initialize(hash={})
6
+ @hash = hash.dup.stringify_keys!
7
+ end
8
+
9
+ # See if a given key is included
10
+ def include?(key)
11
+ @hash.has_key?(key)
12
+ end
13
+
14
+ # Deletes a given key from the object
15
+ def delete(key)
16
+ @hash.delete(key)
17
+ end
18
+
19
+ # Returns a list of available keys
20
+ def keys
21
+ @hash.keys
22
+ end
23
+
24
+ # Outputs an hash
25
+ # This is used to write into the no sql database
26
+ def to_hash
27
+ @hash
28
+ end
29
+
30
+ # Used to manually read an attribute
31
+ def read_attribute(key)
32
+ @hash[key.to_s]
33
+ end
34
+ # Used to manually write an attribute
35
+ def write_attribute(key, value)
36
+ @hash[key.to_s] = value
37
+ end
38
+
39
+ # Passes Inspect onto the internal hash
40
+ def inspect
41
+ @hash.inspect
42
+ end
43
+
44
+ # Updated respond_to to return true if it's a key the hash
45
+ def respond_to?(method)
46
+ return true if @hash.has_key?(method.gsub('=', ''))
47
+ super(method)
48
+ end
49
+
50
+ # Added the ability to read and write attributes in the hash
51
+ def method_missing(meth, *args, &blk)
52
+ match = meth.to_s.match(/^([a-zA-Z\_]+)(=|$)$/)
53
+ if match
54
+ attribute, setter = match[1], !match[2].blank?
55
+ if setter
56
+ write_attribute(attribute, args.first)
57
+ else
58
+ read_attribute(attribute)
59
+ end
60
+ else
61
+ super(meth, *args, &blk)
62
+ end
63
+ end
64
+
65
+ end
66
+ end
67
+ end
@@ -2,10 +2,28 @@ require 'mongo'
2
2
  module Mongify
3
3
  module Database
4
4
  #
5
- # No sql connection configuration
5
+ # No Sql Connection configuration
6
+ #
7
+ # Basic format should look something like this:
8
+ #
9
+ # no_sql_connection do
10
+ # adapter "mongodb"
11
+ # host "localhost"
12
+ # database "my_database"
13
+ # end
14
+ #
15
+ # Possible attributes:
16
+ # adapter
17
+ # host
18
+ # database
19
+ # username
20
+ # password
21
+ # port
6
22
  #
7
23
  class NoSqlConnection < Mongify::Database::BaseConnection
8
24
  include Mongo
25
+
26
+ #Required fields for a no sql connection
9
27
  REQUIRED_FIELDS = %w{host database}
10
28
 
11
29
  def initialize(options=nil)
@@ -13,57 +31,77 @@ module Mongify
13
31
  adapter 'mongodb' if adapter.nil? || adapter == 'mongo'
14
32
  end
15
33
 
34
+ # Sets and/or returns a adapter
35
+ # It takes care of renaming adapter('mongo') to 'mongodb'
36
+ def adapter(name=nil)
37
+ name = 'mongodb' if name && name.to_s.downcase == 'mongo'
38
+ super(name)
39
+ end
40
+
41
+ # Returns a connection string that can be used to build a Mongo Connection
42
+ # (Currently this isn't used due to some issue early on in development)
16
43
  def connection_string
17
44
  "#{@adapter}://#{@host}#{":#{@port}" if @port}"
18
45
  end
19
46
 
47
+ # Returns true or false depending if the given attributes are present and valid to make up a
48
+ # connection to a mongo server
20
49
  def valid?
21
- @database.present? && @host.present?
50
+ super && @database.present?
22
51
  end
23
52
 
53
+ # Sets up a connection to the database
54
+ def setup_connection_adapter
55
+ connection = Connection.new(host, port)
56
+ connection.add_auth(database, username, password) if username && password
57
+ connection
58
+ end
59
+
60
+ # Returns a mongo connection
24
61
  def connection
25
- return @connection if @connection
26
- @connection = Connection.new(host, port)
27
- @connection.add_auth(database, username, password) if username && password
28
- @connection
62
+ @connection ||= setup_connection_adapter
29
63
  end
30
64
 
65
+ # Returns true or false depending if we have a connection to a mongo server
31
66
  def has_connection?
32
67
  connection.connected?
33
68
  end
34
69
 
70
+ # Returns the database from the connection
35
71
  def db
36
72
  @db ||= connection[database]
37
73
  end
38
74
 
75
+ # Returns a hash of all the rows from the database of a given collection
39
76
  def select_rows(collection)
40
77
  db[collection].find
41
78
  end
42
79
 
80
+ # Inserts into the collection a given row
43
81
  def insert_into(colleciton_name, row)
44
82
  db[colleciton_name].insert(row, :safe => true)
45
83
  end
46
84
 
85
+ # Updates a collection item with a given ID with the given attributes
47
86
  def update(colleciton_name, id, attributes)
48
87
  db[colleciton_name].update({"_id" => id}, attributes)
49
88
  end
50
89
 
90
+ # Finds one item from a collection with the given query
51
91
  def find_one(collection_name, query)
52
92
  db[collection_name].find_one(query)
53
93
  end
54
94
 
95
+ # Returns a row of a item from a given collection with a given pre_mongified_id
55
96
  def get_id_using_pre_mongified_id(colleciton_name, pre_mongified_id)
56
97
  db[colleciton_name].find_one('pre_mongified_id' => pre_mongified_id).try(:[], '_id')
57
98
  end
58
99
 
100
+ # Removes pre_mongified_id from all records in a given collection
59
101
  def remove_pre_mongified_ids(collection_name)
60
102
  db[collection_name].update({}, { '$unset' => { 'pre_mongified_id' => 1} }, :multi => true)
61
103
  end
62
104
 
63
- def reset!
64
- @connection = nil
65
- @db = nil
66
- end
67
105
  end
68
106
  end
69
107
  end