ddr-core 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (132) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE.txt +12 -0
  3. data/README.md +27 -0
  4. data/Rakefile +30 -0
  5. data/app/assets/config/ddr_core_manifest.js +0 -0
  6. data/app/controllers/users/omniauth_callbacks_controller.rb +11 -0
  7. data/app/controllers/users/sessions_controller.rb +15 -0
  8. data/app/models/concerns/ddr/captionable.rb +25 -0
  9. data/app/models/concerns/ddr/describable.rb +108 -0
  10. data/app/models/concerns/ddr/governable.rb +25 -0
  11. data/app/models/concerns/ddr/has_admin_metadata.rb +141 -0
  12. data/app/models/concerns/ddr/has_attachments.rb +10 -0
  13. data/app/models/concerns/ddr/has_children.rb +10 -0
  14. data/app/models/concerns/ddr/has_content.rb +132 -0
  15. data/app/models/concerns/ddr/has_extracted_text.rb +10 -0
  16. data/app/models/concerns/ddr/has_intermediate_file.rb +25 -0
  17. data/app/models/concerns/ddr/has_multires_image.rb +14 -0
  18. data/app/models/concerns/ddr/has_parent.rb +18 -0
  19. data/app/models/concerns/ddr/has_struct_metadata.rb +21 -0
  20. data/app/models/concerns/ddr/has_thumbnail.rb +33 -0
  21. data/app/models/concerns/ddr/solr_document_behavior.rb +429 -0
  22. data/app/models/concerns/ddr/streamable.rb +25 -0
  23. data/app/models/ddr/admin_set.rb +28 -0
  24. data/app/models/ddr/attachment.rb +14 -0
  25. data/app/models/ddr/collection.rb +28 -0
  26. data/app/models/ddr/component.rb +31 -0
  27. data/app/models/ddr/contact.rb +23 -0
  28. data/app/models/ddr/digest.rb +8 -0
  29. data/app/models/ddr/file.rb +40 -0
  30. data/app/models/ddr/item.rb +36 -0
  31. data/app/models/ddr/language.rb +31 -0
  32. data/app/models/ddr/media_type.rb +22 -0
  33. data/app/models/ddr/resource.rb +94 -0
  34. data/app/models/ddr/rights_statement.rb +25 -0
  35. data/app/models/ddr/target.rb +17 -0
  36. data/config/initializers/devise.rb +262 -0
  37. data/config/locales/ddr-core.en.yml +85 -0
  38. data/config/routes.rb +3 -0
  39. data/db/migrate/20141104181418_create_users.rb +34 -0
  40. data/db/migrate/20141107124012_add_columns_to_user.rb +46 -0
  41. data/lib/ddr-core.rb +1 -0
  42. data/lib/ddr/auth.rb +80 -0
  43. data/lib/ddr/auth/ability.rb +18 -0
  44. data/lib/ddr/auth/ability_definitions.rb +26 -0
  45. data/lib/ddr/auth/ability_definitions/admin_set_ability_definitions.rb +9 -0
  46. data/lib/ddr/auth/ability_definitions/alias_ability_definitions.rb +23 -0
  47. data/lib/ddr/auth/ability_definitions/attachment_ability_definitions.rb +13 -0
  48. data/lib/ddr/auth/ability_definitions/collection_ability_definitions.rb +28 -0
  49. data/lib/ddr/auth/ability_definitions/component_ability_definitions.rb +13 -0
  50. data/lib/ddr/auth/ability_definitions/item_ability_definitions.rb +13 -0
  51. data/lib/ddr/auth/ability_definitions/lock_ability_definitions.rb +13 -0
  52. data/lib/ddr/auth/ability_definitions/publication_ability_definitions.rb +16 -0
  53. data/lib/ddr/auth/ability_definitions/role_based_ability_definitions.rb +39 -0
  54. data/lib/ddr/auth/ability_definitions/superuser_ability_definitions.rb +9 -0
  55. data/lib/ddr/auth/ability_factory.rb +10 -0
  56. data/lib/ddr/auth/abstract_ability.rb +48 -0
  57. data/lib/ddr/auth/affiliation.rb +14 -0
  58. data/lib/ddr/auth/affiliation_groups.rb +20 -0
  59. data/lib/ddr/auth/anonymous_ability.rb +7 -0
  60. data/lib/ddr/auth/auth_context.rb +109 -0
  61. data/lib/ddr/auth/auth_context_factory.rb +13 -0
  62. data/lib/ddr/auth/detached_auth_context.rb +19 -0
  63. data/lib/ddr/auth/dynamic_groups.rb +13 -0
  64. data/lib/ddr/auth/effective_permissions.rb +12 -0
  65. data/lib/ddr/auth/effective_roles.rb +9 -0
  66. data/lib/ddr/auth/failure_app.rb +16 -0
  67. data/lib/ddr/auth/group.rb +40 -0
  68. data/lib/ddr/auth/grouper_gateway.rb +70 -0
  69. data/lib/ddr/auth/groups.rb +32 -0
  70. data/lib/ddr/auth/ldap_gateway.rb +74 -0
  71. data/lib/ddr/auth/permissions.rb +18 -0
  72. data/lib/ddr/auth/remote_groups.rb +14 -0
  73. data/lib/ddr/auth/role_based_access_controls_enforcement.rb +56 -0
  74. data/lib/ddr/auth/roles.rb +28 -0
  75. data/lib/ddr/auth/roles/role.rb +121 -0
  76. data/lib/ddr/auth/roles/role_type.rb +23 -0
  77. data/lib/ddr/auth/roles/role_types.rb +52 -0
  78. data/lib/ddr/auth/superuser_ability.rb +7 -0
  79. data/lib/ddr/auth/test_helpers.rb +22 -0
  80. data/lib/ddr/auth/user.rb +54 -0
  81. data/lib/ddr/auth/web_auth_context.rb +29 -0
  82. data/lib/ddr/core.rb +110 -0
  83. data/lib/ddr/core/engine.rb +8 -0
  84. data/lib/ddr/core/version.rb +5 -0
  85. data/lib/ddr/error.rb +16 -0
  86. data/lib/ddr/files.rb +13 -0
  87. data/lib/ddr/fits.rb +189 -0
  88. data/lib/ddr/index.rb +29 -0
  89. data/lib/ddr/index/abstract_query_result.rb +22 -0
  90. data/lib/ddr/index/connection.rb +38 -0
  91. data/lib/ddr/index/csv_query_result.rb +84 -0
  92. data/lib/ddr/index/document_builder.rb +9 -0
  93. data/lib/ddr/index/field.rb +35 -0
  94. data/lib/ddr/index/field_attribute.rb +22 -0
  95. data/lib/ddr/index/fields.rb +154 -0
  96. data/lib/ddr/index/filter.rb +139 -0
  97. data/lib/ddr/index/query.rb +82 -0
  98. data/lib/ddr/index/query_builder.rb +185 -0
  99. data/lib/ddr/index/query_clause.rb +112 -0
  100. data/lib/ddr/index/query_params.rb +40 -0
  101. data/lib/ddr/index/query_result.rb +102 -0
  102. data/lib/ddr/index/response.rb +30 -0
  103. data/lib/ddr/index/sort_order.rb +28 -0
  104. data/lib/ddr/index/unique_key_field.rb +12 -0
  105. data/lib/ddr/managers.rb +9 -0
  106. data/lib/ddr/managers/manager.rb +13 -0
  107. data/lib/ddr/managers/technical_metadata_manager.rb +141 -0
  108. data/lib/ddr/structure.rb +188 -0
  109. data/lib/ddr/structures/agent.rb +49 -0
  110. data/lib/ddr/structures/component_type_term.rb +29 -0
  111. data/lib/ddr/structures/div.rb +64 -0
  112. data/lib/ddr/structures/f_locat.rb +54 -0
  113. data/lib/ddr/structures/file.rb +52 -0
  114. data/lib/ddr/structures/file_grp.rb +35 -0
  115. data/lib/ddr/structures/file_sec.rb +22 -0
  116. data/lib/ddr/structures/fptr.rb +31 -0
  117. data/lib/ddr/structures/mets_hdr.rb +37 -0
  118. data/lib/ddr/structures/mptr.rb +49 -0
  119. data/lib/ddr/structures/struct_map.rb +40 -0
  120. data/lib/ddr/utils.rb +185 -0
  121. data/lib/ddr/vocab.rb +22 -0
  122. data/lib/ddr/vocab/asset.rb +51 -0
  123. data/lib/ddr/vocab/contact.rb +9 -0
  124. data/lib/ddr/vocab/display.rb +9 -0
  125. data/lib/ddr/vocab/duke_terms.rb +13 -0
  126. data/lib/ddr/vocab/rdf_vocabulary_parser.rb +43 -0
  127. data/lib/ddr/vocab/roles.rb +25 -0
  128. data/lib/ddr/vocab/sources/duketerms.rdf +870 -0
  129. data/lib/ddr/vocab/vocabulary.rb +37 -0
  130. data/lib/ddr/workflow.rb +8 -0
  131. data/lib/tasks/ddr/core_tasks.rake +4 -0
  132. metadata +428 -0
@@ -0,0 +1,82 @@
1
+ require "virtus"
2
+ require "forwardable"
3
+
4
+ module Ddr::Index
5
+ class Query
6
+ include Virtus.model
7
+ extend Forwardable
8
+ extend Deprecation
9
+
10
+ attribute :q, String, default: '*:*'
11
+ attribute :fields, Array[FieldAttribute], default: [ ]
12
+ attribute :filters, Array[Filter], default: [ ]
13
+ attribute :sort, Array[String], default: [ ]
14
+ attribute :rows, Integer
15
+
16
+ delegate [:count, :docs, :ids, :each_id, :all] => :result
17
+ delegate :params => :query_params
18
+
19
+ def self.build(*args, &block)
20
+ new.tap do |query|
21
+ query.build(*args, &block)
22
+ end
23
+ end
24
+
25
+ def initialize(**args, &block)
26
+ super(**args)
27
+ if block_given?
28
+ build(&block)
29
+ end
30
+ end
31
+
32
+ def inspect
33
+ "#<#{self.class.name} q=#{q.inspect}, filters=#{filters.inspect}," \
34
+ " sort=#{sort.inspect}, rows=#{rows.inspect}, fields=#{fields.inspect}>"
35
+ end
36
+
37
+ def to_s
38
+ URI.encode_www_form(params)
39
+ end
40
+
41
+ def pids
42
+ Deprecation.warn(QueryResult, "`pids` is deprecated; use `ids` instead.")
43
+ ids
44
+ end
45
+
46
+ def each_pid(&block)
47
+ Deprecation.warn(QueryResult, "`each_pid` is deprecated; use `each_id` instead.")
48
+ each_id(&block)
49
+ end
50
+
51
+ def result
52
+ QueryResult.new(self)
53
+ end
54
+
55
+ def csv
56
+ CSVQueryResult.new(self)
57
+ end
58
+
59
+ def filter_clauses
60
+ filters.map(&:clauses).flatten
61
+ end
62
+
63
+ def query_params
64
+ QueryParams.new(self)
65
+ end
66
+
67
+ def build(*args, &block)
68
+ QueryBuilder.new(self, *args, &block)
69
+ self
70
+ end
71
+
72
+ def ==(other)
73
+ other.instance_of?(self.class) &&
74
+ other.q == self.q &&
75
+ other.fields == self.fields &&
76
+ other.filters == self.filters &&
77
+ other.rows == self.rows &&
78
+ other.sort == self.sort
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,185 @@
1
+ module Ddr::Index
2
+ #
3
+ # QueryBuilder - Provides a DSL for building a Query.
4
+ #
5
+ # Note: Where a method receives a [field] parameter, the parameter value is
6
+ # coerced to a Field instance. See FieldAttribute#coerce for details.
7
+ #
8
+ # *** DSL METHODS ***
9
+ #
10
+ # absent [field]
11
+ # Adds a filter selecting documents where the field is not present (no values).
12
+ #
13
+ # asc [field], ...
14
+ # Adds ascending orderings by the fields specified.
15
+ #
16
+ # See also: desc, order_by
17
+ #
18
+ # before [field], [date_time]
19
+ # Adds a filter selecting documents where the field has a date/time before
20
+ # (earlier than) the value.
21
+ #
22
+ # before_days [field], [int]
23
+ # Adds a filter selecting documents where the field has a date/time the
24
+ # specified number of days before today (now) or earlier.
25
+ #
26
+ # desc [field], ...
27
+ # Adds descending orderings by the fields specified.
28
+ #
29
+ # See also: asc, order_by
30
+ #
31
+ # filter [filter1], ...
32
+ # Adds filters to the query.
33
+ #
34
+ # Aliased as: filters
35
+ #
36
+ # filters [filter], ...
37
+ # Alias for: filter
38
+ #
39
+ # field [field1], ...
40
+ # Adds fields to result documents.
41
+ # Note that all fields are returned when none is specified.
42
+ #
43
+ # Aliased as: fields
44
+ #
45
+ # fields [field], ...
46
+ # Alias for: field
47
+ #
48
+ # id [doc_id]
49
+ # For selecting a single document by ID.
50
+ #
51
+ # join [from: {field1}, to: {field2}, where: {condition}]
52
+ # Adds a Solr join clause (see https://wiki.apache.org/solr/Join)
53
+ #
54
+ # limit [int]
55
+ # Limits the number of documents returned by the query.
56
+ #
57
+ # Aliased as: rows
58
+ #
59
+ # model [model_name], ...
60
+ # Adds a filter selecting document where ActiveFedora model equals value
61
+ # or one of the values.
62
+ #
63
+ # negative [field], [value]
64
+ # Adds a filter selecting document where field does not have the value.
65
+ #
66
+ # order_by [{field => order, ...}], ...
67
+ # Adds ordering(s) to the query.
68
+ #
69
+ # Aliased as: sort
70
+ #
71
+ # present [field]
72
+ # Adds a filter selecting document where the field has any value.
73
+ #
74
+ # q [query_clause]
75
+ # Sets a query clause for the `q` parameter.
76
+ #
77
+ # raw [clause1], ...
78
+ # Adds a filter of "raw" query clauses (i.e., pre-constructed).
79
+ #
80
+ # regexp [field], [regexp]
81
+ # Adds a filter selecting documents where the field has a value
82
+ # matching the regular expression.
83
+ # Slashes (/) in the regexp will be escaped as required by Solr.
84
+ #
85
+ # rows [int]
86
+ # Alias for: limit
87
+ #
88
+ # sort [{field => order, ...}]
89
+ # Alias for: order_by
90
+ #
91
+ # term [{field => value, ...}]
92
+ # Adds a filter of "term" query clauses for the fields and values.
93
+ #
94
+ # where [{field => value, ...}]
95
+ # Adds a filter of "standard" query clauses.
96
+ # Values will be escaped when the filter is serialized.
97
+ # If a hash value is an array, that query clause will select documents
98
+ # where the field matches any array entry.
99
+ #
100
+ class QueryBuilder
101
+
102
+ attr_reader :query
103
+
104
+ def initialize(*args, &block)
105
+ @query = args.first.is_a?(Query) ? args.shift : Query.new
106
+ if block_given?
107
+ instance_exec(*args, &block)
108
+ end
109
+ end
110
+
111
+ # @param pid [String]
112
+ # @return [QueryBuilder]
113
+ def id(pid)
114
+ q QueryClause.id(pid)
115
+ limit 1
116
+ end
117
+
118
+ # @param filters [Array<Filter>]
119
+ # @return [QueryBuilder]
120
+ def filter(*filters)
121
+ query.filters += filters
122
+ self
123
+ end
124
+ alias_method :filters, :filter
125
+
126
+ # @param fields [Array<Field>]
127
+ # @return [QueryBuilder] self
128
+ def field(*fields)
129
+ query.fields += fields.flatten.map { |f| FieldAttribute.coerce(f) }
130
+ self
131
+ end
132
+ alias_method :fields, :field
133
+
134
+ # @param num [Integer]
135
+ # @return [QueryBuilder] self
136
+ def limit(num)
137
+ query.rows = num.to_i
138
+ self
139
+ end
140
+ alias_method :rows, :limit
141
+
142
+ # @param orderings [Hash<Field, String>]
143
+ # @return [QueryBuilder] self
144
+ def order_by(*orderings)
145
+ query.sort += orderings.first.map { |field, order| SortOrder.new(field: field, order: order) }
146
+ self
147
+ end
148
+ alias_method :sort, :order_by
149
+
150
+ # @param fields [Array<Field, Symbol, String>]
151
+ # @return [QueryBuilder] self
152
+ def asc(*fields)
153
+ query.sort += fields.map { |field| SortOrder.asc(field) }
154
+ self
155
+ end
156
+ # @param fields [Array<Field, Symbol, String>]
157
+ # @return [QueryBuilder] self
158
+ def desc(*fields)
159
+ query.sort += fields.map { |field| SortOrder.desc(field) }
160
+ self
161
+ end
162
+
163
+ # @param query_clause [QueryClause, String]
164
+ # @return [QueryBuilder] self
165
+ def q(query_clause)
166
+ query.q = query_clause
167
+ self
168
+ end
169
+
170
+ private
171
+
172
+ def respond_to_missing?(name, include_all)
173
+ Filter::ClassMethods.public_instance_methods.include?(name)
174
+ end
175
+
176
+ def method_missing(name, *args, &block)
177
+ if respond_to?(name)
178
+ filter Filter.send(name, *args)
179
+ else
180
+ super
181
+ end
182
+ end
183
+
184
+ end
185
+ end
@@ -0,0 +1,112 @@
1
+ require "virtus"
2
+
3
+ module Ddr::Index
4
+ class QueryClause
5
+ include Virtus.value_object
6
+
7
+ ANY_FIELD = Field.new('*').freeze
8
+ ANY_VALUE = "[* TO *]"
9
+ QUOTE = '"'
10
+
11
+ TERM_QUERY = "{!term f=%{field}}%{value}"
12
+ STANDARD_QUERY = "%{field}:%{value}"
13
+ NEGATIVE_QUERY = "-%{field}:%{value}"
14
+ DISJUNCTION = "{!lucene q.op=OR df=%{field}}%{value}"
15
+ REGEXP_QUERY = "%{field}:/%{value}/"
16
+
17
+ values do
18
+ attribute :field, FieldAttribute
19
+ attribute :value, String
20
+ attribute :quote_value, Boolean, default: false
21
+ attribute :template, String, default: STANDARD_QUERY
22
+ end
23
+
24
+ def to_s
25
+ template % { field: field, value: quote_value ? quote(value) : value }
26
+ end
27
+
28
+ def quote(value)
29
+ self.class.quote(value)
30
+ end
31
+
32
+ class << self
33
+ def quote(value)
34
+ # Derived from Blacklight::Solr::SearchBuilderBehavior#solr_param_quote
35
+ unless value =~ /\A[a-zA-Z0-9$_\-\^]+\z/
36
+ QUOTE + value.gsub("'", "\\\\\'").gsub('"', "\\\\\"") + QUOTE
37
+ else
38
+ value
39
+ end
40
+ end
41
+
42
+ # Builds a query clause to retrieve the index document by unique key.
43
+ def unique_key(value)
44
+ term(UniqueKeyField.instance, value)
45
+ end
46
+ alias_method :id, :unique_key
47
+
48
+ def where(field, value)
49
+ values = Array(value)
50
+ if values.size > 1
51
+ disjunction(field, values)
52
+ else
53
+ new(field: field, value: values.first, quote_value: true)
54
+ end
55
+ end
56
+
57
+ # Builds a query clause to filter where field does not have the given value.
58
+ def negative(field, value)
59
+ new(field: field, value: value, template: NEGATIVE_QUERY, quote_value: true)
60
+ end
61
+
62
+ # Builds a query clause to filter where field is present (i.e, has any value)
63
+ def present(field)
64
+ new(field: field, value: ANY_VALUE)
65
+ end
66
+
67
+ # Builds a query clause to filter where field is NOT present (no values)
68
+ def absent(field)
69
+ new(field: field, value: ANY_VALUE, template: NEGATIVE_QUERY)
70
+ end
71
+
72
+ # Builds a query clause to filter where field contains at least one of a set of values.
73
+ def disjunction(field, values)
74
+ value = values.map { |v| quote(v) }.join(" ")
75
+ new(field: field, value: value, template: DISJUNCTION)
76
+ end
77
+
78
+ # Builds a Solr join clause
79
+ # @see https://wiki.apache.org/solr/Join
80
+ def join(from:, to:, where:)
81
+ field, value = where.to_a.first
82
+ from_field = FieldAttribute.coerce(from)
83
+ to_field = FieldAttribute.coerce(to)
84
+ template = "{!join from=#{from_field} to=#{to_field}}%{field}:%{value}"
85
+ new(field: field, value: value, template: template, quote_value: true)
86
+ end
87
+
88
+ # Builds a query clause to filter where date field value is earlier than a date/time value.
89
+ def before(field, value)
90
+ new(field: field, value: "[* TO %s]" % Ddr::Utils.solr_date(value))
91
+ end
92
+ alias_method :before_date_time, :before
93
+
94
+ # Builds a query clause to filter where date field value is earlier than a number of days before now.
95
+ def before_days(field, value)
96
+ new(field: field, value: "[* TO NOW-%iDAYS]" % value)
97
+ end
98
+
99
+ # Builds a "term query" clause to filter where field contains value.
100
+ def term(field, value)
101
+ new(field: field, value: value, template: TERM_QUERY)
102
+ end
103
+
104
+ # Builds a regular expression query clause
105
+ def regexp(field, value)
106
+ val = value.gsub(/\//, "\\/")
107
+ new(field: field, value: val, template: REGEXP_QUERY)
108
+ end
109
+ end
110
+
111
+ end
112
+ end
@@ -0,0 +1,40 @@
1
+ module Ddr::Index
2
+ class QueryParams
3
+
4
+ attr_reader :query
5
+
6
+ def initialize(query)
7
+ @query = query
8
+ end
9
+
10
+ def params
11
+ { q: q_param,
12
+ fq: filter_queries,
13
+ fl: fields,
14
+ sort: sort,
15
+ rows: rows,
16
+ }.select { |k, v| v.present? }
17
+ end
18
+
19
+ def q_param
20
+ query.q.to_s
21
+ end
22
+
23
+ def filter_queries
24
+ query.filter_clauses.map(&:to_s)
25
+ end
26
+
27
+ def fields
28
+ query.fields.join(",")
29
+ end
30
+
31
+ def sort
32
+ query.sort.join(",")
33
+ end
34
+
35
+ def rows
36
+ query.rows
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,102 @@
1
+ module Ddr::Index
2
+ class QueryResult < AbstractQueryResult
3
+ extend Deprecation
4
+
5
+ PAGE_SIZE = 1000
6
+
7
+ delegate :csv, to: :query
8
+
9
+ def each(&block)
10
+ if params[:rows]
11
+ each_unpaginated(&block)
12
+ else
13
+ each_paginated(&block)
14
+ end
15
+ end
16
+
17
+ def each_unpaginated(&block)
18
+ Connection.select(params).docs.each(&block)
19
+ end
20
+
21
+ def each_paginated(&block)
22
+ pages.each { |pg| pg.each(&block) }
23
+ end
24
+
25
+ def pids
26
+ Deprecation.warn(QueryResult,
27
+ "`pids` is deprecated; use `ids` instead." \
28
+ " (called from #{caller.first})"
29
+ )
30
+ ids
31
+ end
32
+
33
+ def ids
34
+ Enumerator.new do |e|
35
+ each do |doc|
36
+ e << doc[Fields::ID]
37
+ end
38
+ end
39
+ end
40
+
41
+ def each_pid(&block)
42
+ Deprecation.warn(QueryResult,
43
+ "`each_pid` is deprecated; use `each_id` instead." \
44
+ " (called from #{caller.first})"
45
+ )
46
+ each_id(&block)
47
+ end
48
+
49
+ def each_id(&block)
50
+ ids.each(&block)
51
+ end
52
+
53
+ def docs
54
+ Enumerator.new do |e|
55
+ each do |doc|
56
+ e << DocumentBuilder.build(doc)
57
+ end
58
+ end
59
+ end
60
+
61
+ def objects
62
+ Enumerator.new do |e|
63
+ each_id do |id|
64
+ e << ActiveFedora::Base.find(id)
65
+ end
66
+ end
67
+ end
68
+
69
+ def facet_fields
70
+ response = Connection.select(params, rows: 0)
71
+ response.facet_fields.each_with_object({}) do |(field, values), memo|
72
+ memo[field] = Hash[*values]
73
+ end
74
+ end
75
+
76
+ def all
77
+ to_a
78
+ end
79
+
80
+ def pages
81
+ num = 1
82
+ Enumerator.new do |e|
83
+ loop do
84
+ pg = page(num)
85
+ e << pg
86
+ unless pg.has_next?
87
+ break
88
+ end
89
+ num += 1
90
+ end
91
+ end
92
+ end
93
+
94
+ def page(num)
95
+ page_params = params.dup
96
+ page_size = page_params.delete(:rows) || PAGE_SIZE
97
+ response = Connection.page(num, page_size, "select", params: page_params)
98
+ response.docs
99
+ end
100
+
101
+ end
102
+ end