sequel 3.46.0 → 3.47.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (170) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +96 -0
  3. data/Rakefile +7 -1
  4. data/bin/sequel +6 -4
  5. data/doc/active_record.rdoc +1 -1
  6. data/doc/advanced_associations.rdoc +14 -35
  7. data/doc/association_basics.rdoc +66 -4
  8. data/doc/migration.rdoc +4 -0
  9. data/doc/opening_databases.rdoc +6 -0
  10. data/doc/postgresql.rdoc +302 -0
  11. data/doc/release_notes/3.47.0.txt +270 -0
  12. data/doc/security.rdoc +6 -0
  13. data/lib/sequel/adapters/ibmdb.rb +9 -9
  14. data/lib/sequel/adapters/jdbc.rb +22 -7
  15. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -2
  16. data/lib/sequel/adapters/mock.rb +2 -0
  17. data/lib/sequel/adapters/postgres.rb +44 -13
  18. data/lib/sequel/adapters/shared/mssql.rb +1 -1
  19. data/lib/sequel/adapters/shared/mysql.rb +2 -2
  20. data/lib/sequel/adapters/shared/postgres.rb +94 -55
  21. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  22. data/lib/sequel/adapters/sqlite.rb +2 -2
  23. data/lib/sequel/adapters/utils/pg_types.rb +1 -14
  24. data/lib/sequel/adapters/utils/split_alter_table.rb +3 -3
  25. data/lib/sequel/connection_pool/threaded.rb +1 -1
  26. data/lib/sequel/core.rb +1 -1
  27. data/lib/sequel/database/connecting.rb +2 -2
  28. data/lib/sequel/database/features.rb +5 -0
  29. data/lib/sequel/database/misc.rb +47 -5
  30. data/lib/sequel/database/query.rb +2 -2
  31. data/lib/sequel/dataset/actions.rb +4 -2
  32. data/lib/sequel/dataset/misc.rb +1 -1
  33. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  34. data/lib/sequel/dataset/query.rb +8 -6
  35. data/lib/sequel/dataset/sql.rb +8 -6
  36. data/lib/sequel/extensions/constraint_validations.rb +5 -2
  37. data/lib/sequel/extensions/migration.rb +10 -8
  38. data/lib/sequel/extensions/pagination.rb +3 -0
  39. data/lib/sequel/extensions/pg_array.rb +85 -25
  40. data/lib/sequel/extensions/pg_hstore.rb +8 -1
  41. data/lib/sequel/extensions/pg_hstore_ops.rb +4 -1
  42. data/lib/sequel/extensions/pg_inet.rb +16 -13
  43. data/lib/sequel/extensions/pg_interval.rb +6 -2
  44. data/lib/sequel/extensions/pg_json.rb +18 -11
  45. data/lib/sequel/extensions/pg_range.rb +17 -2
  46. data/lib/sequel/extensions/pg_range_ops.rb +7 -5
  47. data/lib/sequel/extensions/pg_row.rb +29 -12
  48. data/lib/sequel/extensions/pretty_table.rb +3 -0
  49. data/lib/sequel/extensions/query.rb +3 -0
  50. data/lib/sequel/extensions/schema_caching.rb +2 -0
  51. data/lib/sequel/extensions/schema_dumper.rb +3 -1
  52. data/lib/sequel/extensions/select_remove.rb +3 -0
  53. data/lib/sequel/model.rb +8 -2
  54. data/lib/sequel/model/associations.rb +39 -27
  55. data/lib/sequel/model/base.rb +99 -38
  56. data/lib/sequel/model/plugins.rb +25 -0
  57. data/lib/sequel/plugins/association_autoreloading.rb +27 -22
  58. data/lib/sequel/plugins/association_dependencies.rb +1 -7
  59. data/lib/sequel/plugins/auto_validations.rb +110 -0
  60. data/lib/sequel/plugins/boolean_readers.rb +1 -6
  61. data/lib/sequel/plugins/caching.rb +6 -13
  62. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  63. data/lib/sequel/plugins/composition.rb +14 -7
  64. data/lib/sequel/plugins/constraint_validations.rb +2 -13
  65. data/lib/sequel/plugins/defaults_setter.rb +1 -6
  66. data/lib/sequel/plugins/dirty.rb +8 -0
  67. data/lib/sequel/plugins/error_splitter.rb +54 -0
  68. data/lib/sequel/plugins/force_encoding.rb +1 -5
  69. data/lib/sequel/plugins/hook_class_methods.rb +1 -6
  70. data/lib/sequel/plugins/input_transformer.rb +79 -0
  71. data/lib/sequel/plugins/instance_filters.rb +7 -1
  72. data/lib/sequel/plugins/instance_hooks.rb +7 -1
  73. data/lib/sequel/plugins/json_serializer.rb +5 -10
  74. data/lib/sequel/plugins/lazy_attributes.rb +20 -7
  75. data/lib/sequel/plugins/list.rb +1 -6
  76. data/lib/sequel/plugins/many_through_many.rb +1 -2
  77. data/lib/sequel/plugins/many_to_one_pk_lookup.rb +23 -39
  78. data/lib/sequel/plugins/optimistic_locking.rb +1 -5
  79. data/lib/sequel/plugins/pg_row.rb +4 -2
  80. data/lib/sequel/plugins/pg_typecast_on_load.rb +3 -7
  81. data/lib/sequel/plugins/prepared_statements.rb +1 -5
  82. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -11
  83. data/lib/sequel/plugins/rcte_tree.rb +2 -2
  84. data/lib/sequel/plugins/serialization.rb +11 -13
  85. data/lib/sequel/plugins/serialization_modification_detection.rb +13 -1
  86. data/lib/sequel/plugins/single_table_inheritance.rb +4 -4
  87. data/lib/sequel/plugins/static_cache.rb +67 -19
  88. data/lib/sequel/plugins/string_stripper.rb +7 -27
  89. data/lib/sequel/plugins/subclasses.rb +3 -5
  90. data/lib/sequel/plugins/tactical_eager_loading.rb +2 -2
  91. data/lib/sequel/plugins/timestamps.rb +2 -7
  92. data/lib/sequel/plugins/touch.rb +5 -8
  93. data/lib/sequel/plugins/tree.rb +1 -6
  94. data/lib/sequel/plugins/typecast_on_load.rb +1 -5
  95. data/lib/sequel/plugins/update_primary_key.rb +26 -14
  96. data/lib/sequel/plugins/validation_class_methods.rb +31 -16
  97. data/lib/sequel/plugins/validation_helpers.rb +50 -26
  98. data/lib/sequel/plugins/xml_serializer.rb +3 -6
  99. data/lib/sequel/sql.rb +1 -1
  100. data/lib/sequel/version.rb +1 -1
  101. data/spec/adapters/postgres_spec.rb +131 -15
  102. data/spec/adapters/sqlite_spec.rb +1 -1
  103. data/spec/core/connection_pool_spec.rb +16 -17
  104. data/spec/core/database_spec.rb +111 -40
  105. data/spec/core/dataset_spec.rb +65 -74
  106. data/spec/core/expression_filters_spec.rb +6 -5
  107. data/spec/core/object_graph_spec.rb +0 -1
  108. data/spec/core/schema_spec.rb +23 -23
  109. data/spec/core/spec_helper.rb +5 -1
  110. data/spec/extensions/association_dependencies_spec.rb +1 -1
  111. data/spec/extensions/association_proxies_spec.rb +1 -1
  112. data/spec/extensions/auto_validations_spec.rb +90 -0
  113. data/spec/extensions/caching_spec.rb +6 -0
  114. data/spec/extensions/class_table_inheritance_spec.rb +8 -1
  115. data/spec/extensions/composition_spec.rb +12 -5
  116. data/spec/extensions/constraint_validations_spec.rb +4 -4
  117. data/spec/extensions/core_refinements_spec.rb +29 -79
  118. data/spec/extensions/dirty_spec.rb +14 -0
  119. data/spec/extensions/error_splitter_spec.rb +18 -0
  120. data/spec/extensions/identity_map_spec.rb +0 -1
  121. data/spec/extensions/input_transformer_spec.rb +54 -0
  122. data/spec/extensions/instance_filters_spec.rb +6 -0
  123. data/spec/extensions/instance_hooks_spec.rb +12 -1
  124. data/spec/extensions/json_serializer_spec.rb +0 -1
  125. data/spec/extensions/lazy_attributes_spec.rb +64 -55
  126. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  127. data/spec/extensions/many_through_many_spec.rb +3 -4
  128. data/spec/extensions/many_to_one_pk_lookup_spec.rb +53 -15
  129. data/spec/extensions/migration_spec.rb +16 -0
  130. data/spec/extensions/null_dataset_spec.rb +1 -1
  131. data/spec/extensions/pg_array_spec.rb +48 -1
  132. data/spec/extensions/pg_hstore_ops_spec.rb +10 -2
  133. data/spec/extensions/pg_hstore_spec.rb +5 -0
  134. data/spec/extensions/pg_inet_spec.rb +5 -0
  135. data/spec/extensions/pg_interval_spec.rb +7 -3
  136. data/spec/extensions/pg_json_spec.rb +6 -1
  137. data/spec/extensions/pg_range_ops_spec.rb +4 -1
  138. data/spec/extensions/pg_range_spec.rb +5 -0
  139. data/spec/extensions/pg_row_plugin_spec.rb +13 -0
  140. data/spec/extensions/pg_row_spec.rb +28 -19
  141. data/spec/extensions/pg_typecast_on_load_spec.rb +6 -1
  142. data/spec/extensions/prepared_statements_associations_spec.rb +1 -1
  143. data/spec/extensions/query_literals_spec.rb +1 -1
  144. data/spec/extensions/rcte_tree_spec.rb +2 -2
  145. data/spec/extensions/schema_spec.rb +2 -2
  146. data/spec/extensions/serialization_modification_detection_spec.rb +8 -0
  147. data/spec/extensions/serialization_spec.rb +15 -1
  148. data/spec/extensions/sharding_spec.rb +1 -1
  149. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  150. data/spec/extensions/static_cache_spec.rb +59 -9
  151. data/spec/extensions/tactical_eager_loading_spec.rb +19 -4
  152. data/spec/extensions/update_primary_key_spec.rb +17 -1
  153. data/spec/extensions/validation_class_methods_spec.rb +25 -0
  154. data/spec/extensions/validation_helpers_spec.rb +59 -3
  155. data/spec/integration/associations_test.rb +5 -5
  156. data/spec/integration/eager_loader_test.rb +32 -63
  157. data/spec/integration/model_test.rb +2 -2
  158. data/spec/integration/plugin_test.rb +88 -56
  159. data/spec/integration/prepared_statement_test.rb +1 -1
  160. data/spec/integration/schema_test.rb +1 -1
  161. data/spec/integration/timezone_test.rb +0 -1
  162. data/spec/integration/transaction_test.rb +0 -1
  163. data/spec/model/association_reflection_spec.rb +1 -1
  164. data/spec/model/associations_spec.rb +106 -84
  165. data/spec/model/base_spec.rb +4 -4
  166. data/spec/model/eager_loading_spec.rb +8 -8
  167. data/spec/model/model_spec.rb +27 -9
  168. data/spec/model/plugins_spec.rb +71 -0
  169. data/spec/model/record_spec.rb +99 -13
  170. metadata +12 -2
@@ -33,12 +33,7 @@ module Sequel
33
33
  end
34
34
 
35
35
  module ClassMethods
36
- # Create boolean readers for the class using the columns from the new dataset.
37
- def set_dataset(*args)
38
- super
39
- create_boolean_readers
40
- self
41
- end
36
+ Plugins.after_set_dataset(self, :create_boolean_readers)
42
37
 
43
38
  private
44
39
 
@@ -70,18 +70,7 @@ module Sequel
70
70
  "#{self}:#{Array(pk).join(',')}"
71
71
  end
72
72
 
73
- # Copy the necessary class instance variables to the subclass.
74
- def inherited(subclass)
75
- super
76
- store = @cache_store
77
- ttl = @cache_ttl
78
- cache_ignore_exceptions = @cache_ignore_exceptions
79
- subclass.instance_eval do
80
- @cache_store = store
81
- @cache_ttl = ttl
82
- @cache_ignore_exceptions = cache_ignore_exceptions
83
- end
84
- end
73
+ Plugins.inherited_instance_variables(self, :@cache_store=>nil, :@cache_ttl=>nil, :@cache_ignore_exceptions=>nil)
85
74
 
86
75
  # Set the time to live for the cache store, in seconds (default is 3600, # so 1 hour).
87
76
  def set_cache_ttl(ttl)
@@ -92,7 +81,11 @@ module Sequel
92
81
 
93
82
  # Delete the entry with the matching key from the cache
94
83
  def cache_delete(ck)
95
- @cache_store.delete(ck)
84
+ if @cache_ignore_exceptions
85
+ @cache_store.delete(ck) rescue nil
86
+ else
87
+ @cache_store.delete(ck)
88
+ end
96
89
  nil
97
90
  end
98
91
 
@@ -187,6 +187,7 @@ module Sequel
187
187
  # Delete the row from all backing tables, starting from the
188
188
  # most recent table and going through all superclasses.
189
189
  def delete
190
+ raise Sequel::Error, "can't delete frozen object" if frozen?
190
191
  m = model
191
192
  m.cti_tables.reverse.each do |table|
192
193
  m.db.from(table).filter(m.primary_key=>pk).delete
@@ -124,12 +124,7 @@ module Sequel
124
124
  define_composition_accessor(name, opts)
125
125
  end
126
126
 
127
- # Copy the necessary class instance variables to the subclass.
128
- def inherited(subclass)
129
- super
130
- c = compositions.dup
131
- subclass.instance_eval{@compositions = c}
132
- end
127
+ Plugins.inherited_instance_variables(self, :@compositions=>:dup)
133
128
 
134
129
  # Define getter and setter methods for the composition object.
135
130
  def define_composition_accessor(name, opts={})
@@ -137,7 +132,13 @@ module Sequel
137
132
  composer = opts[:composer]
138
133
  composition_module.class_eval do
139
134
  define_method(name) do
140
- compositions.include?(name) ? compositions[name] : (compositions[name] = instance_eval(&composer))
135
+ if compositions.has_key?(name)
136
+ compositions[name]
137
+ elsif frozen?
138
+ instance_eval(&composer)
139
+ else
140
+ compositions[name] = instance_eval(&composer)
141
+ end
141
142
  end
142
143
  define_method("#{name}=") do |v|
143
144
  modified!
@@ -165,6 +166,12 @@ module Sequel
165
166
  def compositions
166
167
  @compositions ||= {}
167
168
  end
169
+
170
+ # Freeze compositions hash when freezing model instance.
171
+ def freeze
172
+ compositions.freeze
173
+ super
174
+ end
168
175
  end
169
176
  end
170
177
  end
@@ -61,19 +61,8 @@ module Sequel
61
61
  # The name of the table containing the constraint validations metadata.
62
62
  attr_reader :constraint_validations_table
63
63
 
64
- # Copy the name of the constraint validations metadata table into the subclass.
65
- def inherited(subclass)
66
- super
67
- subclass.instance_variable_set(:@constraint_validations_table, @constraint_validations_table)
68
- end
69
-
70
- # Parse the constraint validations from the database whenever the dataset
71
- # changes.
72
- def set_dataset(*)
73
- r = super
74
- parse_constraint_validations
75
- r
76
- end
64
+ Plugins.inherited_instance_variables(self, :@constraint_validations_table=>nil)
65
+ Plugins.after_set_dataset(self, :parse_constraint_validations)
77
66
 
78
67
  private
79
68
 
@@ -28,12 +28,7 @@ module Sequel
28
28
  # this hash to set specific default values, by default the ones will be parsed from the database.
29
29
  attr_reader :default_values
30
30
 
31
- # Set the default values when loading the dataset.
32
- def set_dataset(*)
33
- x = super
34
- set_default_values
35
- x
36
- end
31
+ Plugins.after_set_dataset(self, :set_default_values)
37
32
 
38
33
  private
39
34
 
@@ -101,6 +101,14 @@ module Sequel
101
101
  @initial_values ||= {}
102
102
  end
103
103
 
104
+ # Freeze internal data structures
105
+ def freeze
106
+ initial_values.freeze
107
+ missing_initial_values.freeze
108
+ @previous_changes.freeze if @previous_changes
109
+ super
110
+ end
111
+
104
112
  # Reset the column to its initial value. If the column was not set
105
113
  # initial, removes it from the values.
106
114
  #
@@ -0,0 +1,54 @@
1
+ module Sequel
2
+ module Plugins
3
+ # The error_splitter plugin automatically splits errors entries related to
4
+ # multiple columns to have separate error entries, one per column. For example,
5
+ # a multiple column uniqueness entry:
6
+ #
7
+ # validates_unique([:artist_id, :name])
8
+ #
9
+ # would by default result in errors entries such as:
10
+ #
11
+ # {[:artist_id, :name]=>'is already taken'}
12
+ #
13
+ # This plugin transforms those errors into:
14
+ #
15
+ # {:artist_id=>'is already taken', :name=>'is already taken'}
16
+ #
17
+ # The main reason to split errors is if you have a list of fields that you
18
+ # are checking for validation errors. If you don't split the errors, then:
19
+ #
20
+ # errors.on(:artist_id)
21
+ #
22
+ # would not return the uniqueness error.
23
+ #
24
+ # Usage:
25
+ #
26
+ # # Make all model subclass instances split errors (called before loading subclasses)
27
+ # Sequel::Model.plugin :error_splitter
28
+ #
29
+ # # Make the Album class split errors
30
+ # Album.plugin :error_splitter
31
+ module ErrorSplitter
32
+ module InstanceMethods
33
+ # If the model instance is not valid, go through all of the errors entries. For
34
+ # any that apply to multiple columns, remove them and add separate error entries,
35
+ # one per column.
36
+ def _valid?(*)
37
+ v = super
38
+ unless v
39
+ errors.keys.select{|k| k.is_a?(Array)}.each do |ks|
40
+ msgs = errors.delete(ks)
41
+ ks.each do |k|
42
+ msgs.each do |msg|
43
+ errors.add(k, msg)
44
+ end
45
+ end
46
+ end
47
+ end
48
+ v
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
54
+
@@ -32,11 +32,7 @@ module Sequel
32
32
  # The string encoding to force on a column string values
33
33
  attr_accessor :forced_encoding
34
34
 
35
- # Copy the forced_encoding value into the subclass
36
- def inherited(subclass)
37
- super
38
- subclass.forced_encoding = forced_encoding
39
- end
35
+ Plugins.inherited_instance_variables(self, :@forced_encoding=>nil)
40
36
  end
41
37
 
42
38
  module InstanceMethods
@@ -91,12 +91,7 @@ module Sequel
91
91
  @hooks[hook].each{|k,v| yield v}
92
92
  end
93
93
 
94
- # Make a copy of the current class's hooks for the subclass.
95
- def inherited(subclass)
96
- hooks = subclass.instance_variable_set(:@hooks, {})
97
- instance_variable_get(:@hooks).each{|k,v| hooks[k] = v.dup}
98
- super
99
- end
94
+ Plugins.inherited_instance_variables(self, :@hooks=>:hash_dup)
100
95
 
101
96
  private
102
97
 
@@ -0,0 +1,79 @@
1
+ module Sequel
2
+ module Plugins
3
+ # InputTransformer is a plugin that allows generic transformations
4
+ # of input values in model column setters. Example:
5
+ #
6
+ # Album.plugin :input_transformer
7
+ # Album.add_input_transformer(:reverser){|v| v.is_a?(String) ? v.reverse : v}
8
+ # album = Album.new(:name=>'foo')
9
+ # album.name # => 'oof'
10
+ #
11
+ # You can specifically set some columns to skip some input
12
+ # input transformers:
13
+ #
14
+ # Album.skip_input_transformer(:reverser, :foo)
15
+ # Album.new(:foo=>'bar').foo # => 'bar'
16
+ #
17
+ # Usage:
18
+ #
19
+ # # Make all model subclass instances support input transformers (called before loading subclasses)
20
+ # Sequel::Model.plugin :input_transformer
21
+ #
22
+ # # Make the Album class support input transformers
23
+ # Album.plugin :input_transformer
24
+ module InputTransformer
25
+ def self.apply(model, *)
26
+ model.instance_eval do
27
+ @input_transformers = {}
28
+ @input_transformer_order = []
29
+ @skip_input_transformer_columns = {}
30
+ end
31
+ end
32
+
33
+ # If an input transformer is given in the plugin call,
34
+ # add it as a transformer
35
+ def self.configure(model, transformer_name=nil, &block)
36
+ model.add_input_transformer(transformer_name, &block) if transformer_name || block
37
+ end
38
+
39
+ module ClassMethods
40
+ # Hash of input transformer name symbols to transformer callables.
41
+ attr_reader :input_transformers
42
+
43
+ # The order in which to call the input transformers.
44
+ attr_reader :input_transformer_order
45
+
46
+ Plugins.inherited_instance_variables(self, :@skip_input_transformer_columns=>:hash_dup, :@input_transformers=>:dup, :@input_transformer_order=>:dup)
47
+
48
+ # Add an input transformer to this model.
49
+ def add_input_transformer(transformer_name, &block)
50
+ raise(Error, 'must provide both transformer name and block when adding input transformer') unless transformer_name && block
51
+ @input_transformers[transformer_name] = block
52
+ @input_transformer_order.unshift(transformer_name)
53
+ @skip_input_transformer_columns[transformer_name] = []
54
+ end
55
+
56
+ # Set columns that the transformer should skip.
57
+ def skip_input_transformer(transformer_name, *columns)
58
+ @skip_input_transformer_columns[transformer_name].concat(columns).uniq!
59
+ end
60
+
61
+ # Return true if the transformer should not be called for the given column.
62
+ def skip_input_transformer?(transformer_name, column)
63
+ @skip_input_transformer_columns[transformer_name].include?(column)
64
+ end
65
+ end
66
+
67
+ module InstanceMethods
68
+ # Transform the input using all of the transformers, except those explicitly
69
+ # skipped, before setting the value in the model object.
70
+ def []=(k, v)
71
+ model.input_transformer_order.each do |transformer_name|
72
+ v = model.input_transformers[transformer_name].call(v) unless model.skip_input_transformer?(transformer_name, k)
73
+ end
74
+ super
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -58,6 +58,12 @@ module Sequel
58
58
  super
59
59
  clear_instance_filters
60
60
  end
61
+
62
+ # Freeze the instance filters when freezing the object
63
+ def freeze
64
+ instance_filters.freeze
65
+ super
66
+ end
61
67
 
62
68
  # Add an instance filter to the array of instance filters
63
69
  # Both the arguments given and the block are passed to the
@@ -85,7 +91,7 @@ module Sequel
85
91
 
86
92
  # Apply the instance filters to the given dataset
87
93
  def apply_instance_filters(ds)
88
- instance_filters.inject(ds){|ds, i| ds.filter(*i[0], &i[1])}
94
+ instance_filters.inject(ds){|ds1, i| ds1.filter(*i[0], &i[1])}
89
95
  end
90
96
 
91
97
  # Clear the instance filters.
@@ -29,7 +29,13 @@ module Sequel
29
29
  BEFORE_HOOKS = Sequel::Model::BEFORE_HOOKS
30
30
  AFTER_HOOKS = Sequel::Model::AFTER_HOOKS - [:after_initialize]
31
31
  HOOKS = BEFORE_HOOKS + AFTER_HOOKS
32
- HOOKS.each{|h| class_eval("def #{h}_hook(&block); add_instance_hook(:#{h}, &block); self end", __FILE__, __LINE__)}
32
+ HOOKS.each{|h| class_eval(<<-END , __FILE__, __LINE__+1)}
33
+ def #{h}_hook(&block)
34
+ raise Sequel::Error, "can't add hooks to frozen object" if frozen?
35
+ add_instance_hook(:#{h}, &block)
36
+ self
37
+ end
38
+ END
33
39
 
34
40
  BEFORE_HOOKS.each{|h| class_eval("def #{h}; run_before_instance_hooks(:#{h}) == false ? false : super end", __FILE__, __LINE__)}
35
41
  AFTER_HOOKS.each{|h| class_eval(<<-END, __FILE__, __LINE__ + 1)}
@@ -175,18 +175,13 @@ module Sequel
175
175
  new.from_json_node(hash, {:all_columns=>true, :all_associations=>true}.merge(opts))
176
176
  end
177
177
 
178
- # Call the dataset +to_json+ method.
179
- def to_json(*a)
180
- dataset.to_json(*a)
181
- end
182
-
183
- # Copy the current model object's default json options into the subclass.
184
- def inherited(subclass)
185
- super
178
+ Plugins.inherited_instance_variables(self, :@json_serializer_opts=>lambda do |json_serializer_opts|
186
179
  opts = {}
187
180
  json_serializer_opts.each{|k, v| opts[k] = (v.is_a?(Array) || v.is_a?(Hash)) ? v.dup : v}
188
- subclass.instance_variable_set(:@json_serializer_opts, opts)
189
- end
181
+ opts
182
+ end)
183
+
184
+ Plugins.def_dataset_methods(self, :to_json)
190
185
  end
191
186
 
192
187
  module InstanceMethods
@@ -5,7 +5,7 @@ module Sequel
5
5
  # is needed after the instance has been retrieved, a database query is made to
6
6
  # retreive the value of the attribute.
7
7
  #
8
- # This plugin depends on the identity_map and tactical_eager_loading plugin, and allows you to
8
+ # This plugin depends on the tactical_eager_loading plugin, and allows you to
9
9
  # eagerly load lazy attributes for all objects retrieved with the current object.
10
10
  # So the following code should issue one query to get the albums and one query to
11
11
  # get the reviews for all of those albums:
@@ -20,9 +20,8 @@ module Sequel
20
20
  # # You can specify multiple columns to lazily load:
21
21
  # Album.plugin :lazy_attributes, :review, :tracklist
22
22
  module LazyAttributes
23
- # Lazy attributes requires the identity map and tactical eager loading plugins
23
+ # Lazy attributes requires the tactical_eager_loading plugin
24
24
  def self.apply(model, *attrs)
25
- model.plugin :identity_map
26
25
  model.plugin :tactical_eager_loading
27
26
  end
28
27
 
@@ -51,7 +50,7 @@ module Sequel
51
50
  include(self.lazy_attributes_module ||= Module.new) unless lazy_attributes_module
52
51
  lazy_attributes_module.class_eval do
53
52
  define_method(a) do
54
- if !values.include?(a) && !new?
53
+ if !values.has_key?(a) && !new?
55
54
  lazy_attribute_lookup(a)
56
55
  else
57
56
  super()
@@ -69,9 +68,23 @@ module Sequel
69
68
  # the attribute for just the current object. Return the value of
70
69
  # the attribute for the current object.
71
70
  def lazy_attribute_lookup(a)
72
- primary_key = model.primary_key
73
- model.select(*(Array(primary_key) + [a])).filter(primary_key=>retrieved_with.map{|o| o.pk}).all if model.identity_map && retrieved_with
74
- values[a] = this.select(a).first[a] unless values.include?(a)
71
+ if frozen?
72
+ return this.dup.select(a).get(a)
73
+ end
74
+
75
+ if retrieved_with
76
+ raise(Error, "Invalid primary key column for #{model}: #{pkc.inspect}") unless primary_key = model.primary_key
77
+ composite_pk = true if primary_key.is_a?(Array)
78
+ id_map = {}
79
+ retrieved_with.each{|o| id_map[o.pk] = o unless o.values.has_key?(a) || o.frozen?}
80
+ model.select(*(Array(primary_key) + [a])).filter(primary_key=>id_map.keys).naked.each do |row|
81
+ obj = id_map[composite_pk ? row.values_at(*primary_key) : row[primary_key]]
82
+ if obj && !obj.values.has_key?(a)
83
+ obj.values[a] = row[a]
84
+ end
85
+ end
86
+ end
87
+ values[a] = this.select(a).get(a) unless values.has_key?(a)
75
88
  values[a]
76
89
  end
77
90
  end
@@ -78,12 +78,7 @@ module Sequel
78
78
  # proc should accept an instance and return a dataset representing the list.
79
79
  attr_accessor :scope_proc
80
80
 
81
- # Copy the +position_field+ and +scope_proc+ to the subclass.
82
- def inherited(subclass)
83
- super
84
- subclass.position_field = position_field
85
- subclass.scope_proc = scope_proc
86
- end
81
+ Plugins.inherited_instance_variables(self, :@position_field=>nil, :@scope_proc=>nil)
87
82
  end
88
83
 
89
84
  module InstanceMethods