sequel 3.12.1 → 3.13.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.
- data/CHANGELOG +42 -0
- data/README.rdoc +137 -118
- data/Rakefile +21 -66
- data/doc/active_record.rdoc +9 -9
- data/doc/advanced_associations.rdoc +59 -188
- data/doc/association_basics.rdoc +15 -2
- data/doc/cheat_sheet.rdoc +38 -33
- data/doc/dataset_filtering.rdoc +16 -7
- data/doc/prepared_statements.rdoc +7 -7
- data/doc/querying.rdoc +5 -4
- data/doc/release_notes/3.13.0.txt +210 -0
- data/doc/sharding.rdoc +1 -1
- data/doc/sql.rdoc +5 -5
- data/doc/validations.rdoc +11 -11
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/do.rb +3 -3
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +39 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -4
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +10 -1
- data/lib/sequel/adapters/shared/mysql.rb +63 -0
- data/lib/sequel/adapters/shared/postgres.rb +61 -3
- data/lib/sequel/adapters/sqlite.rb +105 -18
- data/lib/sequel/connection_pool.rb +31 -30
- data/lib/sequel/core.rb +58 -58
- data/lib/sequel/core_sql.rb +52 -43
- data/lib/sequel/database/misc.rb +11 -0
- data/lib/sequel/database/query.rb +55 -17
- data/lib/sequel/dataset/actions.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -3
- data/lib/sequel/dataset/sql.rb +24 -11
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/metaprogramming.rb +4 -0
- data/lib/sequel/model.rb +37 -19
- data/lib/sequel/model/associations.rb +33 -25
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/model/plugins.rb +7 -2
- data/lib/sequel/plugins/active_model.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +2 -2
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
- data/lib/sequel/plugins/identity_map.rb +3 -3
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +212 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +6 -7
- data/lib/sequel/plugins/tree.rb +118 -0
- data/lib/sequel/plugins/xml_serializer.rb +321 -0
- data/lib/sequel/sql.rb +315 -206
- data/lib/sequel/timezones.rb +40 -17
- data/lib/sequel/version.rb +8 -2
- data/spec/adapters/firebird_spec.rb +2 -2
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/mysql_spec.rb +2 -2
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +36 -6
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +3 -3
- data/spec/core/core_sql_spec.rb +31 -13
- data/spec/core/database_spec.rb +39 -2
- data/spec/core/dataset_spec.rb +24 -12
- data/spec/core/expression_filters_spec.rb +5 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core/version_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +82 -67
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +3 -2
- data/spec/extensions/composition_spec.rb +2 -5
- data/spec/extensions/force_encoding_spec.rb +3 -1
- data/spec/extensions/hook_class_methods_spec.rb +1 -1
- data/spec/extensions/identity_map_spec.rb +1 -1
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +154 -0
- data/spec/extensions/lazy_attributes_spec.rb +1 -2
- data/spec/extensions/list_spec.rb +251 -0
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -3
- data/spec/extensions/migration_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +5 -6
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +6 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +119 -0
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/extensions/xml_serializer_spec.rb +142 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +29 -14
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +1 -1
- data/spec/integration/plugin_test.rb +316 -1
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +8 -8
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +35 -20
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +49 -34
- data/spec/model/base_spec.rb +1 -1
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +1 -1
- data/spec/model/hooks_spec.rb +1 -1
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +7 -1
- data/spec/model/plugins_spec.rb +1 -1
- data/spec/model/record_spec.rb +1 -3
- data/spec/model/spec_helper.rb +2 -2
- data/spec/model/validations_spec.rb +1 -1
- metadata +29 -5
|
@@ -118,7 +118,7 @@ END_MIG
|
|
|
118
118
|
# database type is not recognized, return it as a String type.
|
|
119
119
|
def column_schema_to_ruby_type(schema)
|
|
120
120
|
case t = schema[:db_type].downcase
|
|
121
|
-
when /\A(?:medium|small)?int(?:eger)?(?:\((?:\d+)\))?\z/o
|
|
121
|
+
when /\A(?:medium|small)?int(?:eger)?(?:\((?:\d+)\))?(?: unsigned)?\z/o
|
|
122
122
|
{:type=>Integer}
|
|
123
123
|
when /\Atinyint(?:\((\d+)\))?\z/o
|
|
124
124
|
{:type =>schema[:type] == :boolean ? TrueClass : Integer}
|
|
@@ -2,6 +2,10 @@ module Sequel
|
|
|
2
2
|
# Contains meta_def method for adding methods to objects via blocks, used by some of Sequel's classes and objects.
|
|
3
3
|
module Metaprogramming
|
|
4
4
|
# Define a method with the given name and block body on the receiver.
|
|
5
|
+
#
|
|
6
|
+
# ds = DB[:items]
|
|
7
|
+
# ds.meta_def(:x){42}
|
|
8
|
+
# ds.x # => 42
|
|
5
9
|
def meta_def(name, &block)
|
|
6
10
|
(class << self; self end).send(:define_method, name, &block)
|
|
7
11
|
end
|
data/lib/sequel/model.rb
CHANGED
|
@@ -2,20 +2,38 @@ require 'sequel/core'
|
|
|
2
2
|
|
|
3
3
|
module Sequel
|
|
4
4
|
# Lets you create a Model subclass with its dataset already set.
|
|
5
|
-
# source
|
|
6
|
-
# it will create a dataset using the default database with
|
|
7
|
-
# the given symbol as the table name).
|
|
5
|
+
# +source+ should be an instance of one of the following classes:
|
|
8
6
|
#
|
|
9
|
-
#
|
|
7
|
+
# Database :: Sets the database for this model to +source+.
|
|
8
|
+
# Generally only useful when subclassing directly
|
|
9
|
+
# from the returned class, where the name of the
|
|
10
|
+
# subclass sets the table name (which is combined
|
|
11
|
+
# with the +Database+ in +source+ to create the
|
|
12
|
+
# dataset to use)
|
|
13
|
+
# Dataset :: Sets the dataset for this model to +source+.
|
|
14
|
+
# Symbol :: Sets the table name for this model to +source+. The
|
|
15
|
+
# class will use the default database for model
|
|
16
|
+
# classes in order to create the dataset.
|
|
17
|
+
#
|
|
18
|
+
# The purpose of this method is to set the dataset/database automatically
|
|
10
19
|
# for a model class, if the table name doesn't match the implicit
|
|
11
20
|
# name. This is neater than using set_dataset inside the class,
|
|
12
|
-
# doesn't require a bogus query for the schema
|
|
13
|
-
# it to work correctly in a system that uses code reloading.
|
|
21
|
+
# doesn't require a bogus query for the schema.
|
|
14
22
|
#
|
|
15
|
-
#
|
|
23
|
+
# # Using a symbol
|
|
16
24
|
# class Comment < Sequel::Model(:something)
|
|
17
25
|
# table_name # => :something
|
|
18
26
|
# end
|
|
27
|
+
#
|
|
28
|
+
# # Using a dataset
|
|
29
|
+
# class Comment < Sequel::Model(DB1[:something])
|
|
30
|
+
# dataset # => DB1[:something]
|
|
31
|
+
# end
|
|
32
|
+
#
|
|
33
|
+
# # Using a database
|
|
34
|
+
# class Comment < Sequel::Model(DB1)
|
|
35
|
+
# dataset # => DB1[:comments]
|
|
36
|
+
# end
|
|
19
37
|
def self.Model(source)
|
|
20
38
|
Model::ANONYMOUS_MODEL_CLASSES[source] ||= if source.is_a?(Database)
|
|
21
39
|
c = Class.new(Model)
|
|
@@ -26,20 +44,20 @@ module Sequel
|
|
|
26
44
|
end
|
|
27
45
|
end
|
|
28
46
|
|
|
29
|
-
# Sequel::Model is an object relational mapper built on top of Sequel core. Each
|
|
47
|
+
# <tt>Sequel::Model</tt> is an object relational mapper built on top of Sequel core. Each
|
|
30
48
|
# model class is backed by a dataset instance, and many dataset methods can be
|
|
31
49
|
# called directly on the class. Model datasets return rows as model instances,
|
|
32
50
|
# which have fairly standard ORM instance behavior.
|
|
33
51
|
#
|
|
34
|
-
# Sequel::Model is built completely out of plugins, the only method not part of a
|
|
52
|
+
# <tt>Sequel::Model</tt> is built completely out of plugins, the only method not part of a
|
|
35
53
|
# plugin is the plugin method itself. Plugins can override any class, instance, or
|
|
36
54
|
# dataset method defined by a previous plugin and call super to get the default
|
|
37
55
|
# behavior.
|
|
38
56
|
#
|
|
39
|
-
# You can set the SEQUEL_NO_ASSOCIATIONS constant or environment variable to
|
|
57
|
+
# You can set the +SEQUEL_NO_ASSOCIATIONS+ constant or environment variable to
|
|
40
58
|
# make Sequel not load the associations plugin by default.
|
|
41
59
|
class Model
|
|
42
|
-
# Map that stores model classes created with Sequel::Model()
|
|
60
|
+
# Map that stores model classes created with <tt>Sequel::Model()</tt>, to allow the reopening
|
|
43
61
|
# of classes when dealing with code reloading.
|
|
44
62
|
ANONYMOUS_MODEL_CLASSES = {}
|
|
45
63
|
|
|
@@ -54,24 +72,24 @@ module Sequel
|
|
|
54
72
|
BOOLEAN_SETTINGS = [:typecast_empty_string_to_nil, :typecast_on_assignment, :strict_param_setting, :raise_on_save_failure, :raise_on_typecast_failure, :require_modification, :use_transactions]
|
|
55
73
|
|
|
56
74
|
# Hooks that are called before an action. Can return false to not do the action. When
|
|
57
|
-
# overriding these, it is recommended to call super as the last line of your method,
|
|
75
|
+
# overriding these, it is recommended to call +super+ as the last line of your method,
|
|
58
76
|
# so later hooks are called before earlier hooks.
|
|
59
77
|
BEFORE_HOOKS = [:before_create, :before_update, :before_save, :before_destroy, :before_validation]
|
|
60
78
|
|
|
61
79
|
# Hooks that are called after an action. When overriding these, it is recommended to call
|
|
62
|
-
# super on the first line of your method, so later hooks are called
|
|
80
|
+
# +super+ on the first line of your method, so later hooks are called after earlier hooks.
|
|
63
81
|
AFTER_HOOKS = [:after_initialize, :after_create, :after_update, :after_save, :after_destroy, :after_validation]
|
|
64
82
|
|
|
65
83
|
# Empty instance methods to create that the user can override to get hook/callback behavior.
|
|
66
84
|
# Just like any other method defined by Sequel, if you override one of these, you should
|
|
67
|
-
# call super to get the default behavior (while empty by default, they can also be defined
|
|
85
|
+
# call +super+ to get the default behavior (while empty by default, they can also be defined
|
|
68
86
|
# by plugins). See the {"Model Hooks" guide}[link:files/doc/model_hooks_rdoc.html] for
|
|
69
87
|
# more detail on hooks.
|
|
70
88
|
HOOKS = BEFORE_HOOKS + AFTER_HOOKS
|
|
71
89
|
|
|
72
|
-
# Class instance variables that are inherited in subclasses. If the value is
|
|
90
|
+
# Class instance variables that are inherited in subclasses. If the value is <tt>:dup</tt>, dup is called
|
|
73
91
|
# on the superclass's instance variable when creating the instance variable in the subclass.
|
|
74
|
-
# If the value is nil
|
|
92
|
+
# If the value is +nil+, the superclass's instance variable is used directly in the subclass.
|
|
75
93
|
INHERITED_INSTANCE_VARIABLES = {:@allowed_columns=>:dup, :@dataset_methods=>:dup,
|
|
76
94
|
:@dataset_method_modules=>:dup, :@primary_key=>nil, :@use_transactions=>nil,
|
|
77
95
|
:@raise_on_save_failure=>nil, :@require_modification=>nil,
|
|
@@ -80,8 +98,8 @@ module Sequel
|
|
|
80
98
|
:@typecast_empty_string_to_nil=>nil, :@typecast_on_assignment=>nil,
|
|
81
99
|
:@raise_on_typecast_failure=>nil, :@plugins=>:dup}
|
|
82
100
|
|
|
83
|
-
#
|
|
84
|
-
# it could be
|
|
101
|
+
# Regular expression that determines if a method name is normal in the sense that
|
|
102
|
+
# it could be used literally in ruby code without using send. Used to
|
|
85
103
|
# avoid problems when using eval with a string to define methods.
|
|
86
104
|
NORMAL_METHOD_NAME_REGEXP = /\A[A-Za-z_][A-Za-z0-9_]*\z/
|
|
87
105
|
|
|
@@ -116,7 +134,7 @@ module Sequel
|
|
|
116
134
|
end
|
|
117
135
|
|
|
118
136
|
# The setter methods (methods ending with =) that are never allowed
|
|
119
|
-
# to be called automatically via set
|
|
137
|
+
# to be called automatically via +set+/+update+/+new+/etc..
|
|
120
138
|
RESTRICTED_SETTER_METHODS = instance_methods.map{|x| x.to_s}.grep(SETTER_METHOD_REGEXP)
|
|
121
139
|
end
|
|
122
140
|
end
|
|
@@ -550,6 +550,8 @@ module Sequel
|
|
|
550
550
|
# columns in the associated table.
|
|
551
551
|
# - :limit - Limit the number of records to the provided value. Use
|
|
552
552
|
# an array with two arguments for the value to specify a limit and an offset.
|
|
553
|
+
# - :methods_module - The module that methods the association creates will be placed into. Defaults
|
|
554
|
+
# to the module containing the model's columns.
|
|
553
555
|
# - :order - the column(s) by which to order the association dataset. Can be a
|
|
554
556
|
# singular column or an array.
|
|
555
557
|
# - :order_eager_graph - Whether to add the order to the dataset's order when graphing
|
|
@@ -710,37 +712,43 @@ module Sequel
|
|
|
710
712
|
|
|
711
713
|
private
|
|
712
714
|
|
|
715
|
+
# The module to use for the association's methods. Defaults to
|
|
716
|
+
# the overridable_methods_module.
|
|
717
|
+
def association_module(opts={})
|
|
718
|
+
opts.fetch(:methods_module, overridable_methods_module)
|
|
719
|
+
end
|
|
720
|
+
|
|
713
721
|
# Add a method to the module included in the class, so the method
|
|
714
722
|
# can be easily overridden in the class itself while allowing for
|
|
715
723
|
# super to be called.
|
|
716
|
-
def association_module_def(name, &block)
|
|
717
|
-
|
|
724
|
+
def association_module_def(name, opts={}, &block)
|
|
725
|
+
association_module(opts).module_eval{define_method(name, &block)}
|
|
718
726
|
end
|
|
719
727
|
|
|
720
728
|
# Add a private method to the module included in the class.
|
|
721
|
-
def association_module_private_def(name, &block)
|
|
722
|
-
association_module_def(name, &block)
|
|
723
|
-
|
|
729
|
+
def association_module_private_def(name, opts={}, &block)
|
|
730
|
+
association_module_def(name, opts, &block)
|
|
731
|
+
association_module(opts).send(:private, name)
|
|
724
732
|
end
|
|
725
733
|
|
|
726
734
|
# Add the add_ instance method
|
|
727
735
|
def def_add_method(opts)
|
|
728
|
-
association_module_def(opts.add_method){|o,*args| add_associated_object(opts, o, *args)}
|
|
736
|
+
association_module_def(opts.add_method, opts){|o,*args| add_associated_object(opts, o, *args)}
|
|
729
737
|
end
|
|
730
738
|
|
|
731
739
|
# Adds methods related to the association's dataset to the module included in the class.
|
|
732
740
|
def def_association_dataset_methods(opts)
|
|
733
741
|
# If a block is given, define a helper method for it, because it takes
|
|
734
742
|
# an argument. This is unnecessary in Ruby 1.9, as that has instance_exec.
|
|
735
|
-
association_module_private_def(opts.dataset_helper_method, &opts[:block]) if opts[:block]
|
|
736
|
-
association_module_private_def(opts._dataset_method, &opts[:dataset])
|
|
737
|
-
association_module_def(opts.dataset_method){_dataset(opts)}
|
|
743
|
+
association_module_private_def(opts.dataset_helper_method, opts, &opts[:block]) if opts[:block]
|
|
744
|
+
association_module_private_def(opts._dataset_method, opts, &opts[:dataset])
|
|
745
|
+
association_module_def(opts.dataset_method, opts){_dataset(opts)}
|
|
738
746
|
def_association_method(opts)
|
|
739
747
|
end
|
|
740
748
|
|
|
741
749
|
# Adds method for retrieving the associated objects to the module included in the class.
|
|
742
750
|
def def_association_method(opts)
|
|
743
|
-
association_module_def(opts.association_method){|*reload| load_associated_objects(opts, reload[0])}
|
|
751
|
+
association_module_def(opts.association_method, opts){|*reload| load_associated_objects(opts, reload[0])}
|
|
744
752
|
end
|
|
745
753
|
|
|
746
754
|
# Adds many_to_many association instance methods
|
|
@@ -770,7 +778,7 @@ module Sequel
|
|
|
770
778
|
h = eo[:key_hash][left_pk]
|
|
771
779
|
eo[:rows].each{|object| object.associations[name] = []}
|
|
772
780
|
r = uses_rcks ? rcks.zip(opts.right_primary_keys) : [[right, opts.right_primary_key]]
|
|
773
|
-
l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)},
|
|
781
|
+
l = uses_lcks ? [[lcks.map{|k| SQL::QualifiedIdentifier.new(join_table, k)}, h.keys]] : [[left, h.keys]]
|
|
774
782
|
model.eager_loading_dataset(opts, opts.associated_class.inner_join(join_table, r + l), Array(opts.select), eo[:associations], eo).all do |assoc_record|
|
|
775
783
|
hash_key = if uses_lcks
|
|
776
784
|
left_key_alias.map{|k| assoc_record.values.delete(k)}
|
|
@@ -801,16 +809,16 @@ module Sequel
|
|
|
801
809
|
|
|
802
810
|
return if opts[:read_only]
|
|
803
811
|
|
|
804
|
-
association_module_private_def(opts._add_method) do |o|
|
|
812
|
+
association_module_private_def(opts._add_method, opts) do |o|
|
|
805
813
|
h = {}
|
|
806
814
|
lcks.zip(lcpks).each{|k, pk| h[k] = send(pk)}
|
|
807
815
|
rcks.zip(opts.right_primary_keys).each{|k, pk| h[k] = o.send(pk)}
|
|
808
816
|
_join_table_dataset(opts).insert(h)
|
|
809
817
|
end
|
|
810
|
-
association_module_private_def(opts._remove_method) do |o|
|
|
818
|
+
association_module_private_def(opts._remove_method, opts) do |o|
|
|
811
819
|
_join_table_dataset(opts).filter(lcks.zip(lcpks.map{|k| send(k)}) + rcks.zip(opts.right_primary_keys.map{|k| o.send(k)})).delete
|
|
812
820
|
end
|
|
813
|
-
association_module_private_def(opts._remove_all_method) do
|
|
821
|
+
association_module_private_def(opts._remove_all_method, opts) do
|
|
814
822
|
_join_table_dataset(opts).filter(lcks.zip(lcpks.map{|k| send(k)})).delete
|
|
815
823
|
end
|
|
816
824
|
|
|
@@ -841,7 +849,7 @@ module Sequel
|
|
|
841
849
|
# Skip eager loading if no objects have a foreign key for this association
|
|
842
850
|
unless keys.empty?
|
|
843
851
|
klass = opts.associated_class
|
|
844
|
-
model.eager_loading_dataset(opts, klass.filter(uses_cks ? {opts.primary_keys.map{|k| SQL::QualifiedIdentifier.new(klass.table_name, k)}=>
|
|
852
|
+
model.eager_loading_dataset(opts, klass.filter(uses_cks ? {opts.primary_keys.map{|k| SQL::QualifiedIdentifier.new(klass.table_name, k)}=>keys} : {SQL::QualifiedIdentifier.new(klass.table_name, opts.primary_key)=>keys}), opts.select, eo[:associations], eo).all do |assoc_record|
|
|
845
853
|
hash_key = uses_cks ? opts.primary_keys.map{|k| assoc_record.send(k)} : assoc_record.send(opts.primary_key)
|
|
846
854
|
next unless objects = h[hash_key]
|
|
847
855
|
objects.each{|object| object.associations[name] = assoc_record}
|
|
@@ -863,8 +871,8 @@ module Sequel
|
|
|
863
871
|
|
|
864
872
|
return if opts[:read_only]
|
|
865
873
|
|
|
866
|
-
association_module_private_def(opts._setter_method){|o| cks.zip(opts.primary_keys).each{|k, pk| send(:"#{k}=", (o.send(pk) if o))}}
|
|
867
|
-
association_module_def(opts.setter_method){|o| set_associated_object(opts, o)}
|
|
874
|
+
association_module_private_def(opts._setter_method, opts){|o| cks.zip(opts.primary_keys).each{|k, pk| send(:"#{k}=", (o.send(pk) if o))}}
|
|
875
|
+
association_module_def(opts.setter_method, opts){|o| set_associated_object(opts, o)}
|
|
868
876
|
end
|
|
869
877
|
|
|
870
878
|
# Adds one_to_many association instance methods
|
|
@@ -891,7 +899,7 @@ module Sequel
|
|
|
891
899
|
end
|
|
892
900
|
reciprocal = opts.reciprocal
|
|
893
901
|
klass = opts.associated_class
|
|
894
|
-
model.eager_loading_dataset(opts, klass.filter(uses_cks ? {cks.map{|k| SQL::QualifiedIdentifier.new(klass.table_name, k)}=>
|
|
902
|
+
model.eager_loading_dataset(opts, klass.filter(uses_cks ? {cks.map{|k| SQL::QualifiedIdentifier.new(klass.table_name, k)}=>h.keys} : {SQL::QualifiedIdentifier.new(klass.table_name, key)=>h.keys}), opts.select, eo[:associations], eo).all do |assoc_record|
|
|
895
903
|
hash_key = uses_cks ? cks.map{|k| assoc_record.send(k)} : assoc_record.send(key)
|
|
896
904
|
next unless objects = h[hash_key]
|
|
897
905
|
if one_to_one
|
|
@@ -931,7 +939,7 @@ module Sequel
|
|
|
931
939
|
validate = opts[:validate]
|
|
932
940
|
|
|
933
941
|
if one_to_one
|
|
934
|
-
association_module_private_def(opts._setter_method) do |o|
|
|
942
|
+
association_module_private_def(opts._setter_method, opts) do |o|
|
|
935
943
|
up_ds = _apply_association_options(opts, opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)})))
|
|
936
944
|
if o
|
|
937
945
|
up_ds = up_ds.exclude(o.pk_hash)
|
|
@@ -942,19 +950,19 @@ module Sequel
|
|
|
942
950
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save") if o
|
|
943
951
|
end
|
|
944
952
|
end
|
|
945
|
-
association_module_def(opts.setter_method){|o| set_one_to_one_associated_object(opts, o)}
|
|
953
|
+
association_module_def(opts.setter_method, opts){|o| set_one_to_one_associated_object(opts, o)}
|
|
946
954
|
else
|
|
947
|
-
association_module_private_def(opts._add_method) do |o|
|
|
955
|
+
association_module_private_def(opts._add_method, opts) do |o|
|
|
948
956
|
cks.zip(cpks).each{|k, pk| o.send(:"#{k}=", send(pk))}
|
|
949
957
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
|
950
958
|
end
|
|
951
959
|
def_add_method(opts)
|
|
952
960
|
|
|
953
|
-
association_module_private_def(opts._remove_method) do |o|
|
|
961
|
+
association_module_private_def(opts._remove_method, opts) do |o|
|
|
954
962
|
cks.each{|k| o.send(:"#{k}=", nil)}
|
|
955
963
|
o.save(:validate=>validate) || raise(Sequel::Error, "invalid associated object, cannot save")
|
|
956
964
|
end
|
|
957
|
-
association_module_private_def(opts._remove_all_method) do
|
|
965
|
+
association_module_private_def(opts._remove_all_method, opts) do
|
|
958
966
|
_apply_association_options(opts, opts.associated_class.filter(cks.zip(cpks.map{|k| send(k)}))).update(ck_nil_hash)
|
|
959
967
|
end
|
|
960
968
|
def_remove_methods(opts)
|
|
@@ -969,8 +977,8 @@ module Sequel
|
|
|
969
977
|
|
|
970
978
|
# Add the remove_ and remove_all instance methods
|
|
971
979
|
def def_remove_methods(opts)
|
|
972
|
-
association_module_def(opts.remove_method){|o,*args| remove_associated_object(opts, o, *args)}
|
|
973
|
-
association_module_def(opts.remove_all_method){|*args| remove_all_associated_objects(opts, *args)}
|
|
980
|
+
association_module_def(opts.remove_method, opts){|o,*args| remove_associated_object(opts, o, *args)}
|
|
981
|
+
association_module_def(opts.remove_all_method, opts){|*args| remove_all_associated_objects(opts, *args)}
|
|
974
982
|
end
|
|
975
983
|
end
|
|
976
984
|
|
data/lib/sequel/model/base.rb
CHANGED
|
@@ -175,8 +175,8 @@ module Sequel
|
|
|
175
175
|
|
|
176
176
|
# Like find but invokes create with given conditions when record does not
|
|
177
177
|
# exist.
|
|
178
|
-
def find_or_create(cond)
|
|
179
|
-
find(cond) || create(cond)
|
|
178
|
+
def find_or_create(cond, &block)
|
|
179
|
+
find(cond) || create(cond, &block)
|
|
180
180
|
end
|
|
181
181
|
|
|
182
182
|
# If possible, set the dataset for the model subclass as soon as it
|
data/lib/sequel/model/plugins.rb
CHANGED
|
@@ -59,8 +59,13 @@ module Sequel
|
|
|
59
59
|
Sequel::Plugins.const_get(module_name) == Sequel.const_get(module_name))
|
|
60
60
|
begin
|
|
61
61
|
Sequel.tsk_require "sequel/plugins/#{plugin}"
|
|
62
|
-
rescue LoadError
|
|
63
|
-
|
|
62
|
+
rescue LoadError => e
|
|
63
|
+
begin
|
|
64
|
+
Sequel.tsk_require "sequel_#{plugin}"
|
|
65
|
+
rescue LoadError => e2
|
|
66
|
+
e.message << "; #{e2.message}"
|
|
67
|
+
raise e
|
|
68
|
+
end
|
|
64
69
|
end
|
|
65
70
|
end
|
|
66
71
|
Sequel::Plugins.const_get(module_name)
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
require 'active_model'
|
|
2
2
|
module Sequel
|
|
3
3
|
module Plugins
|
|
4
|
-
# The ActiveModel plugin makes Sequel::Model objects
|
|
4
|
+
# The ActiveModel plugin makes Sequel::Model objects
|
|
5
5
|
# pass the ActiveModel::Lint tests, which should
|
|
6
6
|
# hopefully mean full ActiveModel compliance. This should
|
|
7
7
|
# allow the full support of Sequel::Model objects in Rails 3.
|
|
@@ -39,13 +39,13 @@ module Sequel
|
|
|
39
39
|
|
|
40
40
|
# Define a association_pks method using the block for the association reflection
|
|
41
41
|
def def_association_pks_getter(opts, &block)
|
|
42
|
-
association_module_def(:"#{singularize(opts[:name])}_pks", &block)
|
|
42
|
+
association_module_def(:"#{singularize(opts[:name])}_pks", opts, &block)
|
|
43
43
|
end
|
|
44
44
|
|
|
45
45
|
# Define a association_pks= method using the block for the association reflection,
|
|
46
46
|
# if the association is not read only.
|
|
47
47
|
def def_association_pks_setter(opts, &block)
|
|
48
|
-
association_module_def(:"#{singularize(opts[:name])}_pks=", &block) unless opts[:read_only]
|
|
48
|
+
association_module_def(:"#{singularize(opts[:name])}_pks=", opts, &block) unless opts[:read_only]
|
|
49
49
|
end
|
|
50
50
|
|
|
51
51
|
# Add a getter that checks the join table for matching records and
|
|
@@ -41,7 +41,7 @@ module Sequel
|
|
|
41
41
|
# Changes the association method to return a proxy instead of the associated objects
|
|
42
42
|
# directly.
|
|
43
43
|
def def_association_method(opts)
|
|
44
|
-
opts.returns_array? ? association_module_def(opts.association_method){|*r| AssociationProxy.new(self, opts, r[0])} : super
|
|
44
|
+
opts.returns_array? ? association_module_def(opts.association_method, opts){|*r| AssociationProxy.new(self, opts, r[0])} : super
|
|
45
45
|
end
|
|
46
46
|
end
|
|
47
47
|
end
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
module Sequel
|
|
2
2
|
module Plugins
|
|
3
|
-
# The
|
|
4
|
-
# for boolean columns, which
|
|
3
|
+
# The BooleanReaders plugin allows for the creation of attribute? methods
|
|
4
|
+
# for boolean columns, which provides a nicer API. By default, the accessors
|
|
5
5
|
# are created for all columns of type :boolean. However, you can provide a
|
|
6
6
|
# block to the plugin to change the criteria used to determine if a
|
|
7
7
|
# column is boolean. The block is yielded with the column symbol for each
|
|
@@ -89,7 +89,11 @@ module Sequel
|
|
|
89
89
|
@cti_tables = [table_name]
|
|
90
90
|
@cti_columns = {table_name=>columns}
|
|
91
91
|
@cti_table_map = opts[:table_map] || {}
|
|
92
|
-
dataset.row_proc =
|
|
92
|
+
dataset.row_proc = if key
|
|
93
|
+
lambda{|r| (m.call(r[key]) rescue model).load(r)}
|
|
94
|
+
else
|
|
95
|
+
lambda{|r| model.load(r)}
|
|
96
|
+
end
|
|
93
97
|
end
|
|
94
98
|
end
|
|
95
99
|
|
|
@@ -147,7 +151,11 @@ module Sequel
|
|
|
147
151
|
super
|
|
148
152
|
subclass.instance_eval do
|
|
149
153
|
m = method(:constantize)
|
|
150
|
-
dataset.row_proc =
|
|
154
|
+
dataset.row_proc = if cti_key
|
|
155
|
+
lambda{|r| (m.call(r[ck]) rescue subclass).load(r)}
|
|
156
|
+
else
|
|
157
|
+
lambda{|r| subclass.load(r)}
|
|
158
|
+
end
|
|
151
159
|
(columns - [cbm.primary_key]).each{|a| define_lazy_attribute_getter(a)}
|
|
152
160
|
cti_tables.reverse.each do |table|
|
|
153
161
|
db.schema(table).each{|k,v| db_schema[k] = v}
|
|
@@ -10,17 +10,17 @@ module Sequel
|
|
|
10
10
|
# Album.filter{(id > 0) & (id < 2)}.first.object_id == Album.first(:id=>1).object_id
|
|
11
11
|
# end
|
|
12
12
|
#
|
|
13
|
-
# In
|
|
13
|
+
# In addition to providing a 1-1 correspondence, the identity_map plugin
|
|
14
14
|
# also provides a cached looked up of records in two cases:
|
|
15
15
|
# * Model.[] (e.g. Album[1])
|
|
16
16
|
# * Model.many_to_one accessor methods (e.g. album.artist)
|
|
17
17
|
#
|
|
18
|
-
# If the object you are looking up using one of those two methods is already
|
|
18
|
+
# If the object you are looking up, using one of those two methods, is already
|
|
19
19
|
# in the identity map, the record is returned without a database query being
|
|
20
20
|
# issued.
|
|
21
21
|
#
|
|
22
22
|
# Identity maps are thread-local and only presist for the duration of the block,
|
|
23
|
-
# so they should
|
|
23
|
+
# so they should only be considered as a possible performance enhancer.
|
|
24
24
|
#
|
|
25
25
|
# Usage:
|
|
26
26
|
#
|
|
@@ -2,7 +2,7 @@ module Sequel
|
|
|
2
2
|
module Plugins
|
|
3
3
|
# The instance_hooks plugin allows you to add hooks to specific instances,
|
|
4
4
|
# by passing a block to a _hook method (e.g. before_save_hook{do_something}).
|
|
5
|
-
# The block executed when the hook is called (e.g. before_save).
|
|
5
|
+
# The block is executed when the hook is called (e.g. before_save).
|
|
6
6
|
#
|
|
7
7
|
# All of the standard hooks are supported, except for after_initialize.
|
|
8
8
|
# Instance level before hooks are executed in reverse order of addition before
|
|
@@ -0,0 +1,212 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
tsk_require 'json'
|
|
3
|
+
|
|
4
|
+
module Plugins
|
|
5
|
+
# The json_serializer plugin handles serializing entire Sequel::Model
|
|
6
|
+
# objects to JSON, as well as support for deserializing JSON directly
|
|
7
|
+
# into Sequel::Model objects. It requires the json library, and can
|
|
8
|
+
# work with either the pure ruby version or the C extension.
|
|
9
|
+
#
|
|
10
|
+
# Basic Example:
|
|
11
|
+
#
|
|
12
|
+
# album = Album[1]
|
|
13
|
+
# album.to_json
|
|
14
|
+
# # => '{"json_class"=>"Album","id"=>1,"name"=>"RF","artist_id"=>2}'
|
|
15
|
+
#
|
|
16
|
+
# In addition, you can provide options to control the JSON output:
|
|
17
|
+
#
|
|
18
|
+
# album.to_json(:only=>:name)
|
|
19
|
+
# album.to_json(:except=>[:id, :artist_id])
|
|
20
|
+
# # => '{"json_class"="Album","name"=>"RF"}'
|
|
21
|
+
#
|
|
22
|
+
# album.to_json(:include=>:artist)
|
|
23
|
+
# # => '{"json_class":"Album","id":1,"name":"RF","artist_id":2,
|
|
24
|
+
# "artist":{"json_class":"Artist","id":2,"name":"YJM"}}'
|
|
25
|
+
#
|
|
26
|
+
# You can use a hash value with <tt>:include</tt> to pass options
|
|
27
|
+
# to associations:
|
|
28
|
+
#
|
|
29
|
+
# album.to_json(:include=>{:artist=>{:only=>:name}})
|
|
30
|
+
# # => '{"json_class":"Album","id":1,"name":"RF","artist_id":2,
|
|
31
|
+
# "artist":{"json_class":"Artist","name":"YJM"}}'
|
|
32
|
+
#
|
|
33
|
+
# You can specify the <tt>:root</tt> option to nest the JSON under the
|
|
34
|
+
# name of the model:
|
|
35
|
+
#
|
|
36
|
+
# album.to_json(:root => true)
|
|
37
|
+
# # => '{"album":{"id":1,"name":"RF","artist_id":2}}'
|
|
38
|
+
#
|
|
39
|
+
# In addition to creating JSON, this plugin also enables Sequel::Model
|
|
40
|
+
# objects to be automatically created when JSON is parsed:
|
|
41
|
+
#
|
|
42
|
+
# json = album.to_json
|
|
43
|
+
# album = JSON.parse(json)
|
|
44
|
+
#
|
|
45
|
+
# In addition, you can update existing model objects directly from JSON
|
|
46
|
+
# using +from_json+:
|
|
47
|
+
#
|
|
48
|
+
# album.from_json(json)
|
|
49
|
+
#
|
|
50
|
+
# This works by parsing the JSON (which should return a hash), and then
|
|
51
|
+
# calling +set+ with the returned hash.
|
|
52
|
+
#
|
|
53
|
+
# Additionally, +to_json+ also exists as a class and dataset method, both
|
|
54
|
+
# of which return all objects in the dataset:
|
|
55
|
+
#
|
|
56
|
+
# Album.to_json
|
|
57
|
+
# Album.filter(:artist_id=>1).to_json(:include=>:tags)
|
|
58
|
+
#
|
|
59
|
+
# Usage:
|
|
60
|
+
#
|
|
61
|
+
# # Add JSON output capability to all model subclass instances (called before loading subclasses)
|
|
62
|
+
# Sequel::Model.plugin :json_serializer
|
|
63
|
+
#
|
|
64
|
+
# # Add JSON output capability to Album class instances
|
|
65
|
+
# Album.plugin :json_serializer
|
|
66
|
+
module JsonSerializer
|
|
67
|
+
# Set up the column readers to do deserialization and the column writers
|
|
68
|
+
# to save the value in deserialized_values.
|
|
69
|
+
def self.configure(model, opts={})
|
|
70
|
+
model.instance_eval do
|
|
71
|
+
@json_serializer_opts = (@json_serializer_opts || {}).merge(opts)
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Helper class used for making sure that cascading options
|
|
76
|
+
# for model associations works correctly. Cascaded options
|
|
77
|
+
# work by creating instances of this class, which take a
|
|
78
|
+
# literal JSON string and have +to_json+ return it.
|
|
79
|
+
class Literal
|
|
80
|
+
# Store the literal JSON to use
|
|
81
|
+
def initialize(json)
|
|
82
|
+
@json = json
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# Return the literal JSON to use
|
|
86
|
+
def to_json(*a)
|
|
87
|
+
@json
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
module ClassMethods
|
|
92
|
+
# The default opts to use when serializing model objects to JSON.
|
|
93
|
+
attr_reader :json_serializer_opts
|
|
94
|
+
|
|
95
|
+
# Create a new model object from the hash provided by parsing
|
|
96
|
+
# JSON. Handles column values (stored in +values+), associations
|
|
97
|
+
# (stored in +associations+), and other values (by calling a
|
|
98
|
+
# setter method). If an entry in the hash is not a column or
|
|
99
|
+
# an association, and no setter method exists, raises an Error.
|
|
100
|
+
def json_create(hash)
|
|
101
|
+
obj = new
|
|
102
|
+
cols = columns.map{|x| x.to_s}
|
|
103
|
+
assocs = associations.map{|x| x.to_s}
|
|
104
|
+
meths = obj.send(:setter_methods, nil, nil)
|
|
105
|
+
hash.delete(JSON.create_id)
|
|
106
|
+
hash.each do |k, v|
|
|
107
|
+
if assocs.include?(k)
|
|
108
|
+
obj.associations[k.to_sym] = v
|
|
109
|
+
elsif cols.include?(k)
|
|
110
|
+
obj.values[k.to_sym] = v
|
|
111
|
+
elsif meths.include?("#{k}=")
|
|
112
|
+
obj.send("#{k}=", v)
|
|
113
|
+
else
|
|
114
|
+
raise Error, "Entry in JSON hash not an association or column and no setter method exists: #{k}"
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
obj
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
# Call the dataset +to_json+ method.
|
|
121
|
+
def to_json(*a)
|
|
122
|
+
dataset.to_json(*a)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Copy the current model object's default json options into the subclass.
|
|
126
|
+
def inherited(subclass)
|
|
127
|
+
super
|
|
128
|
+
opts = {}
|
|
129
|
+
json_serializer_opts.each{|k, v| opts[k] = (v.is_a?(Array) || v.is_a?(Hash)) ? v.dup : v}
|
|
130
|
+
subclass.instance_variable_set(:@json_serializer_opts, opts)
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
module InstanceMethods
|
|
135
|
+
# Parse the provided JSON, which should return a hash,
|
|
136
|
+
# and call +set+ with that hash.
|
|
137
|
+
def from_json(json)
|
|
138
|
+
set(JSON.parse(json))
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
# Return a string in JSON format. Accepts the following
|
|
142
|
+
# options:
|
|
143
|
+
#
|
|
144
|
+
# :except :: Symbol or Array of Symbols of columns not
|
|
145
|
+
# to include in the JSON output.
|
|
146
|
+
# :include :: Symbol, Array of Symbols, or a Hash with
|
|
147
|
+
# Symbol keys and Hash values specifying
|
|
148
|
+
# associations or other non-column attributes
|
|
149
|
+
# to include in the JSON output. Using a nested
|
|
150
|
+
# hash, you can pass options to associations
|
|
151
|
+
# to affect the JSON used for associated objects.
|
|
152
|
+
# :naked :: Not to add the JSON.create_id key to the JSON
|
|
153
|
+
# output hash, so when the JSON is parsed, it
|
|
154
|
+
# will yield a hash instead of a model object.
|
|
155
|
+
# :only :: Symbol or Array of Symbols of columns to only
|
|
156
|
+
# include in the JSON output, ignoring all other
|
|
157
|
+
# columns.
|
|
158
|
+
# :root :: Qualify the JSON with the name of the object.
|
|
159
|
+
# Implies :naked since the object name is explicit.
|
|
160
|
+
def to_json(*a)
|
|
161
|
+
if opts = a.first.is_a?(Hash)
|
|
162
|
+
opts = model.json_serializer_opts.merge(a.first)
|
|
163
|
+
a = []
|
|
164
|
+
else
|
|
165
|
+
opts = model.json_serializer_opts
|
|
166
|
+
end
|
|
167
|
+
vals = values
|
|
168
|
+
cols = if only = opts[:only]
|
|
169
|
+
Array(only)
|
|
170
|
+
else
|
|
171
|
+
vals.keys - Array(opts[:except])
|
|
172
|
+
end
|
|
173
|
+
h = (JSON.create_id && !opts[:naked] && !opts[:root]) ? {JSON.create_id=>model.name} : {}
|
|
174
|
+
cols.each{|c| h[c.to_s] = vals[c]}
|
|
175
|
+
if inc = opts[:include]
|
|
176
|
+
if inc.is_a?(Hash)
|
|
177
|
+
inc.each do |k, v|
|
|
178
|
+
v = v.empty? ? [] : [v]
|
|
179
|
+
h[k.to_s] = case objs = send(k)
|
|
180
|
+
when Array
|
|
181
|
+
objs.map{|obj| Literal.new(obj.to_json(*v))}
|
|
182
|
+
else
|
|
183
|
+
Literal.new(objs.to_json(*v))
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
else
|
|
187
|
+
Array(inc).each{|c| h[c.to_s] = send(c)}
|
|
188
|
+
end
|
|
189
|
+
end
|
|
190
|
+
h = {model.send(:underscore, model.to_s) => h} if opts[:root]
|
|
191
|
+
h.to_json(*a)
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
module DatasetMethods
|
|
196
|
+
# Return a JSON string representing an array of all objects in
|
|
197
|
+
# this dataset. Takes the same options as the the instance
|
|
198
|
+
# method, and passes them to every instance.
|
|
199
|
+
def to_json(*a)
|
|
200
|
+
if opts = a.first.is_a?(Hash)
|
|
201
|
+
opts = model.json_serializer_opts.merge(a.first)
|
|
202
|
+
a = []
|
|
203
|
+
else
|
|
204
|
+
opts = model.json_serializer_opts
|
|
205
|
+
end
|
|
206
|
+
res = row_proc ? all.map{|obj| Literal.new(obj.to_json(opts))} : all
|
|
207
|
+
opts[:root] ? {model.send(:pluralize, model.send(:underscore, model.to_s)) => res}.to_json(*a) : res.to_json(*a)
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|