rare_map 2.1.1 → 2.2.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.
@@ -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