sequel 5.83.1 → 5.84.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  3. data/lib/sequel/database/schema_methods.rb +2 -0
  4. data/lib/sequel/extensions/pg_json_ops.rb +328 -1
  5. data/lib/sequel/sql.rb +8 -5
  6. data/lib/sequel/version.rb +2 -2
  7. metadata +2 -236
  8. data/CHANGELOG +0 -1397
  9. data/README.rdoc +0 -936
  10. data/doc/advanced_associations.rdoc +0 -884
  11. data/doc/association_basics.rdoc +0 -1859
  12. data/doc/bin_sequel.rdoc +0 -146
  13. data/doc/cheat_sheet.rdoc +0 -255
  14. data/doc/code_order.rdoc +0 -104
  15. data/doc/core_extensions.rdoc +0 -405
  16. data/doc/dataset_basics.rdoc +0 -96
  17. data/doc/dataset_filtering.rdoc +0 -222
  18. data/doc/extensions.rdoc +0 -77
  19. data/doc/fork_safety.rdoc +0 -84
  20. data/doc/mass_assignment.rdoc +0 -98
  21. data/doc/migration.rdoc +0 -660
  22. data/doc/model_dataset_method_design.rdoc +0 -129
  23. data/doc/model_hooks.rdoc +0 -254
  24. data/doc/model_plugins.rdoc +0 -270
  25. data/doc/mssql_stored_procedures.rdoc +0 -43
  26. data/doc/object_model.rdoc +0 -563
  27. data/doc/opening_databases.rdoc +0 -439
  28. data/doc/postgresql.rdoc +0 -611
  29. data/doc/prepared_statements.rdoc +0 -144
  30. data/doc/querying.rdoc +0 -1070
  31. data/doc/reflection.rdoc +0 -120
  32. data/doc/release_notes/5.0.0.txt +0 -159
  33. data/doc/release_notes/5.1.0.txt +0 -31
  34. data/doc/release_notes/5.10.0.txt +0 -84
  35. data/doc/release_notes/5.11.0.txt +0 -83
  36. data/doc/release_notes/5.12.0.txt +0 -141
  37. data/doc/release_notes/5.13.0.txt +0 -27
  38. data/doc/release_notes/5.14.0.txt +0 -63
  39. data/doc/release_notes/5.15.0.txt +0 -39
  40. data/doc/release_notes/5.16.0.txt +0 -110
  41. data/doc/release_notes/5.17.0.txt +0 -31
  42. data/doc/release_notes/5.18.0.txt +0 -69
  43. data/doc/release_notes/5.19.0.txt +0 -28
  44. data/doc/release_notes/5.2.0.txt +0 -33
  45. data/doc/release_notes/5.20.0.txt +0 -89
  46. data/doc/release_notes/5.21.0.txt +0 -87
  47. data/doc/release_notes/5.22.0.txt +0 -48
  48. data/doc/release_notes/5.23.0.txt +0 -56
  49. data/doc/release_notes/5.24.0.txt +0 -56
  50. data/doc/release_notes/5.25.0.txt +0 -32
  51. data/doc/release_notes/5.26.0.txt +0 -35
  52. data/doc/release_notes/5.27.0.txt +0 -21
  53. data/doc/release_notes/5.28.0.txt +0 -16
  54. data/doc/release_notes/5.29.0.txt +0 -22
  55. data/doc/release_notes/5.3.0.txt +0 -121
  56. data/doc/release_notes/5.30.0.txt +0 -20
  57. data/doc/release_notes/5.31.0.txt +0 -148
  58. data/doc/release_notes/5.32.0.txt +0 -46
  59. data/doc/release_notes/5.33.0.txt +0 -24
  60. data/doc/release_notes/5.34.0.txt +0 -40
  61. data/doc/release_notes/5.35.0.txt +0 -56
  62. data/doc/release_notes/5.36.0.txt +0 -60
  63. data/doc/release_notes/5.37.0.txt +0 -30
  64. data/doc/release_notes/5.38.0.txt +0 -28
  65. data/doc/release_notes/5.39.0.txt +0 -19
  66. data/doc/release_notes/5.4.0.txt +0 -80
  67. data/doc/release_notes/5.40.0.txt +0 -40
  68. data/doc/release_notes/5.41.0.txt +0 -25
  69. data/doc/release_notes/5.42.0.txt +0 -136
  70. data/doc/release_notes/5.43.0.txt +0 -98
  71. data/doc/release_notes/5.44.0.txt +0 -32
  72. data/doc/release_notes/5.45.0.txt +0 -34
  73. data/doc/release_notes/5.46.0.txt +0 -87
  74. data/doc/release_notes/5.47.0.txt +0 -59
  75. data/doc/release_notes/5.48.0.txt +0 -14
  76. data/doc/release_notes/5.49.0.txt +0 -59
  77. data/doc/release_notes/5.5.0.txt +0 -61
  78. data/doc/release_notes/5.50.0.txt +0 -78
  79. data/doc/release_notes/5.51.0.txt +0 -47
  80. data/doc/release_notes/5.52.0.txt +0 -87
  81. data/doc/release_notes/5.53.0.txt +0 -23
  82. data/doc/release_notes/5.54.0.txt +0 -27
  83. data/doc/release_notes/5.55.0.txt +0 -21
  84. data/doc/release_notes/5.56.0.txt +0 -51
  85. data/doc/release_notes/5.57.0.txt +0 -23
  86. data/doc/release_notes/5.58.0.txt +0 -31
  87. data/doc/release_notes/5.59.0.txt +0 -73
  88. data/doc/release_notes/5.6.0.txt +0 -31
  89. data/doc/release_notes/5.60.0.txt +0 -22
  90. data/doc/release_notes/5.61.0.txt +0 -43
  91. data/doc/release_notes/5.62.0.txt +0 -132
  92. data/doc/release_notes/5.63.0.txt +0 -33
  93. data/doc/release_notes/5.64.0.txt +0 -50
  94. data/doc/release_notes/5.65.0.txt +0 -21
  95. data/doc/release_notes/5.66.0.txt +0 -24
  96. data/doc/release_notes/5.67.0.txt +0 -32
  97. data/doc/release_notes/5.68.0.txt +0 -61
  98. data/doc/release_notes/5.69.0.txt +0 -26
  99. data/doc/release_notes/5.7.0.txt +0 -108
  100. data/doc/release_notes/5.70.0.txt +0 -35
  101. data/doc/release_notes/5.71.0.txt +0 -21
  102. data/doc/release_notes/5.72.0.txt +0 -33
  103. data/doc/release_notes/5.73.0.txt +0 -66
  104. data/doc/release_notes/5.74.0.txt +0 -45
  105. data/doc/release_notes/5.75.0.txt +0 -35
  106. data/doc/release_notes/5.76.0.txt +0 -86
  107. data/doc/release_notes/5.77.0.txt +0 -63
  108. data/doc/release_notes/5.78.0.txt +0 -67
  109. data/doc/release_notes/5.79.0.txt +0 -28
  110. data/doc/release_notes/5.8.0.txt +0 -170
  111. data/doc/release_notes/5.80.0.txt +0 -40
  112. data/doc/release_notes/5.81.0.txt +0 -31
  113. data/doc/release_notes/5.82.0.txt +0 -61
  114. data/doc/release_notes/5.83.0.txt +0 -56
  115. data/doc/release_notes/5.9.0.txt +0 -99
  116. data/doc/schema_modification.rdoc +0 -679
  117. data/doc/security.rdoc +0 -443
  118. data/doc/sharding.rdoc +0 -286
  119. data/doc/sql.rdoc +0 -648
  120. data/doc/testing.rdoc +0 -204
  121. data/doc/thread_safety.rdoc +0 -15
  122. data/doc/transactions.rdoc +0 -250
  123. data/doc/validations.rdoc +0 -558
  124. data/doc/virtual_rows.rdoc +0 -265
@@ -1,222 +0,0 @@
1
- = Dataset Filtering
2
-
3
- Sequel is very flexible when it comes to filtering records. You can specify your conditions as a hash of values to compare against, or as ruby code that Sequel translates into SQL expressions, or as an SQL code fragment (with optional parameters), .
4
-
5
- == Filtering using a hash
6
-
7
- If you just need to compare records against values, you can supply a hash:
8
-
9
- items.where(category: 'ruby').sql
10
- # "SELECT * FROM items WHERE (category = 'ruby')"
11
-
12
- Sequel can check for null values:
13
-
14
- items.where(category: nil).sql
15
- # "SELECT * FROM items WHERE (category IS NULL)"
16
-
17
- Or compare two columns:
18
-
19
- items.where{{x: some_table[:y]}}.sql
20
- # "SELECT * FROM items WHERE (x = some_table.y)"
21
-
22
- And also compare against multiple values:
23
-
24
- items.where(category: ['ruby', 'perl']).sql
25
- # "SELECT * FROM items WHERE (category IN ('ruby', 'perl'))"
26
-
27
- Ranges (both inclusive and exclusive) can also be used:
28
-
29
- items.where(price: 100..200).sql
30
- # "SELECT * FROM items WHERE (price >= 100 AND price <= 200)"
31
-
32
- items.where(price: 100...200).sql
33
- # "SELECT * FROM items WHERE (price >= 100 AND price < 200)"
34
-
35
- == Filtering using an array
36
-
37
- If you need to select multiple items from a dataset, you can supply an array:
38
-
39
- items.where(id: [1, 38, 47, 99]).sql
40
- # "SELECT * FROM items WHERE (id IN (1, 38, 47, 99))"
41
-
42
- == Filtering using expressions
43
-
44
- You can pass a block to where (referred to as a virtual row block), which is evaluated in a special context:
45
-
46
- items.where{price * 2 < 50}.sql
47
- # "SELECT * FROM items WHERE ((price * 2) < 50)
48
-
49
- This works for the standard inequality and arithmetic operators:
50
-
51
- items.where{price + 100 < 200}.sql
52
- # "SELECT * FROM items WHERE ((price + 100) < 200)
53
-
54
- items.where{price - 100 > 200}.sql
55
- # "SELECT * FROM items WHERE ((price - 100) > 200)
56
-
57
- items.where{price * 100 <= 200}.sql
58
- # "SELECT * FROM items WHERE ((price * 100) <= 200)
59
-
60
- items.where{price / 100 >= 200}.sql
61
- # "SELECT * FROM items WHERE ((price / 100) >= 200)
62
-
63
- items.where{price ** 2 >= 200}.sql
64
- # "SELECT * FROM items WHERE (power(price, 2) >= 200)
65
-
66
- You use the overloaded bitwise and (&) and or (|) operators to combine expressions:
67
-
68
- items.where{(price + 100 < 200) & (price * 100 <= 200)}.sql
69
- # "SELECT * FROM items WHERE (((price + 100) < 200) AND ((price * 100) <= 200))
70
-
71
- items.where{(price - 100 > 200) | (price / 100 >= 200)}.sql
72
- # "SELECT * FROM items WHERE (((price - 100) > 200) OR ((price / 100) >= 200))
73
-
74
- To filter by equality, you use the standard hash, which can be combined with other expressions using Sequel.& and Sequel.|:
75
-
76
- items.where{Sequel.&({category: 'ruby'}, (price + 100 < 200))}.sql
77
- # "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
78
-
79
- You can also use the =~ operator:
80
-
81
- items.where{(category =~ 'ruby') & (price + 100 < 200)}.sql
82
- # "SELECT * FROM items WHERE ((category = 'ruby') AND ((price + 100) < 200))"
83
-
84
- This works with other hash values, such as arrays and ranges:
85
-
86
- items.where{Sequel.|({category: ['ruby', 'other']}, (price - 100 > 200))}.sql
87
- # "SELECT * FROM items WHERE ((category IN ('ruby', 'other')) OR ((price - 100) > 200))"
88
-
89
- items.where{(price =~ (100..200)) & :active}.sql
90
- # "SELECT * FROM items WHERE ((price >= 100 AND price <= 200) AND active)"
91
-
92
- == Filtering using a custom filter string
93
-
94
- If you wish to include an SQL fragment as part of a filter, you need to wrap it with +Sequel.lit+ to mark that it is literal SQL code, and pass it to the #where method:
95
-
96
- items.where(Sequel.lit('x < 10')).sql
97
- # "SELECT * FROM items WHERE x < 10"
98
-
99
- In order to prevent SQL injection, you can replace literal values with question marks and supply the values as additional arguments to +Sequel.lit+:
100
-
101
- items.where(Sequel.lit('category = ?', 'ruby')).sql
102
- # "SELECT * FROM items WHERE category = 'ruby'"
103
-
104
- You can also use placeholders with :placeholder and a hash of placeholder values:
105
-
106
- items.where(Sequel.lit('category = :category', category: "ruby")).sql
107
- # "SELECT * FROM items WHERE category = 'ruby'"
108
-
109
- In order to combine AND and OR together, you have a few options:
110
-
111
- items.where(category: nil).or(category: "ruby")
112
- # SELECT * FROM items WHERE (category IS NULL) OR (category = 'ruby')
113
-
114
- This won't work if you add other conditions:
115
-
116
- items.where(name: "Programming in Ruby").where(category: nil).or(category: 'ruby')
117
- # SELECT * FROM items WHERE ((name = 'Programming in Ruby') AND (category IS NULL)) OR (category = 'ruby')
118
-
119
- The OR applies globally and not locally. To fix this, use & and |:
120
-
121
- items.where(Sequel[name: "Programming in Ruby"] & (Sequel[category: nil] | Sequel[category: "ruby"]))
122
- # SELECT * FROM items WHERE ((name = 'Programming in Ruby') AND ((category IS NULL) OR (category = 'ruby')))
123
-
124
- === Specifying SQL functions
125
-
126
- Sequel also allows you to specify functions by using the Sequel.function method:
127
-
128
- items.literal(Sequel.function(:avg, :price)) # "avg(price)"
129
-
130
- If you are specifying a filter/selection/order, you can use a virtual row block:
131
-
132
- items.select{avg(price)}
133
-
134
- === Negating conditions
135
-
136
- You can use the exclude method to exclude whole conditions:
137
-
138
- items.exclude(category: 'ruby').sql
139
- # "SELECT * FROM items WHERE (category != 'ruby')"
140
-
141
- items.exclude(:active).sql
142
- # "SELECT * FROM items WHERE NOT active"
143
-
144
- items.exclude{price / 100 >= 200}.sql
145
- # "SELECT * FROM items WHERE ((price / 100) < 200)
146
-
147
- To exclude only parts of conditions, you can use when in combination with Sequel.~ or the ~ method on Sequel expressions:
148
-
149
- items.where{Sequel.&(Sequel.~(category: 'ruby'), (price + 100 < 200))}.sql
150
- # "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
151
-
152
- items.where{~(category =~ 'ruby') & (price + 100 < 200)}.sql
153
- # "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
154
-
155
- You can also use the !~ method:
156
-
157
- items.where{(category !~ 'ruby') & (price + 100 < 200)}.sql
158
- # "SELECT * FROM items WHERE ((category != 'ruby') AND ((price + 100) < 200))"
159
-
160
- === Comparing against column references
161
-
162
- You can also compare against other columns:
163
-
164
- items.where{credit > debit}.sql
165
- # "SELECT * FROM items WHERE (credit > debit)
166
-
167
- Or against SQL functions:
168
-
169
- items.where{price - 100 < max(price)}.sql
170
- # "SELECT * FROM items WHERE ((price - 100) < max(price))"
171
-
172
- == String search functions
173
-
174
- You can search SQL strings in a case sensitive manner using the Sequel.like method:
175
-
176
- items.where(Sequel.like(:name, 'Acme%')).sql
177
- # "SELECT * FROM items WHERE (name LIKE 'Acme%' ESCAPE '\')"
178
-
179
- You can search SQL strings in a case insensitive manner using the Sequel.ilike method:
180
-
181
- items.where(Sequel.ilike(:name, 'Acme%')).sql
182
- # "SELECT * FROM items WHERE (name ILIKE 'Acme%' ESCAPE '\')"
183
-
184
- You can specify a Regexp as a hash value (or like argument), but this will probably only work
185
- on PostgreSQL and MySQL:
186
-
187
- items.where(name: /Acme.*/).sql
188
- # "SELECT * FROM items WHERE (name ~ 'Acme.*')"
189
-
190
- Like can also take more than one argument:
191
-
192
- items.where(Sequel.like(:name, 'Acme%', /Beta.*/)).sql
193
- # "SELECT * FROM items WHERE ((name LIKE 'Acme%' ESCAPE '\') OR (name ~ 'Beta.*'))"
194
-
195
- == String concatenation
196
-
197
- You can concatenate SQL strings using Sequel.join:
198
-
199
- items.where(Sequel.join([:name, :comment]).like('Jo%nice%')).sql
200
- # "SELECT * FROM items WHERE ((name || comment) LIKE 'Jo%nice%' ESCAPE '\')"
201
-
202
- Sequel.join also takes a join argument:
203
-
204
- items.where(Sequel.join([:name, :comment], ':').like('John:%nice%')).sql
205
- # "SELECT * FROM items WHERE ((name || ':' || comment) LIKE 'John:%nice%' ESCAPE '\')"
206
-
207
- == Filtering using sub-queries
208
-
209
- Datasets can be used as subqueries. Subqueries can be very useful for filtering records, and many times provide a simpler alternative to table joins. Subqueries can be used in all forms of filters:
210
-
211
- refs = consumer_refs.where(:logged_in).select(:consumer_id)
212
- consumers.where(id: refs).sql
213
- # "SELECT * FROM consumers WHERE (id IN (SELECT consumer_id FROM consumer_refs WHERE logged_in))"
214
-
215
- Note that if you are checking for the inclusion of a single column in a subselect, the subselect should only select a single column.
216
-
217
- == Using OR instead of AND
218
-
219
- By default, if you chain calls to +where+, the conditions get ANDed together. If you want to use an OR for a condition, you can use the +or+ method:
220
-
221
- items.where(name: 'Food').or(vendor: 1).sql
222
- # "SELECT * FROM items WHERE ((name = 'Food') OR (vendor = 1))"
data/doc/extensions.rdoc DELETED
@@ -1,77 +0,0 @@
1
- = Sequel Extensions
2
-
3
- Sequel has an official extension system, for adding global, Database, and Dataset extensions.
4
-
5
- == Global Extensions
6
-
7
- Global extensions can add or modify the behavior of any part of Sequel. Technically, they are not limited to affecting Sequel, as they can also modify code outside of Sequel (e.g. the blank extension). However, extensions that modify things outside of Sequel generally do so only for backwards compatibility.
8
-
9
- Global extensions are loaded via <tt>Sequel.extension</tt>:
10
-
11
- Sequel.extension :named_timezones
12
-
13
- All this does is require the relevent extension from <tt>sequel/extensions/named_timezones</tt> somewhere in the ruby path. Global extensions are just a simpler, consistent way to require code that modifies Sequel.
14
-
15
- == Database Extensions
16
-
17
- Database extensions should add or modify the behavior of a single <tt>Sequel::Database</tt> instance. They are loaded via <tt>Sequel::Database#extension</tt>:
18
-
19
- DB.extension :server_block
20
-
21
- The first thing that this does is load the relevent extension globally. However, Database extensions should be structured in a way that loading the relevent extension globally just adds a module with the related behavior, it doesn't modify any other state. After loading the extension globally, it modifies the related <tt>Sequel::Database</tt> object to modify it's behavior, usually by extending it with a module.
22
-
23
- If you want a Database extension loaded into all future Database instances, you can use <tt>Sequel::Database.extension</tt>:
24
-
25
- Sequel::Database.extension :server_block
26
-
27
- All future <tt>Sequel::Database</tt> instances created afterward will then automatically have the server_block extension loaded.
28
-
29
- == Dataset Extensions
30
-
31
- Dataset extensions should add or modify the behavior of a single <tt>Sequel::Dataset</tt> instance. They are loaded via <tt>Sequel::Dataset#extension</tt>. <tt>Sequel::Dataset#extension</tt> returns a modifies copy of the dataset that includes the extension (similar to how most dataset query methods work):
32
-
33
- ds = DB[:a].extension(:columns_introspection)
34
-
35
- The first thing loading a Dataset extension does is load the relevent extension globally. Similar to Database extensions, loading a Dataset extension globally should not affect state other than maybe adding a module. After loading the extension globally, it returned a modified copy of the <tt>Sequel::Dataset</tt> with the extension loaded into it.
36
-
37
- If you want to load an extension into all future datasets for a given <tt>Sequel::Database</tt> instance, you can also load it as a Database extension:
38
-
39
- DB.extension :columns_introspection
40
-
41
- Likewise, if you want to load an extension into all future datasets for all future databases, you can load it via <tt>Sequel::Database.extension</tt>:
42
-
43
- Sequel::Database.extension :columns_introspection
44
-
45
- == Creating Global Extensions
46
-
47
- If you want to create a global extension, you just need to store your code so that you can require it via <tt>sequel/extensions/extension_name</tt>. Then users can load it via:
48
-
49
- Sequel.extension :extension_name
50
-
51
- It is recommended you only create a global extension if what you want to do would not work as a Database or Dataset extension.
52
-
53
- == Creating Database Extensions
54
-
55
- Creating Database extensions is similar to global extensions in terms of creating the file. However, somewhere in the file, you need to call <tt>Sequel::Database.register_extension</tt>. Usually you would call this with the module that will be added to the related <tt>Sequel::Database</tt> instance when the extension is loaded. For example, the server_block extension uses something like:
56
-
57
- Sequel::Database.register_extension(:server_block, Sequel::ServerBlock)
58
-
59
- The first argument is the name of the extension as a symbol, and the second is the module.
60
-
61
- In some cases, just extending the <tt>Sequel::Database</tt> instance with a module is not sufficient. So <tt>Sequel::Database.register_extension</tt> also accepts a proc instead of a second argument. This proc is called with the <tt>Sequel::Database</tt> instance, and can then run any related code:
62
-
63
- Sequel::Database.register_extension(:arbitrary_servers){|db| db.pool.extend(Sequel::ArbitraryServers)}
64
-
65
- == Creating Dataset Extensions
66
-
67
- Creating Dataset extensions is very similar to creating Database extensions, but instead of calling <tt>Sequel::Database.register_extension</tt>, you call <tt>Sequel::Dataset.register_extension</tt>. In general, you would call this with the module that will be added to the related <tt>Sequel::Dataset</tt> instance when the extension is loaded. For example, the columns_introspection extension uses something like:
68
-
69
- Sequel::Dataset.register_extension(:columns_introspection, Sequel::ColumnsIntrospection)
70
-
71
- The first argument is the name of the extension as a symbol, and the second is the module. When you call the <tt>Sequel::Dataset.register_extension</tt> method with a module, it in turn calls <tt>Sequel::Database.register_extension</tt> and adds a Database extension that loads this Dataset extension into all future Datasets created from the Database.
72
-
73
- You can also call <tt>Sequel::Dataset.register_extension</tt> with a proc:
74
-
75
- Sequel::Dataset.register_extension(:extension_name){|ds| }
76
-
77
- Note that if you use a proc, a corresponding Database extension will not be created automatically (you can still call <tt>Sequel::Database.register_extension</tt> manually in this case).
data/doc/fork_safety.rdoc DELETED
@@ -1,84 +0,0 @@
1
- = Fork Safety
2
-
3
- If you are forking or using a library that forks after you have created a
4
- Sequel::Database instance, then you must disconnect database connections before forking. If you
5
- don't do this, you can end up with child processes sharing database connections
6
- and all sorts of weird behavior, including crashes. Sequel will automatically create new
7
- connections on an as needed basis in the child processes, so you only need to do the following in
8
- the parent process:
9
-
10
- DB.disconnect
11
-
12
- Or if you have connections to multiple databases:
13
-
14
- Sequel::DATABASES.each(&:disconnect)
15
-
16
- == Puma
17
-
18
- When using the Puma web server in clustered mode (which is the default behavior in Puma 5+ when
19
- using multiple processes), you should disconnect inside the +before_fork+ hook in your
20
- Puma config:
21
-
22
- before_fork do
23
- Sequel::DATABASES.each(&:disconnect)
24
- end
25
-
26
- == Unicorn
27
-
28
- When using the Unicorn web server and preloading the application (+preload_app true+ in the Unicorn
29
- config), you should disconnect inside the +before_fork+ hook in the Unicorn config:
30
-
31
- before_fork do
32
- Sequel::DATABASES.each(&:disconnect)
33
- end
34
-
35
- == Passenger
36
-
37
- In Passenger web server, you should disconnect inside the
38
- +starting_worker_process+ event hook:
39
-
40
- if defined?(PhusionPassenger)
41
- PhusionPassenger.on_event(:starting_worker_process) do |forked|
42
- Sequel::DATABASES.each(&:disconnect) if forked
43
- end
44
- end
45
-
46
- Note that this disconnects after forking instead of before forking. Passenger does not
47
- offer a before fork hook.
48
-
49
- == Spring
50
-
51
- In Spring application preloader, you should disconnect inside the +after_fork+ hook:
52
-
53
- if defined?(Spring)
54
- Spring.after_fork do
55
- Sequel::DATABASES.each(&:disconnect)
56
- end
57
- end
58
-
59
- As the method indicates, this disconnects after forking instead of before forking.
60
- Spring does not offer a before fork hook.
61
-
62
- == Resque
63
-
64
- In Resque, you should disconnect inside the +before_fork+ hook:
65
-
66
- Resque.before_fork do |job|
67
- Sequel::DATABASES.each(&:disconnect)
68
- end
69
-
70
- == Parallel
71
-
72
- If you're using the Parallel gem with processes, you should disconnect before
73
- calling it:
74
-
75
- Sequel::DATABASES.each(&:disconnect)
76
- Parallel.map(['a','b','c'], in_processes: 3) { |one_letter| }
77
-
78
- == Other Libraries Calling fork
79
-
80
- For any other library that calls fork, you should disconnect before calling
81
- a method that forks:
82
-
83
- Sequel::DATABASES.each(&:disconnect)
84
- SomeLibrary.method_that_forks
@@ -1,98 +0,0 @@
1
- = Sequel::Model Mass Assignment
2
-
3
- Most Model methods that take a hash of attribute keys and values, including <tt>Model.new</tt>,
4
- <tt>Model.create</tt>, <tt>Model#set</tt> and <tt>Model#update</tt> are subject to Sequel's mass assignment rules.
5
-
6
- If you have an instance of a plain Sequel::Model class:
7
-
8
- class Post < Sequel::Model
9
- end
10
- post = Post.new
11
-
12
- and you call a mass assignment method with a hash:
13
-
14
- post.set(title: 'T', body: 'B')
15
-
16
- the mass assignment method will go through each key in the hash, append <tt>=</tt> to it to determine the
17
- setter method, and if the setter method is defined and access to it is not restricted, Sequel will call the
18
- setter method with the hash value. So if we assume that the posts table has title and body columns, what
19
- the above mass assignment call actually does is:
20
-
21
- post.title=('T')
22
- post.body=('B')
23
-
24
- By default, there are two types of setter methods that are restricted.
25
- The first is methods like <tt>typecast_on_assignment=</tt> and <tt>==</tt>, which don't affect columns.
26
- These methods cannot be enabled for mass assignment.
27
- The second is primary key setters.
28
-
29
- So if you do:
30
-
31
- post = Post.new(id: 1)
32
-
33
- Sequel will raise a Sequel::MassAssignmentRestriction exception, since by default setting the primary key is not allowed.
34
-
35
- To enable use of primary key setters, you need to call +unrestrict_primary_key+ for that model:
36
-
37
- Post.unrestrict_primary_key
38
-
39
- If you want to change mass assignment so it ignores attempts to access restricted setter methods, you can do:
40
-
41
- # Global default
42
- Sequel::Model.strict_param_setting = false
43
- # Class level
44
- Post.strict_param_setting = false
45
- # Instance level
46
- post.strict_param_setting = false
47
-
48
- Since mass assignment by default allows modification of all column values except for primary key columns, it can be a security risk in some cases.
49
- If you are dealing with untrusted input, you are generally going to want to restrict what should be updated.
50
-
51
- Sequel has <tt>Model#set_fields</tt> and <tt>Model#update_fields</tt> methods, which are designed to be used with untrusted input.
52
- These methods take two arguments, the untrusted hash as the first argument, and a trusted array of field names as the second argument:
53
-
54
- post.set_fields({title: 'T', body: 'B'}, [:title, :body])
55
-
56
- Instead of looking at every key in the untrusted hash, +set_fields+ will iterate over the trusted field names, looking each up in the hash, and
57
- calling the setter method appropriately with the result. +set_fields+ basically translates the above method call to:
58
-
59
- post.title=('T')
60
- post.body=('B')
61
-
62
- By using this method, you can be sure that the mass assignment method only sets the fields you expect it to set.
63
-
64
- Note that if one of the fields does not exist in the hash:
65
-
66
- post.set_fields({title: 'T'}, [:title, :body])
67
-
68
- +set_fields+ will set the value to nil (the default hash value) by default, with behavior equivalent to:
69
-
70
- post.title=('T')
71
- post.body=(nil)
72
-
73
- You can use the :missing option to +set_fields+ to change the behavior:
74
-
75
- post.set_fields({title: 'T'}, [:title, :body], missing: :skip)
76
- # post.title=('T') # only
77
-
78
- post.set_fields({title: 'T'}, [:title, :body], missing: :raise)
79
- # raises Sequel::Error
80
-
81
- If you want to set a model level default for the +set_fields+ options, you can use the +default_set_fields_options+ class accessor:
82
-
83
- # Global default
84
- Sequel::Model.default_set_fields_options[:missing] = :skip
85
- # Class level
86
- Post.default_set_fields_options[:missing] = :skip
87
-
88
- Here's a table describing Sequel's default mass assignment methods:
89
-
90
- Model.new(hash) :: Creates a new model instance, then calls Model#set(hash)
91
- Model.create(hash) :: Calls Model.new(hash).save
92
- Model#set(hash) :: Calls related setter method (unless access is restricted) for each key in the hash, then returns self
93
- Model#update(hash) :: Calls set(hash).save_changes
94
- Model#set_fields(hash, columns, options) :: For each column in columns, looks up related entry in hash, and calls the related setter method
95
- Model#update_fields(hash, columns, options) :: Calls set_fields(hash, columns, options).save_changes
96
-
97
- For backwards compatibility, Sequel also ships with a whitelist_security and blacklist_security plugins that offer additional mass assignment
98
- methods, but it is recommended to use +set_fields+ or +update_fields+ for untrusted input, and the other methods for trusted input.