activerecord-sort 7.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +72 -1
- data/README.md +31 -8
- data/ext/active_record/base.rb +113 -64
- data/lib/active_record/sort/version.rb +1 -1
- metadata +13 -13
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 50d689663cb6717dab8e034f88d8645cf24a31489e322ca79d9cd52634281dc4
|
|
4
|
+
data.tar.gz: 544297bd2fc5b8322f8a719d3a4f52e235681a1c756dc191ac74fd69a4c556a7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: b0caa63a30b528ed30f2fa2ff5c29fe779fd633e9d30ec4404ede2c8b08f9a24a1832b3bd4bd10c39914bebe07cfd8fe23b10ce93496b254d6bb81d2450a3c9e
|
|
7
|
+
data.tar.gz: afc2f2911285ed3e681a39013013ad1b9ffbce9161a4ac5bb2da6fda40ddd03421942f3b42eaf0c0a3865878fe667759d4c77df0b8962b431937267be40a9d70
|
data/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,74 @@
|
|
|
1
|
-
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [8.0.0] - 2026-08-27
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- Relation sorts are unified across association types. Sorting by any
|
|
8
|
+
relation (`has_many`, `has_and_belongs_to_many`, `has_one`,
|
|
9
|
+
`belongs_to`) now `LEFT OUTER JOIN`s the association, groups by the
|
|
10
|
+
sorted table's primary key, and orders by an aggregate of the requested
|
|
11
|
+
column. For `has_many` sorts this changes behavior:
|
|
12
|
+
- each record is returned once, instead of once per associated row
|
|
13
|
+
(the join no longer fans out into duplicates)
|
|
14
|
+
- records with no associated rows are included (previously dropped by
|
|
15
|
+
the `INNER JOIN`)
|
|
16
|
+
- Descending relation sorts key each record by its largest member
|
|
17
|
+
(`MAX`), ascending by its smallest (`MIN`) — the member you'd expect
|
|
18
|
+
to see first in that direction. For records with multiple associated
|
|
19
|
+
rows, descending is therefore not the reverse of ascending: a record
|
|
20
|
+
holding both extremes sorts first in both directions.
|
|
21
|
+
- Sort columns are no longer added to the `SELECT`:
|
|
22
|
+
- loaded records keep their own attributes — a joined sort column can
|
|
23
|
+
no longer overwrite a same-named attribute on the base record
|
|
24
|
+
(previously even `id` could be clobbered)
|
|
25
|
+
- `pluck` and `ids` keep the sort (previously raised on
|
|
26
|
+
`has_and_belongs_to_many` sorts)
|
|
27
|
+
- a caller's `select` is left untouched
|
|
28
|
+
- chaining `.distinct` after a relation sort now raises: PostgreSQL
|
|
29
|
+
requires `ORDER BY` expressions to appear in the select list for
|
|
30
|
+
`SELECT DISTINCT`. It previously appeared to work while silently
|
|
31
|
+
deduplicating over the wrong tuple.
|
|
32
|
+
- A bare `belongs_to`/`has_one` sort with no direction (e.g.
|
|
33
|
+
`Address.sort(property: :name)`) now defaults to ascending, matching
|
|
34
|
+
every other sort form (previously descending).
|
|
35
|
+
- An unknown sort direction on a relation sort (e.g. `:dsc`) now raises
|
|
36
|
+
`ActiveRecord::Sort::InvalidSort` (previously sorted ascending
|
|
37
|
+
silently), matching column sorts.
|
|
38
|
+
|
|
39
|
+
### Added
|
|
40
|
+
|
|
41
|
+
- Sorting by `has_and_belongs_to_many` relations.
|
|
42
|
+
- Unrecognized sort columns, associations, and directions raise
|
|
43
|
+
`ActiveRecord::Sort::InvalidSort`, a subclass of
|
|
44
|
+
`ActiveRecord::StatementInvalid` — so existing
|
|
45
|
+
`rescue ActiveRecord::StatementInvalid` handlers still catch bad sort
|
|
46
|
+
parameters, while callers can rescue the narrower class. Relation sort
|
|
47
|
+
columns are now validated up front (against the associated model's
|
|
48
|
+
columns) instead of only failing once the query reaches the database.
|
|
49
|
+
- Sorts of different types compose: they can be combined in one call
|
|
50
|
+
(`Property.sort(:name, tags: :name, addresses: :id)`) or chained
|
|
51
|
+
(`.sort(...).sort(...)`), sharing a single `GROUP BY`.
|
|
52
|
+
- Aggregates on a sorted relation (`count`, `sum`, `average`, `minimum`,
|
|
53
|
+
`maximum`) are computed over the records themselves rather than the
|
|
54
|
+
sort's grouped, joined rows — `count` returns the record count instead
|
|
55
|
+
of a per-group `Hash`, and multi-member records aren't weighted once
|
|
56
|
+
per member. A caller-supplied `group` still gets standard grouped
|
|
57
|
+
results.
|
|
58
|
+
- A blank direction (`''`, as query parameters often produce) is
|
|
59
|
+
accepted as ascending on all sort forms.
|
|
60
|
+
- `ActionController::Parameters` are accepted for relation sorts, and
|
|
61
|
+
their nulls option (`nulls_first`/`nulls_last`) is honored.
|
|
62
|
+
|
|
63
|
+
### Fixed
|
|
64
|
+
|
|
65
|
+
- `has_and_belongs_to_many` sorts crashed with `NoMethodError` on
|
|
66
|
+
ActiveRecord <= 8.0 (`Function#as` mutates and returns the receiver
|
|
67
|
+
there).
|
|
68
|
+
- Combining a `has_and_belongs_to_many` sort with another relation sort
|
|
69
|
+
raised `PG::GroupingError`.
|
|
70
|
+
|
|
71
|
+
## [7.0.0] - 2026-08-27
|
|
2
72
|
|
|
3
73
|
The gem's version is now independent of the Rails version it targets.
|
|
4
74
|
|
|
@@ -25,4 +95,5 @@ The gem's version is now independent of the Rails version it targets.
|
|
|
25
95
|
`ActiveRecord::QueryMethods.public_instance_methods(false)` — which keeps
|
|
26
96
|
Rails' internal delegation invariants intact.
|
|
27
97
|
|
|
98
|
+
[8.0.0]: https://github.com/malomalo/activerecord-sort/releases/tag/v8.0.0
|
|
28
99
|
[7.0.0]: https://github.com/malomalo/activerecord-sort/releases/tag/v7.0.0
|
data/README.md
CHANGED
|
@@ -50,22 +50,41 @@ Property.sort(id: {asc: :nulls_last}).to_sql
|
|
|
50
50
|
# => "...ORDER BY properties.id ASC NULLS LAST"
|
|
51
51
|
```
|
|
52
52
|
|
|
53
|
-
It can also sort on
|
|
53
|
+
It can also sort on relations. A relation sort groups by the sorted table's
|
|
54
|
+
primary key — so each record appears once and records with no associated
|
|
55
|
+
rows are still included — and orders by an aggregate of the requested
|
|
56
|
+
column: `MIN` ascending or `MAX` descending, keying each record by the
|
|
57
|
+
member you'd expect to see first in that direction. A record with no
|
|
58
|
+
associated rows has a `NULL` sort key; where `NULL`s land is
|
|
59
|
+
database-dependent, so pass `nulls_first`/`nulls_last` to place those
|
|
60
|
+
records explicitly:
|
|
54
61
|
|
|
55
62
|
```ruby
|
|
56
63
|
Property.sort(addresses: :id).to_sql
|
|
57
|
-
# => "
|
|
58
|
-
# => "
|
|
64
|
+
# => "SELECT properties.* FROM properties
|
|
65
|
+
# => " LEFT OUTER JOIN addresses ON addresses.property_id = properties.id
|
|
66
|
+
# => " GROUP BY properties.id
|
|
67
|
+
# => " ORDER BY MIN(addresses.id) ASC"
|
|
59
68
|
|
|
60
69
|
Property.sort(addresses: {id: :desc}).to_sql
|
|
61
|
-
# => "...
|
|
62
|
-
# => " ORDER BY addresses.id DESC"
|
|
70
|
+
# => "...ORDER BY MAX(addresses.id) DESC"
|
|
63
71
|
|
|
64
72
|
Property.sort(addresses: {id: {asc: :nulls_first}}).to_sql
|
|
65
|
-
# => "...
|
|
66
|
-
|
|
73
|
+
# => "...ORDER BY MIN(addresses.id) ASC NULLS FIRST"
|
|
74
|
+
|
|
75
|
+
Property.sort(tags: :name).to_sql # has_and_belongs_to_many
|
|
76
|
+
# => "SELECT properties.* FROM properties
|
|
77
|
+
# => " LEFT OUTER JOIN properties_tags ON properties_tags.property_id = properties.id
|
|
78
|
+
# => " LEFT OUTER JOIN tags ON tags.id = properties_tags.tag_id
|
|
79
|
+
# => " GROUP BY properties.id
|
|
80
|
+
# => " ORDER BY MIN(tags.name) ASC"
|
|
67
81
|
```
|
|
68
82
|
|
|
83
|
+
A relation sort is order-only — it never adds or removes records — so
|
|
84
|
+
aggregates on a sorted relation (`count`, `sum`, `average`, `minimum`,
|
|
85
|
+
`maximum`) are computed over the records themselves, not the sort's
|
|
86
|
+
grouped and joined rows.
|
|
87
|
+
|
|
69
88
|
Order randomly:
|
|
70
89
|
|
|
71
90
|
```ruby
|
|
@@ -77,9 +96,13 @@ Unrecognized columns raise, so unfiltered params can't inject SQL:
|
|
|
77
96
|
|
|
78
97
|
```ruby
|
|
79
98
|
Property.sort(:name_or_something_unexpected)
|
|
80
|
-
# => raises ActiveRecord::
|
|
99
|
+
# => raises ActiveRecord::Sort::InvalidSort
|
|
81
100
|
```
|
|
82
101
|
|
|
102
|
+
`ActiveRecord::Sort::InvalidSort` subclasses `ActiveRecord::StatementInvalid`,
|
|
103
|
+
so existing `rescue ActiveRecord::StatementInvalid` handlers keep catching bad
|
|
104
|
+
sort parameters, while callers that want to can rescue the narrower class.
|
|
105
|
+
|
|
83
106
|
Called with no arguments, `#sort` behaves like Ruby's `Enumerable#sort` —
|
|
84
107
|
it loads the records and sorts them by `<=>` — rather than building a query:
|
|
85
108
|
|
data/ext/active_record/base.rb
CHANGED
|
@@ -8,6 +8,15 @@ module ActiveRecord
|
|
|
8
8
|
# delegation tests assert against — while relations still respond to them.
|
|
9
9
|
module Sort
|
|
10
10
|
|
|
11
|
+
# Raised when a sort references an unrecognized column or association,
|
|
12
|
+
# or an unknown direction. Subclasses StatementInvalid so existing
|
|
13
|
+
# `rescue ActiveRecord::StatementInvalid` handlers — the safety
|
|
14
|
+
# contract that makes it OK to pass request params straight through —
|
|
15
|
+
# keep working, while callers can rescue this narrower class to tell a
|
|
16
|
+
# bad sort parameter apart from a genuine database error.
|
|
17
|
+
class InvalidSort < ActiveRecord::StatementInvalid
|
|
18
|
+
end
|
|
19
|
+
|
|
11
20
|
# ordering:
|
|
12
21
|
# :id
|
|
13
22
|
# :name, :id
|
|
@@ -29,7 +38,6 @@ module ActiveRecord
|
|
|
29
38
|
ordering.flatten!
|
|
30
39
|
return resource if ordering.size == 0
|
|
31
40
|
|
|
32
|
-
order_columns = []
|
|
33
41
|
ordering.each do |order|
|
|
34
42
|
order = Array(order)
|
|
35
43
|
order.each do |column_or_relation, options|
|
|
@@ -38,97 +46,138 @@ module ActiveRecord
|
|
|
38
46
|
elsif self.column_names.include?(column_or_relation.to_s)
|
|
39
47
|
resource = resource.sort_for_column(self.arel_table[column_or_relation.to_s], options)
|
|
40
48
|
elsif reflect_on_association(column_or_relation.to_sym)
|
|
41
|
-
resource = resource.sort_for_relation(column_or_relation.to_sym, options
|
|
49
|
+
resource = resource.sort_for_relation(column_or_relation.to_sym, options)
|
|
42
50
|
else
|
|
43
|
-
raise
|
|
51
|
+
raise InvalidSort.new("Unknown column #{column_or_relation}")
|
|
44
52
|
end
|
|
45
53
|
end
|
|
46
54
|
end
|
|
47
|
-
|
|
48
|
-
if order_columns.present?
|
|
49
|
-
resource = resource.select(resource.klass.arel_table[Arel::Nodes::SqlLiteral.new('*')], *order_columns)
|
|
50
|
-
end
|
|
51
55
|
|
|
52
56
|
resource
|
|
53
57
|
end
|
|
54
58
|
|
|
59
|
+
# Normalizes per-column sort options into [direction, nulls]. A blank
|
|
60
|
+
# direction — a bare column, or "" as query params often produce —
|
|
61
|
+
# means :asc.
|
|
55
62
|
# TODO: probably don't need to cast to sym
|
|
63
|
+
def sort_direction_and_nulls(options)
|
|
64
|
+
if options.is_a?(Hash) || options.class.name == "ActionController::Parameters"
|
|
65
|
+
[options.keys.first.to_sym, options.values.first.to_sym]
|
|
66
|
+
elsif options.blank?
|
|
67
|
+
[:asc, nil]
|
|
68
|
+
else
|
|
69
|
+
[options.to_s.downcase.to_sym, nil]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
|
|
56
73
|
def sort_for_column(column, options)
|
|
57
|
-
direction = (options
|
|
74
|
+
direction, nulls = sort_direction_and_nulls(options)
|
|
58
75
|
|
|
59
|
-
nulls = (options.is_a?(Hash) ? options.values.first.to_sym : nil)
|
|
60
76
|
if direction == :desc
|
|
61
77
|
self.order(Arel::Nodes::Descending.new(column, nulls))
|
|
62
|
-
elsif direction == :asc
|
|
78
|
+
elsif direction == :asc
|
|
63
79
|
self.order(Arel::Nodes::Ascending.new(column, nulls))
|
|
64
80
|
else
|
|
65
|
-
raise
|
|
81
|
+
raise InvalidSort.new("Unknown ordering #{direction}")
|
|
66
82
|
end
|
|
67
83
|
end
|
|
68
84
|
|
|
69
|
-
|
|
85
|
+
# The sort key lives only in the ORDER BY — nothing is added to the
|
|
86
|
+
# select list. That keeps the ORDER BY self-contained (so pluck/ids,
|
|
87
|
+
# which replace the select list, keep the sort), leaves the caller's
|
|
88
|
+
# select untouched, and means loaded records carry exactly their own
|
|
89
|
+
# attributes (a selected join column would overwrite a same-named
|
|
90
|
+
# attribute on the base table).
|
|
91
|
+
def sort_for_relation(relation, options)
|
|
70
92
|
resource = self
|
|
71
93
|
relation = reflect_on_association(relation)
|
|
94
|
+
options = [options] if !options.is_a?(Array)
|
|
95
|
+
|
|
96
|
+
# LEFT JOIN the association, group by this table's primary key — so
|
|
97
|
+
# rows don't fan / duplicate and records with an empty collection
|
|
98
|
+
# still appear — and order by an aggregate of the requested column:
|
|
99
|
+
#
|
|
100
|
+
# SELECT properties.*
|
|
101
|
+
# FROM properties
|
|
102
|
+
# LEFT JOIN properties_tags ON properties_tags.property_id = properties.id
|
|
103
|
+
# LEFT JOIN tags ON tags.id = properties_tags.tag_id
|
|
104
|
+
# GROUP BY properties.id
|
|
105
|
+
# ORDER BY MIN(tags.name) ASC
|
|
106
|
+
#
|
|
107
|
+
# Ascending keys each record by its smallest member (MIN), descending
|
|
108
|
+
# by its largest (MAX) — the member you'd expect to see first in that
|
|
109
|
+
# direction. Toggling asc/desc therefore re-keys multi-value records
|
|
110
|
+
# rather than strictly reversing the list.
|
|
111
|
+
options.each do |order|
|
|
112
|
+
Array(order).each do |column_name, column_options|
|
|
113
|
+
if !relation.klass.column_names.include?(column_name.to_s)
|
|
114
|
+
raise InvalidSort.new("Unknown column #{column_name}")
|
|
115
|
+
end
|
|
72
116
|
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
direction = (options.is_a?(Hash) ? options.keys.first.to_sym : options.to_s.downcase.to_sym)
|
|
82
|
-
|
|
83
|
-
nulls = (options.is_a?(Hash) ? options.values.first.to_sym : nil)
|
|
84
|
-
if direction == :desc
|
|
85
|
-
# aggregation = Arel::Nodes::Max.new([column], "max_#{relation.name}_#{column.name}")
|
|
86
|
-
# order = Arel::Nodes::Descending.new(Arel::Nodes::SqlLiteral.new("max_#{relation.name}_#{column.name}"), nulls)
|
|
87
|
-
|
|
88
|
-
if relation.options[:through]
|
|
89
|
-
resource = resource.joins(relation.options[:through] => relation.source_reflection_name)
|
|
90
|
-
else
|
|
91
|
-
resource = resource.joins(relation.name)
|
|
92
|
-
end
|
|
93
|
-
# resource = resource.select(aggregation)
|
|
94
|
-
# resource = resource.order(order)
|
|
95
|
-
resource = resource.order(Arel::Nodes::Descending.new(column, nulls))
|
|
96
|
-
else
|
|
97
|
-
# aggregation = Arel::Nodes::Min.new([column], "min_#{relation.name}_#{column.name}")
|
|
98
|
-
order = Arel::Nodes::Ascending.new(Arel::Nodes::SqlLiteral.new("min_#{relation.name}_#{column.name}"), nulls)
|
|
99
|
-
|
|
100
|
-
resource = resource.joins(relation.name)
|
|
101
|
-
# resource = resource.select(aggregation)
|
|
102
|
-
# resource = resource.order(order)
|
|
103
|
-
resource = resource.order(Arel::Nodes::Ascending.new(column, nulls))
|
|
104
|
-
end
|
|
117
|
+
# A collection (has_many / has_and_belongs_to_many) wraps the
|
|
118
|
+
# attribute in an Arel::Attributes::Relation so the aggregate
|
|
119
|
+
# references the joined table correctly; a singular association
|
|
120
|
+
# (belongs_to / has_one) uses the plain attribute.
|
|
121
|
+
column = if relation.collection?
|
|
122
|
+
Arel::Attributes::Relation.new(relation.klass.arel_table[column_name], relation.name)
|
|
123
|
+
else
|
|
124
|
+
relation.klass.arel_table[column_name]
|
|
105
125
|
end
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
direction = (options.is_a?(Hash) ? options.keys.first.to_sym : options.to_s.downcase.to_sym)
|
|
116
|
-
|
|
117
|
-
nulls = (options.is_a?(Hash) ? options.values.first.to_sym : nil)
|
|
118
|
-
if direction == :asc
|
|
119
|
-
order = Arel::Nodes::Ascending.new(column, nulls)
|
|
120
|
-
else
|
|
121
|
-
order = Arel::Nodes::Descending.new(column, nulls)
|
|
122
|
-
end
|
|
123
|
-
|
|
124
|
-
resource = resource.left_outer_joins(relation.name)
|
|
125
|
-
resource = resource.order(order)
|
|
126
|
+
|
|
127
|
+
direction, nulls = sort_direction_and_nulls(column_options)
|
|
128
|
+
|
|
129
|
+
order = if direction == :desc
|
|
130
|
+
Arel::Nodes::Descending.new(column.maximum, nulls)
|
|
131
|
+
elsif direction == :asc
|
|
132
|
+
Arel::Nodes::Ascending.new(column.minimum, nulls)
|
|
133
|
+
else
|
|
134
|
+
raise InvalidSort.new("Unknown ordering #{direction}")
|
|
126
135
|
end
|
|
136
|
+
|
|
137
|
+
resource = resource.left_outer_joins(relation.name)
|
|
138
|
+
resource = resource.order(order)
|
|
127
139
|
end
|
|
128
140
|
end
|
|
129
141
|
|
|
142
|
+
# Group by the primary key so the aggregate collapses the joined,
|
|
143
|
+
# fanned-out rows to one per record. Every relation sort adds this,
|
|
144
|
+
# so only add it once: ActiveRecord < 8.1's group! appends rather
|
|
145
|
+
# than unioning, which would leave a duplicate [pk, pk] that trips
|
|
146
|
+
# the count override's group_values check below.
|
|
147
|
+
# TODO: once Rails <= 8.0 is no longer supported, group! unions on
|
|
148
|
+
# its own — drop the include? guard and just group(primary_key).
|
|
149
|
+
primary_key = klass.arel_table[klass.primary_key]
|
|
150
|
+
resource = resource.group(primary_key) unless resource.group_values.include?(primary_key)
|
|
151
|
+
# Tag the relation for the count override below. Chained relations
|
|
152
|
+
# are built with clone, which copies instance variables, so the tag
|
|
153
|
+
# survives further chaining.
|
|
154
|
+
resource.instance_variable_set(:@sorted_by_relation, true)
|
|
130
155
|
resource
|
|
131
156
|
end
|
|
132
157
|
|
|
133
158
|
end
|
|
159
|
+
|
|
160
|
+
module Sort
|
|
161
|
+
module Calculations
|
|
162
|
+
# sort_for_relation groups by the primary key and LEFT JOINs the
|
|
163
|
+
# relation, so aggregates would see grouped, fanned-out rows: count
|
|
164
|
+
# returns a per-group Hash, and sum/average weigh a record once per
|
|
165
|
+
# collection member. But a relation sort is order-only — records are
|
|
166
|
+
# never added or removed — so every aggregate has a well-defined
|
|
167
|
+
# answer: compute it over the base table restricted to the sorted
|
|
168
|
+
# relation's (distinct) primary keys. User joins and conditions still
|
|
169
|
+
# apply inside the subquery, and a user-added group falls through to
|
|
170
|
+
# the standard grouped behavior.
|
|
171
|
+
def calculate(operation, column_name)
|
|
172
|
+
if @sorted_by_relation && group_values == [klass.arel_table[klass.primary_key]]
|
|
173
|
+
klass.where(klass.primary_key => unscope(:group, :order, :select).select(klass.primary_key))
|
|
174
|
+
.calculate(operation, column_name)
|
|
175
|
+
else
|
|
176
|
+
super
|
|
177
|
+
end
|
|
178
|
+
end
|
|
179
|
+
end
|
|
180
|
+
end
|
|
134
181
|
end
|
|
182
|
+
|
|
183
|
+
ActiveRecord::Relation.prepend(ActiveRecord::Sort::Calculations)
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: activerecord-sort
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version:
|
|
4
|
+
version: 8.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jon Bracy
|
|
@@ -103,18 +103,18 @@ dependencies:
|
|
|
103
103
|
name: simplecov
|
|
104
104
|
requirement: !ruby/object:Gem::Requirement
|
|
105
105
|
requirements:
|
|
106
|
-
- -
|
|
106
|
+
- - '='
|
|
107
107
|
- !ruby/object:Gem::Version
|
|
108
|
-
version:
|
|
108
|
+
version: 1.0.0.rc5
|
|
109
109
|
type: :development
|
|
110
110
|
prerelease: false
|
|
111
111
|
version_requirements: !ruby/object:Gem::Requirement
|
|
112
112
|
requirements:
|
|
113
|
-
- -
|
|
113
|
+
- - '='
|
|
114
114
|
- !ruby/object:Gem::Version
|
|
115
|
-
version:
|
|
115
|
+
version: 1.0.0.rc5
|
|
116
116
|
- !ruby/object:Gem::Dependency
|
|
117
|
-
name:
|
|
117
|
+
name: faker
|
|
118
118
|
requirement: !ruby/object:Gem::Requirement
|
|
119
119
|
requirements:
|
|
120
120
|
- - ">="
|
|
@@ -128,35 +128,35 @@ dependencies:
|
|
|
128
128
|
- !ruby/object:Gem::Version
|
|
129
129
|
version: '0'
|
|
130
130
|
- !ruby/object:Gem::Dependency
|
|
131
|
-
name:
|
|
131
|
+
name: sunstone
|
|
132
132
|
requirement: !ruby/object:Gem::Requirement
|
|
133
133
|
requirements:
|
|
134
134
|
- - ">="
|
|
135
135
|
- !ruby/object:Gem::Version
|
|
136
|
-
version:
|
|
136
|
+
version: 7.0.0
|
|
137
137
|
type: :development
|
|
138
138
|
prerelease: false
|
|
139
139
|
version_requirements: !ruby/object:Gem::Requirement
|
|
140
140
|
requirements:
|
|
141
141
|
- - ">="
|
|
142
142
|
- !ruby/object:Gem::Version
|
|
143
|
-
version:
|
|
143
|
+
version: 7.0.0
|
|
144
144
|
- !ruby/object:Gem::Dependency
|
|
145
|
-
name:
|
|
145
|
+
name: webmock
|
|
146
146
|
requirement: !ruby/object:Gem::Requirement
|
|
147
147
|
requirements:
|
|
148
148
|
- - ">="
|
|
149
149
|
- !ruby/object:Gem::Version
|
|
150
|
-
version:
|
|
150
|
+
version: '0'
|
|
151
151
|
type: :development
|
|
152
152
|
prerelease: false
|
|
153
153
|
version_requirements: !ruby/object:Gem::Requirement
|
|
154
154
|
requirements:
|
|
155
155
|
- - ">="
|
|
156
156
|
- !ruby/object:Gem::Version
|
|
157
|
-
version:
|
|
157
|
+
version: '0'
|
|
158
158
|
- !ruby/object:Gem::Dependency
|
|
159
|
-
name:
|
|
159
|
+
name: debug
|
|
160
160
|
requirement: !ruby/object:Gem::Requirement
|
|
161
161
|
requirements:
|
|
162
162
|
- - ">="
|