occams-record 0.19.0 → 0.20.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4835c655a7c4f9e68b567bc5727d9d99c13e638bb457a7615bb5ee598ae0499b
4
- data.tar.gz: 7a3ec8be97cbcb06f1b74bfd5e55e163b2fd6bacd163764551581ccdee92f16f
3
+ metadata.gz: 1731e8d0d0a676f2f94e5b019795eb3a1d5560be4e66f1d38df73cb876946de0
4
+ data.tar.gz: 92af45a6d871c13a344926319fd26a45380a3f0527ac1d3fda6f042885cb00bc
5
5
  SHA512:
6
- metadata.gz: b38f0a2f9fb329ee1ea9d0fe098a9dc8854744effef9f6699209b5375e2cd1f68b2dcb557821c074653396ad4607db75f38c054b2b72b3736714a13b3ea49050
7
- data.tar.gz: 5c9869505a2bf795156252e81871166fa0a5818798cae9ad51d6a1099938fd3a3e00922aabd22259f4073f0f8f68f4d5cb83f27bed58d01bc02a1b793e48ac99
6
+ metadata.gz: df7d5ebd7e07c4bc75ae4e895f9d362fb99ff6cfb56e24ad6d8cfc4c7b3bcd3e63e5af943573f818988be0ff0083ba2f62094c0375feb97776860fbc59f7f018
7
+ data.tar.gz: 7c7b58321d235b00c00da9f77fcd1275e92358ddbce580e12795dd0eb6c4dc6eabd752ebe85d89e48ba7ea52b66a49566a9028822ca926f9d0c93ede32feac84
data/lib/occams-record.rb CHANGED
@@ -5,3 +5,4 @@ require 'occams-record/eager_loaders'
5
5
  require 'occams-record/results'
6
6
  require 'occams-record/query'
7
7
  require 'occams-record/raw_query'
8
+ require 'occams-record/errors'
@@ -117,7 +117,7 @@ module OccamsRecord
117
117
  when :has_many
118
118
  HasMany
119
119
  when :has_and_belongs_to_many
120
- EagerLoaders::Habtm
120
+ Habtm
121
121
  else
122
122
  raise "Unsupported association type `#{macro}`"
123
123
  end
@@ -49,7 +49,13 @@ module OccamsRecord
49
49
  # @yield
50
50
  #
51
51
  def calc_ids(rows)
52
- ids = rows.map { |r| r.send @foreign_key }.compact.uniq
52
+ ids = rows.map { |row|
53
+ begin
54
+ row.send @foreign_key
55
+ rescue NoMethodError => e
56
+ raise MissingColumnError.new(row, e.name)
57
+ end
58
+ }.compact.uniq
53
59
  yield ids
54
60
  end
55
61
 
@@ -55,8 +55,6 @@ module OccamsRecord
55
55
  raise 'Not Implemented'
56
56
  end
57
57
 
58
- private
59
-
60
58
  #
61
59
  # Returns the base scope for the relation, including any scope defined on the association itself,
62
60
  # and any optional scope passed into the eager loader.
@@ -11,7 +11,13 @@ module OccamsRecord
11
11
  # @yield
12
12
  #
13
13
  def query(rows)
14
- ids = rows.map { |r| r.send @ref.foreign_key }.compact.uniq
14
+ ids = rows.map { |row|
15
+ begin
16
+ row.send @ref.foreign_key
17
+ rescue NoMethodError => e
18
+ raise MissingColumnError.new(row, e.name)
19
+ end
20
+ }.compact.uniq
15
21
  yield base_scope.where(@ref.active_record_primary_key => ids) if ids.any?
16
22
  end
17
23
 
@@ -30,14 +30,22 @@ module OccamsRecord
30
30
  }
31
31
 
32
32
  assoc_rows_by_id = assoc_rows.reduce({}) { |a, row|
33
- id = row.send(@ref.association_primary_key).to_s
33
+ begin
34
+ id = row.send(@ref.association_primary_key).to_s
35
+ rescue NoMethodError => e
36
+ raise MissingColumnError.new(row, e.name)
37
+ end
34
38
  a[id] = row
35
39
  a
36
40
  }
37
41
 
38
42
  assign = "#{name}="
39
43
  rows.each do |row|
40
- id = row.send(@ref.active_record_primary_key).to_s
44
+ begin
45
+ id = row.send(@ref.active_record_primary_key).to_s
46
+ rescue NoMethodError => e
47
+ raise MissingColumnError.new(row, e.name)
48
+ end
41
49
  assoc_fkeys = (joins_by_id[id] || []).uniq
42
50
  associations = assoc_rows_by_id.values_at(*assoc_fkeys).compact.uniq
43
51
  row.send assign, associations
@@ -59,7 +67,14 @@ module OccamsRecord
59
67
  join_table = conn.quote_table_name @ref.join_table
60
68
  assoc_fkey = conn.quote_column_name @ref.association_foreign_key
61
69
  fkey = conn.quote_column_name @ref.foreign_key
62
- quoted_ids = rows.map { |r| conn.quote r.send @ref.active_record_primary_key }
70
+ quoted_ids = rows.map { |row|
71
+ begin
72
+ id = row.send @ref.active_record_primary_key
73
+ rescue NoMethodError => e
74
+ raise MissingColumnError.new(row, e.name)
75
+ end
76
+ conn.quote id
77
+ }
63
78
 
64
79
  @join_rows = conn.
65
80
  exec_query("SELECT #{fkey}, #{assoc_fkey} FROM #{join_table} WHERE #{fkey} IN (#{quoted_ids.join ','})").
@@ -12,7 +12,13 @@ module OccamsRecord
12
12
  #
13
13
  def query(rows)
14
14
  return if rows.empty?
15
- ids = rows.map { |r| r.send @ref.active_record_primary_key }.compact.uniq
15
+ ids = rows.map { |row|
16
+ begin
17
+ row.send @ref.active_record_primary_key
18
+ rescue NoMethodError => e
19
+ raise MissingColumnError.new(row, e.name)
20
+ end
21
+ }.compact.uniq
16
22
  q = base_scope.where(@ref.foreign_key => ids)
17
23
  q.where!(@ref.type => rows[0].class&.model_name) if @ref.options[:as]
18
24
  yield q if ids.any?
@@ -57,7 +57,13 @@ module OccamsRecord
57
57
  def merge!(assoc_rows_of_type, rows)
58
58
  return if assoc_rows_of_type.empty?
59
59
  type = assoc_rows_of_type[0].class.model_name
60
- rows_of_type = rows.select { |r| r.send(@foreign_type) == type }
60
+ rows_of_type = rows.select { |row|
61
+ begin
62
+ row.send(@foreign_type) == type
63
+ rescue NoMethodError => e
64
+ raise MissingColumnError.new(row, e.name)
65
+ end
66
+ }
61
67
  model = type.constantize
62
68
  Merge.new(rows_of_type, name).
63
69
  single!(assoc_rows_of_type, @ref.foreign_key.to_s, model.primary_key.to_s)
@@ -0,0 +1,34 @@
1
+ module OccamsRecord
2
+ # Exception raised when a record wasn't loaded with all requested data
3
+ class MissingDataError < StandardError
4
+ # @return [String]
5
+ attr_reader :model_name
6
+ # @return [OccamsRecord::Result::Row]
7
+ attr_reader :record
8
+ # @return [Symbol]
9
+ attr_reader :name
10
+
11
+ # @param record [OccamsRecord::Result::Row]
12
+ # @param name [Symbol]
13
+ def initialize(record, name)
14
+ @record, @name = record, name
15
+ @model_name = record.class.model_name
16
+ end
17
+ end
18
+
19
+ # Exception when an unselected column is called on a result row
20
+ class MissingColumnError < MissingDataError
21
+ # @return [String]
22
+ def message
23
+ "Column '#{name}' is unavailable on #{model_name} because it was not included in the SELECT statement!"
24
+ end
25
+ end
26
+
27
+ # Exception when an unloaded association is called on a result row
28
+ class MissingEagerLoadError < MissingDataError
29
+ # @return [String]
30
+ def message
31
+ "Association '#{name}' is unavailable on #{model_name} because it was not eager loaded!"
32
+ end
33
+ end
34
+ end
@@ -9,25 +9,6 @@ module OccamsRecord
9
9
  # @return [Array<OccamsRecord::Results::Row>] the rows into which associated rows will be merged
10
10
  attr_reader :target_rows
11
11
 
12
- # Exception raised when a foreign or primary key is missing from a record
13
- class MissingFieldError < StandardError
14
- # @return [OccamsRecord::Result::Row]
15
- attr_reader :record
16
- # @return [Symbol]
17
- attr_reader :field
18
-
19
- # @param record [OccamsRecord::Result::Row]
20
- # @param field [Symbol]
21
- def initialize(record, field)
22
- @record, @field = record, field
23
- end
24
-
25
- # @return [String]
26
- def message
27
- "Missing field '#{field}' on #{record.inspect}. Did you forget to select it?"
28
- end
29
- end
30
-
31
12
  #
32
13
  # Initialize a new Merge operation.
33
14
  #
@@ -52,7 +33,7 @@ module OccamsRecord
52
33
  begin
53
34
  id = assoc_row.send assoc_attr
54
35
  rescue NoMethodError => e
55
- raise MissingFieldError.new(assoc_row, e.name)
36
+ raise MissingColumnError.new(assoc_row, e.name)
56
37
  end
57
38
  a[id] = assoc_row
58
39
  a
@@ -62,7 +43,7 @@ module OccamsRecord
62
43
  begin
63
44
  attr = row.send target_attr
64
45
  rescue NoMethodError => e
65
- raise MissingFieldError.new(row, e.name)
46
+ raise MissingColumnError.new(row, e.name)
66
47
  end
67
48
  row.send @assign, attr ? assoc_rows_by_id[attr] : nil
68
49
  end
@@ -77,14 +58,14 @@ module OccamsRecord
77
58
  begin
78
59
  assoc_rows_by_attr = assoc_rows.group_by(&assoc_attr.to_sym)
79
60
  rescue NoMethodError => e
80
- raise MissingFieldError.new(assoc_rows[0], e.name)
61
+ raise MissingColumnError.new(assoc_rows[0], e.name)
81
62
  end
82
63
 
83
64
  target_rows.each do |row|
84
65
  begin
85
66
  pkey = row.send target_attr
86
67
  rescue NoMethodError => e
87
- raise MissingFieldError.new(row, e.name)
68
+ raise MissingColumnError.new(row, e.name)
88
69
  end
89
70
  row.send @assign, assoc_rows_by_attr[pkey] || []
90
71
  end
@@ -60,10 +60,25 @@ module OccamsRecord
60
60
  #
61
61
  # Run the query and return the results.
62
62
  #
63
+ # You may optionally pass a block to modify the query just before it's run (the change will NOT persist).
64
+ # This is very useful for running paginated queries.
65
+ #
66
+ # occams = OccamsRecord.query(Widget.all)
67
+ #
68
+ # # returns first 100 rows
69
+ # occams.run { |q| q.offset(0).limit(100) }
70
+ #
71
+ # # returns second 100 rows
72
+ # occams.run { |q| q.offset(100).limit(100) }
73
+ #
74
+ # # returns ALL rows
75
+ # occams.run
76
+ #
63
77
  # @return [Array<OccamsRecord::Results::Row>]
78
+ # @yield [ActiveRecord::Relation] You may use this to return and run a modified relation
64
79
  #
65
80
  def run
66
- sql = scope.to_sql
81
+ sql = block_given? ? yield(scope).to_sql : scope.to_sql
67
82
  @query_logger << sql if @query_logger
68
83
  result = model.connection.exec_query sql
69
84
  row_class = OccamsRecord::Results.klass(result.columns, result.column_types, @eager_loaders.map(&:name), model: model, modules: @use)
@@ -74,6 +89,15 @@ module OccamsRecord
74
89
 
75
90
  alias_method :to_a, :run
76
91
 
92
+ #
93
+ # Returns the number of rows that will be returned if the query is run.
94
+ #
95
+ # @return [Integer]
96
+ #
97
+ def count
98
+ scope.count
99
+ end
100
+
77
101
  #
78
102
  # Run the query and return the first result (which could be nil). This WILL append a LIMIT 1 to the query.
79
103
  #
@@ -107,9 +107,9 @@ module OccamsRecord
107
107
  model = self.class.model_name.constantize
108
108
 
109
109
  if model.reflections.has_key? name.to_s
110
- raise MissingEagerLoadError.new(model.name, name)
110
+ raise MissingEagerLoadError.new(self, name)
111
111
  elsif model.columns_hash.has_key? name.to_s
112
- raise MissingColumnSelectError.new(model.name, name)
112
+ raise MissingColumnError.new(self, name)
113
113
  else
114
114
  super
115
115
  end
@@ -124,33 +124,5 @@ module OccamsRecord
124
124
  "#<OccamsRecord::Results::Row @model_name=#{self.class.model_name} @raw_values=#{@raw_values}>"
125
125
  end
126
126
  end
127
-
128
- # Exception when an unloaded association is called on a result row
129
- class MissingEagerLoadError < StandardError
130
- attr_reader :model_name
131
- attr_reader :name
132
-
133
- def initialize(model_name, name)
134
- @model_name, @name = model_name, name
135
- end
136
-
137
- def message
138
- "The association '#{name}' is unavailable on #{model_name} because it has not been eager loaded!"
139
- end
140
- end
141
-
142
- # Exception when an unselected column is called on a result row
143
- class MissingColumnSelectError < StandardError
144
- attr_reader :model_name
145
- attr_reader :name
146
-
147
- def initialize(model_name, name)
148
- @model_name, @name = model_name, name
149
- end
150
-
151
- def message
152
- "The column '#{name}' is unavailable on #{model_name} because it was not included in the SELECT statement!"
153
- end
154
- end
155
127
  end
156
128
  end
@@ -3,5 +3,5 @@
3
3
  #
4
4
  module OccamsRecord
5
5
  # Library version
6
- VERSION = '0.19.0'.freeze
6
+ VERSION = '0.20.0'.freeze
7
7
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: occams-record
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jordan Hollinger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-27 00:00:00.000000000 Z
11
+ date: 2018-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -50,6 +50,7 @@ files:
50
50
  - lib/occams-record/eager_loaders/has_many.rb
51
51
  - lib/occams-record/eager_loaders/has_one.rb
52
52
  - lib/occams-record/eager_loaders/polymorphic_belongs_to.rb
53
+ - lib/occams-record/errors.rb
53
54
  - lib/occams-record/merge.rb
54
55
  - lib/occams-record/query.rb
55
56
  - lib/occams-record/raw_query.rb