rare_map 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,78 @@
1
+ require 'active_support/core_ext/hash'
2
+
3
+ module RareMap
4
+ # RareMap::Options defines all available options of RareMap.
5
+ # @author Wei-Ming Wu
6
+ # @!attribute [r] opts
7
+ # @return [Hash] the details of options
8
+ class Options
9
+ # A default group name
10
+ DEFAULT_GROUP = 'default'
11
+ attr_reader :opts
12
+
13
+ # Creates a Options.
14
+ #
15
+ # @param raw_opts [Hash] the details of options
16
+ # @return [Options] a Options object
17
+ def initialize(raw_opts = {})
18
+ raw_opts ||= {}
19
+ raw_opts = raw_opts.with_indifferent_access
20
+ @opts = { group: DEFAULT_GROUP,
21
+ primary_key: {},
22
+ foreign_key: { suffix: nil, alias: {} } }.with_indifferent_access
23
+
24
+ if raw_opts.kind_of? Hash
25
+ if raw_opts[:group]
26
+ @opts[:group] = raw_opts[:group]
27
+ end
28
+ if raw_opts[:primary_key].kind_of? Hash
29
+ @opts[:primary_key] = raw_opts[:primary_key].select { |k, v| k.kind_of? String and v.kind_of? String }
30
+ end
31
+ if raw_opts[:foreign_key].kind_of? Hash and raw_opts[:foreign_key][:suffix].kind_of? String
32
+ @opts[:foreign_key][:suffix] = raw_opts[:foreign_key][:suffix]
33
+ end
34
+ if raw_opts[:foreign_key].kind_of? Hash and raw_opts[:foreign_key][:alias].kind_of? Hash
35
+ @opts[:foreign_key][:alias] = raw_opts[:foreign_key][:alias].select { |k, v| k.kind_of? String and v.kind_of? String }
36
+ end
37
+ if raw_opts[:group].kind_of? String
38
+ @opts[:group] = raw_opts[:group]
39
+ end
40
+ end
41
+ end
42
+
43
+ # Checks if this Options belongs to a group.
44
+ #
45
+ # @return [true, false] true if this Options contains a group, false otherwise
46
+ def group?
47
+ @opts[:group] != DEFAULT_GROUP
48
+ end
49
+
50
+ # Returns the name of this Options' group
51
+ #
52
+ # @return [String] the name of this Options' group
53
+ def group
54
+ @opts[:group] || DEFAULT_GROUP
55
+ end
56
+
57
+ # Returns the primary key of a table specified by this Options
58
+ #
59
+ # @return [String, nil] the primary key of a table specified by this Options
60
+ def find_primary_key_by_table(table_name)
61
+ @opts[:primary_key].values_at(table_name).first
62
+ end
63
+
64
+ # Returns the table of a foreign key specified by this Options
65
+ #
66
+ # @return [String, nil] the table of a foreign key specified by this Options
67
+ def find_table_by_foreign_key(column_name)
68
+ @opts[:foreign_key][:alias].values_at(column_name).first
69
+ end
70
+
71
+ # Returns the suffix of a foreign key should have
72
+ #
73
+ # @return [String, nil] the suffix of a foreign key should have
74
+ def fk_suffix
75
+ @opts[:foreign_key][:suffix]
76
+ end
77
+ end
78
+ end
@@ -1,19 +1,27 @@
1
1
  module RareMap
2
+ # RareMap::RailsLocator locates the root of a rails application from any
3
+ # where inside it.
4
+ # @author Wei-Ming Wu
2
5
  module RailsLocator
3
- def locate_rails_root(depth = 5)
4
- rails_dirs = ['app', 'config', 'db', 'lib', 'log', 'public']
6
+ # Finds the root of a rails application.
7
+ #
8
+ # @param [String] path the location where start to search
9
+ # @param [Integer] depth the max levels of folders to search
10
+ # @return [String, nil] the root of a rails application
11
+ def locate_rails_root(path = '.', depth = 5)
12
+ rails_dirs = %w(app config db lib log public)
5
13
 
6
14
  depth.times do |level|
7
15
  found = true
8
- path = ''
16
+ paths = [path]
9
17
 
10
- level.times { path << '../' }
18
+ level.times { paths << '..' }
11
19
 
12
20
  rails_dirs.each do |dir|
13
- found = false unless Dir.exist?(path + dir)
21
+ found = false unless Dir.exist? File.join(*(paths + [dir]))
14
22
  end
15
23
 
16
- return path if found
24
+ return File.absolute_path(File.join(*paths)) if found
17
25
  end
18
26
 
19
27
  nil
@@ -0,0 +1,41 @@
1
+ require 'rare_map/errors'
2
+
3
+ module RareMap
4
+ # RareMap::Relation defines one of has_one, has_many and has_many_through
5
+ # relations of a database table.
6
+ # @author Wei-Ming Wu
7
+ # @!attribute [r] type
8
+ # @return [Symbol] the type of relation, `:has_one` or `:has_many` or `:has_many_through`
9
+ # @!attribute [r] foreign_key
10
+ # @return [String] the foreign key of this Relation based on
11
+ # @!attribute [r] table
12
+ # @return [String] the table of this Relation refers to
13
+ # @!attribute [r] through
14
+ # @return [String, nil] the table of this Relation goes through
15
+ class Relation
16
+ # The :has_one association.
17
+ HAS_ONE = :has_one
18
+ # The :belongs_to association.
19
+ BELONGS_TO = :belongs_to
20
+ # The :has_many association.
21
+ HAS_MANY = :has_many
22
+ # The :has_many_through association.
23
+ HAS_MANY_THROUGH = :has_many_through
24
+ # All three kinds of relations
25
+ RELATIONS = [HAS_ONE, BELONGS_TO, HAS_MANY, HAS_MANY_THROUGH]
26
+ include Errors
27
+ attr_reader :type, :foreign_key, :table, :through
28
+
29
+ # Creates a Relation.
30
+ #
31
+ # @param type [Symbol] the type, `:has_one` or `:has_many` or `:has_many_through`
32
+ # @param foreign_key [String] the foreign key of this Relation based on
33
+ # @param table [String] the table of this Relation refers to
34
+ # @param through [String, nil] the table of this Relation goes through
35
+ # @return [Relation] the Relation object
36
+ def initialize(type, foreign_key, table, through = nil)
37
+ raise RelationNotDefinedError, 'Relation type not defined.' unless RELATIONS.include? type
38
+ @type, @foreign_key, @table, @through = type, foreign_key, table, through
39
+ end
40
+ end
41
+ end
@@ -1,7 +1,13 @@
1
- require 'active_support/inflector'
1
+ require 'rare_map/table'
2
+ require 'rare_map/column'
2
3
 
3
4
  module RareMap
5
+ # RareMap::SchemaParser parses schema.rb into Table.
6
+ # @author Wei-Ming Wu
4
7
  module SchemaParser
8
+ # Parses schema.rb into an Array of Table.
9
+ #
10
+ # @return [Array] an Array of Table
5
11
  def parse_schema(schema)
6
12
  tables = []
7
13
 
@@ -27,74 +33,4 @@ module RareMap
27
33
  tables
28
34
  end
29
35
  end
30
-
31
- class Table
32
- attr_reader :name, :id, :columns
33
- attr_writer :primary_key
34
- attr_accessor :fk_suffix
35
-
36
- def initialize(name, opts = { :id => true, :primary_key => nil })
37
- @name = name
38
- @id = opts[:id]
39
- @primary_key = opts[:primary_key]
40
- @columns = []
41
- @fk_suffix = 'id'
42
- end
43
-
44
- def primary_key
45
- return @primary_key if @primary_key
46
- return 'id' if @id
47
-
48
- candidates = @columns.find_all { |col| col.unique }.map { |col| col.name }
49
- # return @primary_key if candidates.include? @primary_key
50
- return 'id' if candidates.include? 'id'
51
- candidates.find { |c| c =~ eval("/^#{@name}.*id$/") } ||
52
- candidates.find { |c| c =~ eval("/^#{singularize}.*id$/") } ||
53
- candidates.find { |c| c =~ eval("/^#{pluralize}.*id$/") } ||
54
- candidates.first
55
- end
56
-
57
- def singularize
58
- @name.pluralize.singularize
59
- end
60
-
61
- def pluralize
62
- @name.pluralize
63
- end
64
-
65
- def match_foreign_key(column)
66
- if column.references == @name || foreign_keys.include?(column.name)
67
- @name if primary_key
68
- end
69
- end
70
-
71
- def match_foreign_key_by_primary_key(pk)
72
- @name if foreign_keys.include?(pk) && primary_key
73
- end
74
-
75
- private
76
- def foreign_keys
77
- ["#{@name}_#{fk_suffix}", "#{@name}#{fk_suffix}", "#{singularize}_#{fk_suffix}",
78
- "#{singularize}#{fk_suffix}", "#{pluralize}_#{fk_suffix}", "#{pluralize}#{fk_suffix}"]
79
- end
80
- end
81
-
82
- class Column
83
- attr_reader :name, :type
84
- attr_accessor :unique, :references
85
-
86
- def initialize(name, type)
87
- @name = name
88
- @type = type
89
- @unique = false
90
- end
91
-
92
- def unique?
93
- @unique
94
- end
95
-
96
- def foreign_key?
97
- @references ? true : false
98
- end
99
- end
100
36
  end
@@ -2,7 +2,12 @@ require 'active_record'
2
2
  require 'activerecord-jdbc-adapter' if RUBY_PLATFORM == 'java'
3
3
 
4
4
  module RareMap
5
+ # RareMap::SchemaReader dumps database schema by using ActiveRecord::SchemaDumper.
6
+ # @author Wei-Ming Wu
5
7
  module SchemaReader
8
+ # Returns the content of schema.rb which is created by ActiveRecord::SchemaDumper.
9
+ #
10
+ # @return [String] the content of schema.rb which is created by ActiveRecord::SchemaDumper
6
11
  def read_schema(db_profile)
7
12
  conn = db_profile.connection.map { |k, v| v.kind_of?(Integer) ? "'#{k}'=>#{v}" : "'#{k}'=>'#{v}'" }.join(', ')
8
13
  schema = if RUBY_PLATFORM == 'java'
@@ -21,6 +26,8 @@ module RareMap
21
26
  =end
22
27
  end
23
28
 
29
+ private
30
+
24
31
  def detect_errors(schema)
25
32
  if $? != 0
26
33
  puts schema
@@ -0,0 +1,89 @@
1
+ require 'active_support/inflector'
2
+
3
+ module RareMap
4
+ # RareMap::Table defines a table of a database.
5
+ # @author Wei-Ming Wu
6
+ # @!attribute [r] name
7
+ # @return [String] the name of this Table
8
+ # @!attribute [r] id
9
+ # @return [true, false] true if this Table has id, false otherwise
10
+ # @!attribute primary_key
11
+ # @return [String] the primary key of this Table
12
+ # @!attribute fk_suffix
13
+ # @return [String] the foreign key suffix of this Table
14
+ # @!attribute columns
15
+ # @return [Array] an Array of Column of this Table
16
+ class Table
17
+ attr_reader :name, :id
18
+ attr_writer :primary_key
19
+ attr_accessor :fk_suffix, :columns
20
+
21
+ # Creates a Table.
22
+ #
23
+ # @param name [String] the name of this Table
24
+ # @param opts [Hash] the options of this Table
25
+ # @option opts [true, false] :id the id existence of this Table
26
+ # @option opts [String] :primary_key the primary key of this Table
27
+ # @option opts [String] :fk_suffix the foreign key suffix of this Table
28
+ # @return [Table] a Table object
29
+ def initialize(name, opts = {})
30
+ @name = name
31
+ @id = opts[:id] != false
32
+ @primary_key = opts[:primary_key]
33
+ @columns = []
34
+ @fk_suffix = opts[:fk_suffix] || 'id'
35
+ end
36
+
37
+ # Returns the primary key of this Table.
38
+ #
39
+ # @return [String, nil] the primary key of this Table
40
+ def primary_key
41
+ return @primary_key if @primary_key
42
+ return 'id' if @id
43
+
44
+ candidates = @columns.find_all { |col| col.unique }.map { |col| col.name }
45
+ return 'id' if candidates.include? 'id'
46
+ candidates.find { |c| c =~ eval("/^#{@name}.*id$/") } ||
47
+ candidates.find { |c| c =~ eval("/^#{singularize}.*id$/") } ||
48
+ candidates.find { |c| c =~ eval("/^#{pluralize}.*id$/") } ||
49
+ candidates.first
50
+ end
51
+
52
+ # Returns the singular name of this Table.
53
+ #
54
+ # @return [String] the singular name of this Table
55
+ def singularize
56
+ @name.pluralize.singularize
57
+ end
58
+
59
+ # Returns the plural name of this Table.
60
+ #
61
+ # @return [String] the plural name of this Table
62
+ def pluralize
63
+ @name.pluralize
64
+ end
65
+
66
+ # Returns the name of this Table if given Column matched.
67
+ #
68
+ # @return [String, nil] the name of this Table if given Column matched, nil otherwise
69
+ def match_foreign_key(column)
70
+ if column.ref_table == @name || foreign_keys.include?(column.name)
71
+ @name if primary_key
72
+ end
73
+ end
74
+
75
+ # Returns the name of this Table if given primary key matched.
76
+ #
77
+ # @return [String, nil] the name of this Table if given primary key matched, nil otherwise
78
+ def match_foreign_key_by_primary_key(pk)
79
+ @name if foreign_keys.include?(pk) && primary_key
80
+ end
81
+
82
+ private
83
+
84
+ def foreign_keys
85
+ ["#{@name}_#{fk_suffix}", "#{@name}#{fk_suffix}", "#{singularize}_#{fk_suffix}",
86
+ "#{singularize}#{fk_suffix}", "#{pluralize}_#{fk_suffix}", "#{pluralize}#{fk_suffix}"]
87
+ end
88
+ end
89
+ end
@@ -1,7 +1,11 @@
1
1
  module RareMap
2
+ # The major version of RareMap
2
3
  MAJOR = 2
3
- MINOR = 1
4
- PATCH = 1
5
-
4
+ # The minor version of RareMap
5
+ MINOR = 2
6
+ # The patch version of RareMap
7
+ PATCH = 0
8
+
9
+ # The version of RareMap
6
10
  VERSION = [MAJOR, MINOR, PATCH].compact.join('.')
7
11
  end
@@ -0,0 +1,29 @@
1
+ require 'test_helper'
2
+ require 'rare_map/column'
3
+
4
+ class ColumnTest < Test::Unit::TestCase
5
+ def setup
6
+ @column = RareMap::Column.new 'col1', 'integer'
7
+ end
8
+
9
+ def test_constructor
10
+ assert @column.kind_of? RareMap::Column
11
+ end
12
+
13
+ def test_properties
14
+ assert_equal 'col1', @column.name
15
+ assert_equal 'integer', @column.type
16
+ end
17
+
18
+ def test_unique?
19
+ assert_equal false, @column.unique?
20
+ @column.unique = true
21
+ assert @column.unique?
22
+ end
23
+
24
+ def test_foreign_key?
25
+ assert_equal false, @column.foreign_key?
26
+ @column.ref_table = 'table1'
27
+ assert @column.foreign_key?
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ require 'test_helper'
2
+ require 'rare_map/config_loader'
3
+ require 'yaml'
4
+
5
+ class ConfigLoaderTest < Test::Unit::TestCase
6
+ include RareMap::ConfigLoader
7
+ include RareMap::Errors
8
+
9
+ def test_load_config
10
+ db_profiles = load_config File.dirname(__FILE__)
11
+ assert_equal 1, db_profiles.size
12
+ db_profile = db_profiles.first
13
+ assert db_profile.kind_of? RareMap::DatabaseProfile
14
+ assert_equal 'main', db_profile.name
15
+ assert_equal YAML.load_file(File.join(File.dirname(__FILE__), 'rare_map.yml'))['sample'][1]['main'], db_profile.connection
16
+ assert_equal 'sample', db_profile.options.group
17
+ end
18
+
19
+ def test_load_config_simple
20
+ db_profiles = load_config File.dirname(__FILE__), 'rare_map_simple.yml'
21
+ assert_equal 1, db_profiles.size
22
+ db_profile = db_profiles.first
23
+ assert db_profile.kind_of? RareMap::DatabaseProfile
24
+ assert_equal 'main', db_profile.name
25
+ assert_equal YAML.load_file(File.join(File.dirname(__FILE__), 'rare_map_simple.yml'))['main'], db_profile.connection
26
+ assert_equal 'default', db_profile.options.group
27
+ end
28
+
29
+ def test_load_config_error
30
+ assert_raise ConfigNotFoundError do
31
+ load_config 'no_file'
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,25 @@
1
+ require 'test_helper'
2
+ require 'rare_map/database_profile'
3
+ require 'rare_map/options'
4
+
5
+ class DatabaseProfileTest < Test::Unit::TestCase
6
+ def setup
7
+ @connection = { adapter: 'sqlite3', database: 'db/test.sqlite3' }
8
+ @options = RareMap::Options.new
9
+ @db_profile = RareMap::DatabaseProfile.new 'profile1', @connection, @options
10
+ end
11
+
12
+ def test_constructor
13
+ assert @db_profile.kind_of? RareMap::DatabaseProfile
14
+ end
15
+
16
+ def test_properties
17
+ assert_equal 'profile1', @db_profile.name
18
+ assert_equal @connection, @db_profile.connection
19
+ assert_equal @options, @db_profile.options
20
+ assert_equal nil, @db_profile.schema
21
+ @db_profile.schema = ''
22
+ assert_equal '', @db_profile.schema
23
+ assert_equal [], @db_profile.tables
24
+ end
25
+ end