partitioned 0.8.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,77 +1,56 @@
1
1
  module Partitioned
2
+ #
3
+ # MixIn used to extend ActiveRecord::Base classes implementing bulk insert and update operations
4
+ # through {#create_many} and {#update_many}. {Partitioned::PartitionedBase} classes are extended
5
+ # with these methods by default.
6
+ #
7
+ # @example to use in non-{Partitioned::PartitionedBase} class:
8
+ # class Company < ActiveRecord::Base
9
+ # extend Partitioned::BulkMethodsMixin
10
+ # end
11
+ #
2
12
  module BulkMethodsMixin
13
+ # exception thrown when row data structures are inconsistent between rows in single call to {#create_many} or {#update_many}
3
14
  class BulkUploadDataInconsistent < StandardError
4
15
  def initialize(model, table_name, expected_columns, found_columns, while_doing)
5
16
  super("#{model.name}: for table: #{table_name}; #{expected_columns} != #{found_columns}; #{while_doing}")
6
17
  end
7
18
  end
8
- #
19
+
9
20
  # BULK creation of many rows
10
21
  #
11
- # rows: an array of hashtables of data to insert into the database
12
- # each hashtable must have the same number of keys (and same
13
- # names for each key).
14
- #
15
- # options:
16
- # :slice_size = 1000
17
- # :returning = nil
18
- # :check_consistency = true
19
- #
20
- # examples:
21
- # first example didn't uses more options.
22
- #
23
- # rows = [{
24
- # :name => 'Keith',
25
- # :salary => 1000,
26
- # },
27
- # {
28
- # :name => 'Alex',
29
- # :salary => 2000,
30
- # }]
31
- #
32
- # Employee.create_many(rows)
33
- #
34
- # this second example uses :returning option
35
- # to returns key values
36
- #
37
- # rows = [{
38
- # :name => 'Keith',
39
- # :salary => 1000,
40
- # },
41
- # {
42
- # :name => 'Alex',
43
- # :salary => 2000,
44
- # }]
45
- #
46
- # options = {
47
- # :returning => [:id]
48
- # }
49
- #
50
- # Employee.create_many(rows, options) returns [#<Employee id: 1>, #<Employee id: 2>]
51
- #
52
- # third example uses :slice_size option.
53
- # Slice_size - is an integer that specifies how many
54
- # records will be created in a single SQL query.
55
- #
56
- # rows = [{
57
- # :name => 'Keith',
58
- # :salary => 1000,
59
- # },
60
- # {
61
- # :name => 'Alex',
62
- # :salary => 2000,
63
- # },
64
- # {
65
- # :name => 'Mark',
66
- # :salary => 3000,
67
- # }]
68
- #
69
- # options = {
70
- # :slice_size => 2
71
- # }
72
- #
73
- # Employee.create_many(rows, options) will generate two insert queries
74
- #
22
+ # @example no options used
23
+ # rows = [
24
+ # { :name => 'Keith', :salary => 1000 },
25
+ # { :name => 'Alex', :salary => 2000 }
26
+ # ]
27
+ # Employee.create_many(rows)
28
+ #
29
+ # @example with :returning option to returns key value
30
+ # rows = [
31
+ # { :name => 'Keith', :salary => 1000 },
32
+ # { :name => 'Alex', :salary => 2000 }
33
+ # ]
34
+ # options = { :returning => [:id] }
35
+ # Employee.create_many(rows, options)
36
+ # [#<Employee id: 1>, #<Employee id: 2>]
37
+ #
38
+ # @example with :slice_size option (will generate two insert queries)
39
+ # rows = [
40
+ # { :name => 'Keith', :salary => 1000 },
41
+ # { :name => 'Alex', :salary => 2000 },
42
+ # { :name => 'Mark', :salary => 3000 }
43
+ # ]
44
+ # options = { :slice_size => 2 }
45
+ # Employee.create_many(rows, options)
46
+ #
47
+ # @param [Array<Hash>] rows ([]) data to be inserted into database
48
+ # @param [Hash] options ({}) options for bulk inserts
49
+ # @option options [Integer] :slice_size (1000) how many records will be created in a single SQL query
50
+ # @option options [Boolean] :check_consitency (true) ensure some modicum of sanity on the incoming dataset, specifically: does each row define the same set of key/value pairs
51
+ # @option options [Array or String] :returning (nil) list of fields to return.
52
+ # @return [Array<Hash>] rows returned from DB as option[:returning] requests
53
+ # @raise [BulkUploadDataInconsistent] raised when key/value pairs between rows are inconsistent (check disabled with option :check_consistency)
75
54
  def create_many(rows, options = {})
76
55
  return [] if rows.blank?
77
56
  options[:slice_size] = 1000 unless options.has_key?(:slice_size)
@@ -127,90 +106,56 @@ module Partitioned
127
106
  #
128
107
  # BULK updates of many rows
129
108
  #
130
- # rows: an array of hashtables of data to insert into the database
131
- # each hashtable must have the same number of keys (and same
132
- # names for each key).
133
- #
134
- # options:
135
- # :slice_size = 1000
136
- # :returning = nil
137
- # :set_array = from first row passed in
138
- # :check_consistency = true
139
- # :where = '"#{table_name}.id = datatable.id"'
140
- #
141
- # examples:
142
- # this first example uses "set_array" to add the value of "salary"
143
- # to the specific employee's salary
144
- # the default where clause is to match IDs so, it works here.
145
- # rows = [{
146
- # :id => 1,
147
- # :salary => 1000,
148
- # },
149
- # {
150
- # :id => 10,
151
- # :salary => 2000,
152
- # },
153
- # {
154
- # :id => 23,
155
- # :salary => 2500,
156
- # }]
157
- #
158
- # options = {
159
- # :set_array => '"salary = datatable.salary"'
160
- # }
161
- #
162
- # Employee.update_many(rows, options)
163
- #
164
- #
165
- # this versions sets the where clause to match Salaries.
166
- # rows = [{
167
- # :id => 1,
168
- # :salary => 1000,
169
- # :company_id => 10
170
- # },
171
- # {
172
- # :id => 10,
173
- # :salary => 2000,
174
- # :company_id => 12
175
- # },
176
- # {
177
- # :id => 23,
178
- # :salary => 2500,
179
- # :company_id => 5
180
- # }]
181
- #
182
- # options = {
183
- # :set_array => '"company_id = datatable.company_id"',
184
- # :where => '"#{table_name}.salary = datatable.salary"'
185
- # }
186
- #
187
- # Employee.update_many(rows, options)
188
- #
189
- #
190
- # this version sets the where clause to the KEY of the hash passed in
191
- # and the set_array is generated from the VALUES
192
- #
193
- # rows = {
194
- # { :id => 1 } => {
195
- # :salary => 100000,
196
- # :company_id => 10
197
- # },
198
- # { :id => 10 } => {
199
- # :salary => 110000,
200
- # :company_id => 12
201
- # },
202
- # { :id => 23 } => {
203
- # :salary => 90000,
204
- # :company_id => 5
109
+ # @return [Array<Hash>] rows returned from DB as option[:returning] requests
110
+ # @raise [BulkUploadDataInconsistent] raised when key/value pairs between rows are inconsistent (check disabled with option :check_consistency)
111
+ # @param [Hash] options ({}) options for bulk inserts
112
+ # @option options [Integer] :slice_size (1000) how many records will be created in a single SQL query
113
+ # @option options [Boolean] :check_consitency (true) ensure some modicum of sanity on the incoming dataset, specifically: does each row define the same set of key/value pairs
114
+ # @option options [Array] :returning (nil) list of fields to return.
115
+ # @option options [String] :returning (nil) single field to return.
116
+ #
117
+ # @overload update_many(rows = [], options = {})
118
+ # @param [Array<Hash>] rows ([]) data to be updated
119
+ # @option options [String] :set_array (built from first row passed in) the set clause
120
+ # @option options [String] :where ('"#{table_name}.id = datatable.id"') the where clause
121
+ #
122
+ # @overload update_many(rows = {}, options = {})
123
+ # @param [Hash<Hash, Hash>] rows ({}) data to be updated
124
+ # @option options [String] :set_array (built from the values in the first key/value pair of `rows`) the set clause
125
+ # @option options [String] :where (built from the keys in the first key/value pair of `rows`) the where clause
126
+ #
127
+ # @example using "set_array" to add the value of "salary" to the specific employee's salary the default where clause matches IDs so, it works here.
128
+ # rows = [
129
+ # { :id => 1, :salary => 1000 },
130
+ # { :id => 10, :salary => 2000 },
131
+ # { :id => 23, :salary => 2500 }
132
+ # ]
133
+ # options = { :set_array => '"salary = datatable.salary"' }
134
+ # Employee.update_many(rows, options)
135
+ #
136
+ # @example using where clause to match salary.
137
+ # rows = [
138
+ # { :id => 1, :salary => 1000, :company_id => 10 },
139
+ # { :id => 10, :salary => 2000, :company_id => 12 },
140
+ # { :id => 23, :salary => 2500, :company_id => 5 }
141
+ # ]
142
+ # options = {
143
+ # :set_array => '"company_id = datatable.company_id"',
144
+ # :where => '"#{table_name}.salary = datatable.salary"'
205
145
  # }
206
- # }
146
+ # Employee.update_many(rows, options)
207
147
  #
208
- # Employee.update_many(rows)
148
+ # @example setting where clause to the KEY of the hash passed in and the set_array is generated from the VALUES
149
+ # rows = {
150
+ # { :id => 1 } => { :salary => 100000, :company_id => 10 },
151
+ # { :id => 10 } => { :salary => 110000, :company_id => 12 },
152
+ # { :id => 23 } => { :salary => 90000, :company_id => 5 }
153
+ # }
154
+ # Employee.update_many(rows)
209
155
  #
210
- # Remember that you should probably set updated_at using "updated = datatable.updated_at"
211
- # or "updated_at = now()" in the set_array if you want to follow
212
- # the standard active record model for time columns (and you have an updated_at column)
213
-
156
+ # @note Remember that you should probably set updated_at using "updated = datatable.updated_at"
157
+ # or "updated_at = now()" in the set_array if you want to follow
158
+ # the standard active record model for time columns (and you have an updated_at column)
214
159
  def update_many(rows, options = {})
215
160
  return [] if rows.blank?
216
161
  if rows.is_a?(Hash)
@@ -1,11 +1,13 @@
1
1
  module Partitioned
2
2
  #
3
- # partition tables by created_at grouping them by week, with
3
+ # Partition tables by created_at grouping them by week, with
4
4
  # a week defined as seven days starting on Monday.
5
5
  #
6
6
  class ByCreatedAt < ByWeeklyTimeField
7
7
  self.abstract_class = true
8
8
 
9
+ # the field to partition on, `created_at`
10
+ # @return [Symbol] the partition field: `created_at`
9
11
  def self.partition_time_field
10
12
  return :created_at
11
13
  end
@@ -1,11 +1,16 @@
1
1
  module Partitioned
2
+ # Partitioned abstract class for all partitioned models based as a single integer field value that is used as a foreign key
2
3
  class ByForeignKey < ByIntegerField
3
4
  self.abstract_class = true
4
5
 
6
+ # the field to partition on
7
+ # @return [Integer] re-routed to {#self.partition_foreign_key}
5
8
  def self.partition_integer_field
6
9
  return partition_foreign_key
7
10
  end
8
11
 
12
+ # the field to partition on
13
+ # @return [String] the name of the foreign key field
9
14
  def self.partition_foreign_key
10
15
  raise MethodNotImplemented.new(self, :partition_foreign_key)
11
16
  end
@@ -1,7 +1,7 @@
1
1
  module Partitioned
2
2
  #
3
- # table partitioning by id. this partitioning breaks up data by
4
- # the value of its primary key. a specific record's child table
3
+ # Table partitioning by id. this partitioning breaks up data by
4
+ # the value of its primary key. A specific record's child table
5
5
  # is determined by the number resulting from the integer math:
6
6
  # ID / ById::partition_table_size * ById::partition_table_size
7
7
  #
@@ -9,21 +9,27 @@ module Partitioned
9
9
  self.abstract_class = true
10
10
 
11
11
  #
12
- # specific to this partitioning, we need to prefetch the primary key (id)
12
+ # Specific to this partitioning, we need to prefetch the primary key (id)
13
13
  # before we attempt to do the insert because the insert wants to know the
14
14
  # name of the specific child table to access.
15
15
  #
16
+ # @return [Boolean] true
16
17
  def self.prefetch_primary_key?
17
18
  return true
18
19
  end
19
20
 
20
21
  #
21
- # the number of records in each child table.
22
+ # The number of records in each child table.
22
23
  #
24
+ # @return [Integer] the number of rows in a partition
23
25
  def self.partition_table_size
24
26
  return 10000000
25
27
  end
26
28
 
29
+ #
30
+ # The name of the field to partition on
31
+ #
32
+ # @return [String] the name of the field to partition on
27
33
  def self.partition_integer_field
28
34
  return :id
29
35
  end
@@ -1,15 +1,24 @@
1
1
  module Partitioned
2
+ #
3
+ # Partitioned abstract class for all partitioned models based as a single integer field value.
4
+ #
2
5
  class ByIntegerField < PartitionedBase
3
6
  self.abstract_class = true
4
7
 
8
+ # the size of each table
9
+ # @return [Integer] how many different values are in each partition
5
10
  def self.partition_table_size
6
11
  return 1
7
12
  end
8
13
 
14
+ # the name of the partition key field
15
+ # @return [String] the name of the field
9
16
  def self.partition_integer_field
10
17
  raise MethodNotImplemented.new(self, :partition_integer_field)
11
18
  end
12
19
 
20
+ # the normalized key value for a given key value
21
+ # @return [Integer] the normalized value
13
22
  def self.partition_normalize_key_value(integer_field_value)
14
23
  return integer_field_value / partition_table_size * partition_table_size
15
24
  end
@@ -1,15 +1,22 @@
1
1
  module Partitioned
2
2
  #
3
- # partition tables by a time field grouping them by week, with
3
+ # Partition tables by a time field grouping them by week, with
4
4
  # a week defined as seven days starting on Monday.
5
5
  #
6
6
  class ByMonthlyTimeField < ByTimeField
7
7
  self.abstract_class = true
8
8
 
9
+ # Normalize a partition key value by month.
10
+ #
11
+ # @param [Time] time_value the time value to normalize
12
+ # @return [Time] the value normalized
9
13
  def self.partition_normalize_key_value(time_value)
10
14
  return time_value.at_beginning_of_month
11
15
  end
12
16
 
17
+ # The size of the partition table, a month
18
+ #
19
+ # @return [Integer] the size of this partition
13
20
  def self.partition_table_size
14
21
  return 1.month
15
22
  end
@@ -1,16 +1,20 @@
1
1
  module Partitioned
2
2
  #
3
- # partition tables by a time field grouping them by day
3
+ # Partition tables by a time field grouping them by day.
4
4
  #
5
5
  class ByTimeField < PartitionedBase
6
6
  self.abstract_class = true
7
7
 
8
8
  #
9
- # generate an enumerable that represents all the dates between
10
- # start_date and end_date skipping step
9
+ # Generate an enumerable that represents all the dates between
10
+ # start_date and end_date skipping step.
11
11
  #
12
- # this can be used to calls that take an enumerable like create_infrastructure
12
+ # This can be used to calls that take an enumerable like create_infrastructure.
13
13
  #
14
+ # @param [Date] start_date the first date to generate the range from
15
+ # @param [Date] end_date the last date to generate the range from
16
+ # @param [Object] step (:default) number of values to advance (:default means use {#self.partition_table_size}).
17
+ # @return [Enumerable] the range generated
14
18
  def self.partition_generate_range(start_date, end_date, step = :default)
15
19
  step = partition_table_size if step == :default
16
20
  current_date = partition_normalize_key_value(start_date)
@@ -23,23 +27,27 @@ module Partitioned
23
27
  end
24
28
 
25
29
  #
26
- # normalize the value to the current day
30
+ # Normalize the value to the current day.
27
31
  #
32
+ # @param [Time] time_value the partitioned key value
33
+ # @return [Time] time_value normalized
28
34
  def self.partition_normalize_key_value(time_value)
29
35
  return time_value.to_date
30
36
  end
31
37
 
32
38
  #
33
- # the size of the partition, 1.day
39
+ # The size of the partition, 1.day
34
40
  #
41
+ # @return [Integer] the size of the partition
35
42
  def self.partition_table_size
36
43
  return 1.day
37
44
  end
38
45
 
39
46
  #
40
- # abstract -- implement in a derived clas.
41
- # the name of the time-related field we will use to partition child tables
47
+ # Abstract -- implement in a derived class.
48
+ # The name of the time-related field we will use to partition child tables.
42
49
  #
50
+ # @raise MethodNotImplemented
43
51
  def self.partition_time_field
44
52
  raise MethodNotImplemented.new(self, :partition_time_field)
45
53
  end
@@ -1,15 +1,17 @@
1
1
  module Partitioned
2
2
  #
3
- # partition tables by a time field grouping them by week, with
3
+ # Partition tables by a time field grouping them by week, with
4
4
  # a week defined as seven days starting on Monday.
5
5
  #
6
6
  class ByWeeklyTimeField < ByTimeField
7
7
  self.abstract_class = true
8
8
 
9
9
  #
10
- # normalize a partition key value by week. We've picked
11
- # the begining of the week to key on, which is Monday.
10
+ # Normalize a partition key value by week. We've picked
11
+ # the beginning of the week to key on, which is Monday.
12
12
  #
13
+ # @param [Time] time_value the time value to normalize
14
+ # @return [Time] the value normalized
13
15
  def self.partition_normalize_key_value(time_value)
14
16
  return time_value.at_beginning_of_week
15
17
  end
@@ -17,6 +19,7 @@ module Partitioned
17
19
  #
18
20
  # The size of the partition table, 7 days (1.week)
19
21
  #
22
+ # @return [Integer] the size of this partition
20
23
  def self.partition_table_size
21
24
  return 1.week
22
25
  end
@@ -1,6 +1,7 @@
1
1
  module Partitioned
2
2
  class MultiLevel
3
3
  module Configurator
4
+ # partitioning configuration information
4
5
  class Data < Partitioned::PartitionedBase::Configurator::Data
5
6
  attr_accessor :using_classes
6
7
 
@@ -1,7 +1,15 @@
1
1
  module Partitioned
2
2
  class MultiLevel
3
+ #
4
+ # Configuration manager for multi-level partitioning
5
+ # it supports, the front-end UI (a Domain Specific Language) using {Dsl}
6
+ # state using {Data}
7
+ # and a parser using {Reader}
8
+ #
3
9
  module Configurator
10
+ # The Domain Specific Language UI manager for multi level partitioning classes
4
11
  class Dsl < Partitioned::PartitionedBase::Configurator::Dsl
12
+ # used to prevent use of invalid directives
5
13
  class InvalidForMultiLevelPartitioning < StandardError
6
14
  def initialize(model, dsl_key, remedy)
7
15
  super("#{model.name}: '#{dsl_key}' is not valid for multi-level partitioning. #{remedy}")
@@ -18,10 +26,13 @@ module Partitioned
18
26
  #
19
27
  # Definition of classes which will be used at multi level partitioning.
20
28
  #
29
+ # @param [*Array<Class>] classes the classes, in order, used to partition this model
30
+ # @return [optional]
21
31
  def using_classes(*classes)
22
32
  data.using_classes += [*classes]
23
33
  end
24
34
 
35
+ # @raise [InvalidForMultiLevelPartitioning] always raised. `on` is not a valid DSL directive for multi-level partitioning
25
36
  def on(*ignored)
26
37
  raise InvalidForMultiLevelPartitioning.new(model, :on, "the partitioned keyword 'using' is used to define multi-level partitioned tables.")
27
38
  end
@@ -1,8 +1,13 @@
1
1
  module Partitioned
2
2
  class MultiLevel
3
3
  module Configurator
4
+ # coalesces and parses all {Data} objects allowing the
5
+ # {PartitionManager} to request partitioning information froma
6
+ # centralized source from multi level partitioned models
4
7
  class Reader < Partitioned::PartitionedBase::Configurator::Reader
8
+ # configurator for a specific class level
5
9
  UsingConfigurator = Struct.new(:model, :sliced_class, :dsl)
10
+
6
11
  def initialize(most_derived_activerecord_class)
7
12
  super
8
13
  @using_classes = nil
@@ -12,6 +17,7 @@ module Partitioned
12
17
  #
13
18
  # The field used to partition child tables.
14
19
  #
20
+ # @return [Array<Symbol>] fields used to partition this model
15
21
  def on_fields
16
22
  unless @on_fields
17
23
  @on_fields = using_collect(&:on_field).map(&:to_sym)
@@ -72,10 +78,22 @@ module Partitioned
72
78
  return parts.join('_')
73
79
  end
74
80
 
81
+ # retrieve a specific configurator from an ordered list. for multi-level partitioning
82
+ # we need to find the specific configurator for the partitioning level we are interested
83
+ # in managing.
84
+ #
85
+ # @param [Integer] index the partitioning level to query
86
+ # @return [Configurator] the configurator for the specific level queried
75
87
  def using_configurator(index)
76
88
  return using_class(index).configurator
77
89
  end
78
90
 
91
+ # retrieve a specific partitioning class from an ordered list. for multi-level partitioning
92
+ # we need to find the specific {Partitioned::PartitionedBase} class for the partitioning level we are interested
93
+ # in managing.
94
+ #
95
+ # @param [Integer] index the partitioning level to query
96
+ # @return [{Partitioned::PartitionedBase}] the class for the specific level queried
79
97
  def using_class(index)
80
98
  return using_classes[index]
81
99
  end
@@ -1,10 +1,15 @@
1
1
  module Partitioned
2
2
  class MultiLevel
3
+ #
4
+ # the manger of partitioned requests for models partitioned multiple times
5
+ #
3
6
  class PartitionManager < Partitioned::PartitionedBase::PartitionManager
4
7
  #
5
- # the once called function to prepare a parent table for partitioning as well
8
+ # The once called function to prepare a parent table for partitioning as well
6
9
  # as create the schema that the child tables will be placed in.
7
10
  #
11
+ # @param [Enumerable] enumerable (Array<Array>) the key values that should be used to create the parent partition tables.
12
+ # @return [optional]
8
13
  def create_infrastructure(enumerable = [[]])
9
14
  super()
10
15
  enumerable.each do |*partition_key_values|
@@ -15,11 +20,13 @@ module Partitioned
15
20
  protected
16
21
 
17
22
  #
18
- # create a specific child table that does not currently
23
+ # Create a specific child table that does not currently
19
24
  # exist and whose schema (the schema that the table exists in)
20
25
  # also already exists (#create_infrastructure is designed to
21
26
  # create this).
22
27
  #
28
+ # @param [*Array<Object>] partition_key_values all key values needed to create a partition
29
+ # @return [optional]
23
30
  def create_new_partition(*partition_key_values)
24
31
  create_partition_table(*partition_key_values)
25
32
  if is_leaf_partition?(*partition_key_values)
@@ -31,14 +38,16 @@ module Partitioned
31
38
  end
32
39
 
33
40
  #
34
- # is the table a child table without itself having any children.
41
+ # Is the table a child table without itself having any children.
35
42
  # generally leaf tables are where all indexes and foreign key
36
43
  # constraints will be placed because that is where the data will be.
37
44
  #
38
45
  # Non leaf tables will typically have a rule placed on them
39
- # (via add_parent_table_rules) that prevents any inserts from occuring
46
+ # (via add_parent_table_rules) that prevents any inserts from occurring
40
47
  # on them.
41
48
  #
49
+ # @param [*Array<Object>] partition_key_values all key values specifying a given child table
50
+ # @return [Boolean] true if this partition should contain records
42
51
  def is_leaf_partition?(*partition_key_values)
43
52
  return partition_key_values.length == parent_table_class.configurator.on_fields.length
44
53
  end
@@ -1,6 +1,6 @@
1
1
  module Partitioned
2
2
  #
3
- # table partitioning by a referenced id column which itself is partitioned
3
+ # Table partitioning by a referenced id column which itself is partitioned
4
4
  # further weekly by a date column.
5
5
  #
6
6
  class MultiLevel < PartitionedBase
@@ -9,6 +9,8 @@ module Partitioned
9
9
  #
10
10
  # Normalize the values for the each of using class.
11
11
  #
12
+ # @param [Array<Object>] value the partition key values
13
+ # @return [Array<Object>] the normalized values for the key values passed in
12
14
  def self.partition_normalize_key_value(values)
13
15
  normalized_values = []
14
16
  [*values].each_with_index do |value,index|
@@ -2,9 +2,10 @@ module Partitioned
2
2
  class PartitionedBase
3
3
  module Configurator
4
4
  #
5
- # the state configured by the Dsl and read by Reader
5
+ # The state configured by the Dsl and read by Reader.
6
6
  #
7
7
  class Data
8
+ # represents a SQL index
8
9
  class Index
9
10
  attr_accessor :field, :options
10
11
  def initialize(field, options = {})
@@ -12,6 +13,7 @@ module Partitioned
12
13
  @options = options
13
14
  end
14
15
  end
16
+ # represents a SQL foreign key reference
15
17
  class ForeignKey
16
18
  attr_accessor :referencing_field, :referenced_table, :referenced_field
17
19
  def initialize(referencing_field, referenced_table = nil, referenced_field = :id)
@@ -24,6 +26,13 @@ module Partitioned
24
26
  @referenced_field = referenced_field
25
27
  end
26
28
 
29
+ #
30
+ # Produce a table name from the name of the foreign key. in rails, this really
31
+ # means "foo_id" should be mapped to "foos", and "company_id" should be mapped to
32
+ # "companies"
33
+ #
34
+ # @param [String] foreign_key_field the name of the foreign key field
35
+ # @return [String] the name of the table associated with the foreign key
27
36
  def self.foreign_key_to_foreign_table_name(foreign_key_field)
28
37
  return ActiveSupport::Inflector::pluralize(foreign_key_field.to_s.sub(/_id$/,''))
29
38
  end