mongoid 7.1.0.rc0 → 7.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/CHANGELOG.md +6 -6
  5. data/LICENSE +1 -1
  6. data/README.md +5 -5
  7. data/Rakefile +14 -0
  8. data/lib/config/locales/en.yml +5 -5
  9. data/lib/mongoid.rb +3 -2
  10. data/lib/mongoid/association/accessors.rb +37 -2
  11. data/lib/mongoid/association/embedded/embeds_many.rb +2 -1
  12. data/lib/mongoid/association/embedded/embeds_one.rb +2 -1
  13. data/lib/mongoid/association/many.rb +3 -2
  14. data/lib/mongoid/association/proxy.rb +6 -4
  15. data/lib/mongoid/association/referenced/belongs_to/binding.rb +1 -1
  16. data/lib/mongoid/association/referenced/belongs_to/eager.rb +38 -2
  17. data/lib/mongoid/association/referenced/eager.rb +29 -9
  18. data/lib/mongoid/association/referenced/has_many/enumerable.rb +2 -22
  19. data/lib/mongoid/association/referenced/has_many/proxy.rb +3 -2
  20. data/lib/mongoid/atomic.rb +13 -3
  21. data/lib/mongoid/attributes.rb +28 -20
  22. data/lib/mongoid/clients/factory.rb +2 -2
  23. data/lib/mongoid/clients/options.rb +8 -8
  24. data/lib/mongoid/clients/sessions.rb +20 -4
  25. data/lib/mongoid/clients/storage_options.rb +5 -5
  26. data/lib/mongoid/config.rb +42 -12
  27. data/lib/mongoid/config/options.rb +5 -2
  28. data/lib/mongoid/contextual.rb +5 -4
  29. data/lib/mongoid/contextual/geo_near.rb +3 -2
  30. data/lib/mongoid/contextual/map_reduce.rb +3 -2
  31. data/lib/mongoid/contextual/mongo.rb +2 -1
  32. data/lib/mongoid/criteria.rb +23 -4
  33. data/lib/mongoid/criteria/modifiable.rb +2 -1
  34. data/lib/mongoid/criteria/queryable/extensions/numeric.rb +1 -1
  35. data/lib/mongoid/criteria/queryable/extensions/regexp.rb +6 -6
  36. data/lib/mongoid/criteria/queryable/extensions/time_with_zone.rb +12 -0
  37. data/lib/mongoid/criteria/queryable/mergeable.rb +75 -8
  38. data/lib/mongoid/criteria/queryable/pipeline.rb +3 -2
  39. data/lib/mongoid/criteria/queryable/selectable.rb +120 -13
  40. data/lib/mongoid/criteria/queryable/storable.rb +104 -99
  41. data/lib/mongoid/errors/eager_load.rb +2 -0
  42. data/lib/mongoid/errors/no_client_config.rb +2 -2
  43. data/lib/mongoid/errors/no_default_client.rb +1 -1
  44. data/lib/mongoid/extensions/hash.rb +4 -2
  45. data/lib/mongoid/extensions/regexp.rb +1 -1
  46. data/lib/mongoid/fields.rb +2 -1
  47. data/lib/mongoid/fields/standard.rb +2 -1
  48. data/lib/mongoid/fields/validators/macro.rb +4 -1
  49. data/lib/mongoid/findable.rb +5 -4
  50. data/lib/mongoid/interceptable.rb +5 -1
  51. data/lib/mongoid/matchable/regexp.rb +2 -2
  52. data/lib/mongoid/persistable/pushable.rb +11 -2
  53. data/lib/mongoid/persistence_context.rb +6 -6
  54. data/lib/mongoid/query_cache.rb +61 -18
  55. data/lib/mongoid/railties/database.rake +7 -0
  56. data/lib/mongoid/serializable.rb +10 -2
  57. data/lib/mongoid/shardable.rb +56 -4
  58. data/lib/mongoid/tasks/database.rake +10 -5
  59. data/lib/mongoid/tasks/database.rb +83 -0
  60. data/lib/mongoid/timestamps/timeless.rb +3 -1
  61. data/lib/mongoid/validatable/uniqueness.rb +1 -1
  62. data/lib/mongoid/version.rb +1 -1
  63. data/lib/rails/generators/mongoid/config/templates/mongoid.yml +32 -23
  64. data/lib/rails/generators/mongoid/model/templates/model.rb.tt +1 -1
  65. data/spec/app/models/coding.rb +4 -0
  66. data/spec/app/models/coding/pull_request.rb +12 -0
  67. data/spec/app/models/delegating_patient.rb +16 -0
  68. data/spec/app/models/passport.rb +1 -0
  69. data/spec/app/models/phone.rb +1 -0
  70. data/spec/app/models/publication.rb +5 -0
  71. data/spec/app/models/publication/encyclopedia.rb +12 -0
  72. data/spec/app/models/publication/review.rb +14 -0
  73. data/spec/integration/app_spec.rb +254 -0
  74. data/spec/integration/associations/embedded_spec.rb +54 -0
  75. data/spec/integration/associations/has_many_spec.rb +34 -0
  76. data/spec/integration/associations/has_one_spec.rb +34 -0
  77. data/spec/integration/bson_regexp_raw_spec.rb +20 -0
  78. data/spec/integration/criteria/date_field_spec.rb +41 -0
  79. data/spec/integration/criteria/logical_spec.rb +13 -0
  80. data/spec/integration/document_spec.rb +22 -0
  81. data/spec/integration/shardable_spec.rb +149 -0
  82. data/spec/lite_spec_helper.rb +15 -4
  83. data/spec/mongoid/association/accessors_spec.rb +238 -63
  84. data/spec/mongoid/association/embedded/embeds_many_models.rb +19 -0
  85. data/spec/mongoid/association/embedded/embeds_many_spec.rb +10 -0
  86. data/spec/mongoid/association/embedded/embeds_one_spec.rb +0 -2
  87. data/spec/mongoid/association/referenced/belongs_to/eager_spec.rb +193 -10
  88. data/spec/mongoid/association/referenced/has_and_belongs_to_many/proxy_spec.rb +140 -1
  89. data/spec/mongoid/association/referenced/has_many/enumerable_spec.rb +146 -68
  90. data/spec/mongoid/association/referenced/has_many/proxy_spec.rb +2 -1
  91. data/spec/mongoid/attributes_spec.rb +19 -7
  92. data/spec/mongoid/changeable_spec.rb +23 -0
  93. data/spec/mongoid/clients/factory_spec.rb +8 -8
  94. data/spec/mongoid/clients/options_spec.rb +11 -11
  95. data/spec/mongoid/clients/sessions_spec.rb +8 -4
  96. data/spec/mongoid/clients/transactions_spec.rb +20 -8
  97. data/spec/mongoid/clients_spec.rb +2 -2
  98. data/spec/mongoid/contextual/atomic_spec.rb +22 -11
  99. data/spec/mongoid/contextual/geo_near_spec.rb +11 -2
  100. data/spec/mongoid/contextual/map_reduce_spec.rb +20 -5
  101. data/spec/mongoid/contextual/mongo_spec.rb +76 -53
  102. data/spec/mongoid/criteria/queryable/extensions/regexp_raw_spec.rb +1 -1
  103. data/spec/mongoid/criteria/queryable/extensions/regexp_spec.rb +7 -7
  104. data/spec/mongoid/criteria/queryable/extensions/string_spec.rb +1 -1
  105. data/spec/mongoid/criteria/queryable/extensions/time_spec.rb +19 -7
  106. data/spec/mongoid/criteria/queryable/extensions/time_with_zone_spec.rb +28 -1
  107. data/spec/mongoid/criteria/queryable/mergeable_spec.rb +45 -12
  108. data/spec/mongoid/criteria/queryable/selectable_logical_spec.rb +1051 -392
  109. data/spec/mongoid/criteria/queryable/selectable_spec.rb +52 -0
  110. data/spec/mongoid/criteria/queryable/storable_spec.rb +80 -2
  111. data/spec/mongoid/criteria_spec.rb +36 -2
  112. data/spec/mongoid/document_fields_spec.rb +29 -0
  113. data/spec/mongoid/document_persistence_context_spec.rb +33 -0
  114. data/spec/mongoid/errors/no_client_config_spec.rb +2 -2
  115. data/spec/mongoid/errors/no_client_database_spec.rb +3 -3
  116. data/spec/mongoid/errors/no_client_hosts_spec.rb +3 -3
  117. data/spec/mongoid/fields_spec.rb +24 -1
  118. data/spec/mongoid/indexable_spec.rb +6 -4
  119. data/spec/mongoid/interceptable_spec.rb +62 -0
  120. data/spec/mongoid/interceptable_spec_models.rb +76 -0
  121. data/spec/mongoid/matchable/default_spec.rb +1 -1
  122. data/spec/mongoid/matchable/regexp_spec.rb +2 -2
  123. data/spec/mongoid/matchable_spec.rb +2 -2
  124. data/spec/mongoid/persistable/pushable_spec.rb +55 -1
  125. data/spec/mongoid/query_cache_spec.rb +77 -9
  126. data/spec/mongoid/relations/proxy_spec.rb +1 -1
  127. data/spec/mongoid/scopable_spec.rb +2 -1
  128. data/spec/mongoid/serializable_spec.rb +129 -18
  129. data/spec/mongoid/shardable_models.rb +61 -0
  130. data/spec/mongoid/shardable_spec.rb +69 -16
  131. data/spec/mongoid/tasks/database_rake_spec.rb +13 -13
  132. data/spec/mongoid/tasks/database_spec.rb +1 -1
  133. data/spec/spec_helper.rb +2 -31
  134. data/spec/support/child_process_helper.rb +76 -0
  135. data/spec/support/cluster_config.rb +3 -3
  136. data/spec/support/constraints.rb +26 -10
  137. data/spec/support/expectations.rb +3 -1
  138. data/spec/support/helpers.rb +11 -0
  139. data/spec/support/lite_constraints.rb +22 -0
  140. data/spec/support/session_registry.rb +50 -0
  141. data/spec/support/spec_config.rb +12 -4
  142. metadata +518 -480
  143. metadata.gz.sig +0 -0
@@ -108,6 +108,89 @@ module Mongoid
108
108
  end.compact
109
109
  end
110
110
 
111
+ # Shard collections for models that declare shard keys.
112
+ #
113
+ # Returns the model classes that have had their collections sharded,
114
+ # including model classes whose collections had already been sharded
115
+ # prior to the invocation of this method.
116
+ #
117
+ # @example Shard all collections
118
+ # Mongoid::Tasks::Database.shard_collections
119
+ #
120
+ # @return [ Array<Class> ] The sharded models
121
+ def shard_collections(models = ::Mongoid.models)
122
+ models.map do |model|
123
+ next if model.shard_config.nil?
124
+
125
+ if model.embedded? && !model.cyclic?
126
+ logger.warn("MONGOID: #{model} has shard config but is emdedded")
127
+ next
128
+ end
129
+
130
+ unless model.collection.cluster.sharded?
131
+ logger.warn("MONGOID: #{model} has shard config but is not persisted in a sharded cluster: #{model.collection.cluster.summary}")
132
+ next
133
+ end
134
+
135
+ # Database of the collection must exist in order to run collStats.
136
+ # Depending on server version, the collection itself must also
137
+ # exist.
138
+ # MongoDB does not have a command to create the database; the best
139
+ # approximation of it is to create the collection we want.
140
+ # On older servers, creating a collection that already exists is
141
+ # an error.
142
+ # Additionally, 3.6 and potentially older servers do not provide
143
+ # the error code when they are asked to collStats a non-existent
144
+ # collection (https://jira.mongodb.org/browse/SERVER-50070).
145
+ begin
146
+ stats = model.collection.database.command(collStats: model.collection.name).first
147
+ rescue Mongo::Error::OperationFailure => exc
148
+ # Code 26 is database does not exist.
149
+ # Code 8 is collection does not exist, as of 4.0.
150
+ # On 3.6 and earlier match the text of exception message.
151
+ if exc.code == 26 || exc.code == 8 ||
152
+ exc.code.nil? && exc.message =~ /not found/
153
+ then
154
+ model.collection.create
155
+
156
+ stats = model.collection.database.command(collStats: model.collection.name).first
157
+ else
158
+ raise
159
+ end
160
+ end
161
+
162
+ stats = model.collection.database.command(collStats: model.collection.name).first
163
+ if stats[:sharded]
164
+ logger.info("MONGOID: #{model.collection.namespace} is already sharded for #{model}")
165
+ next model
166
+ end
167
+
168
+ admin_db = model.collection.client.use(:admin).database
169
+
170
+ begin
171
+ admin_db.command(enableSharding: model.collection.database.name)
172
+ rescue Mongo::Error::OperationFailure => exc
173
+ # Server 2.6 fails if sharding is already enabled
174
+ if exc.code == 23 || exc.code.nil? && exc.message =~ /already enabled/
175
+ # Nothing
176
+ else
177
+ raise
178
+ end
179
+ end
180
+
181
+ begin
182
+ admin_db.command(shardCollection: model.collection.namespace, **model.shard_config)
183
+ rescue Mongo::Error::OperationFailure => e
184
+ logger.error("MONGOID: Failed to shard collection #{model.collection.namespace} for #{model}: #{e.class}: #{e}")
185
+ next
186
+ end
187
+
188
+ logger.info("MONGOID: Sharded collection #{model.collection.namespace} for #{model}")
189
+
190
+ model
191
+ end.compact
192
+ end
193
+
111
194
  private
112
195
 
113
196
  def logger
@@ -44,11 +44,13 @@ module Mongoid
44
44
  end
45
45
 
46
46
  class << self
47
+ extend Forwardable
47
48
 
48
49
  def timeless_table
49
50
  Thread.current['[mongoid]:timeless'] ||= Hash.new
50
51
  end
51
- delegate :[]=, :[], to: :timeless_table
52
+
53
+ def_delegators :timeless_table, :[]=, :[]
52
54
  end
53
55
 
54
56
  private
@@ -150,7 +150,7 @@ module Mongoid
150
150
  #
151
151
  # @since 2.3.0
152
152
  def filter(value)
153
- !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}$/i : value
153
+ !case_sensitive? && value ? /\A#{Regexp.escape(value.to_s)}\z/i : value
154
154
  end
155
155
 
156
156
  # Scope the criteria to the scope options provided.
@@ -2,5 +2,5 @@
2
2
  # encoding: utf-8
3
3
 
4
4
  module Mongoid
5
- VERSION = "7.1.0.rc0"
5
+ VERSION = "7.1.5"
6
6
  end
@@ -114,12 +114,37 @@ development:
114
114
 
115
115
  # Configure Mongoid specific options. (optional)
116
116
  options:
117
- # Includes the root model name in json serialization. (default: false)
117
+ # Application name that is printed to the mongodb logs upon establishing
118
+ # a connection in server versions >= 3.4. Note that the name cannot
119
+ # exceed 128 bytes. It is also used as the database name if the
120
+ # database name is not explicitly defined. (default: nil)
121
+ # app_name: MyApplicationName
122
+
123
+ # Create indexes in background by default. (default: false)
124
+ # background_indexing: false
125
+
126
+ # Mark belongs_to associations as required by default, so that saving a
127
+ # model with a missing belongs_to association will trigger a validation
128
+ # error. (default: true)
129
+ # belongs_to_required_by_default: true
130
+
131
+ # Raise an exception when a field is redefined. (default: false)
132
+ # duplicate_fields_exception: false
133
+
134
+ # Include the root model name in json serialization. (default: false)
118
135
  # include_root_in_json: false
119
136
 
120
137
  # Include the _type field in serialization. (default: false)
121
138
  # include_type_for_serialization: false
122
139
 
140
+ # Whether to join nested persistence contexts for atomic operations
141
+ # to parent contexts by default. (default: false)
142
+ # join_contexts: false
143
+
144
+ # Set the Mongoid and Ruby driver log levels when Mongoid is not using
145
+ # Ruby on Rails logger instance. (default: :info)
146
+ # log_level: :info
147
+
123
148
  # Preload all models in development, needed when models use
124
149
  # inheritance. (default: false)
125
150
  # preload_models: false
@@ -132,32 +157,16 @@ development:
132
157
  # existing method. (default: false)
133
158
  # scope_overwrite_exception: false
134
159
 
135
- # Raise an error when defining a field with the same name as an
136
- # existing method. (default: false)
137
- # duplicate_fields_exception: false
138
-
139
- # Use Active Support's time zone in conversions. (default: true)
160
+ # Use ActiveSupport's time zone in time operations instead of
161
+ # the Ruby default time zone. See the time zone section below for
162
+ # further information. (default: true)
140
163
  # use_activesupport_time_zone: true
141
164
 
142
- # Ensure all times are UTC in the app side. (default: false)
165
+ # Return stored times as UTC. See the time zone section below for
166
+ # further information. Most applications should not use this option.
167
+ # (default: false)
143
168
  # use_utc: false
144
169
 
145
- # Set the Mongoid and Ruby driver log levels when not in a Rails
146
- # environment. The Mongoid logger will be set to the Rails logger
147
- # otherwise.(default: :info)
148
- # log_level: :info
149
-
150
- # Control whether `belongs_to` association is required. By default
151
- # `belongs_to` will trigger a validation error if the association
152
- # is not present. (default: true)
153
- # belongs_to_required_by_default: true
154
-
155
- # Application name that is printed to the mongodb logs upon establishing a
156
- # connection in server versions >= 3.4. Note that the name cannot exceed 128 bytes.
157
- # app_name: MyApplicationName
158
-
159
- # Use background indexes by default if `background` option not specified. (default: false)
160
- # background_indexing: false
161
170
  test:
162
171
  clients:
163
172
  default:
@@ -13,7 +13,7 @@ class <%= class_name %><%= " < #{options[:parent].classify}" if options[:parent]
13
13
  field :<%= attribute.name %>, type: <%= attribute.type_class %>
14
14
  <% end -%>
15
15
  <% attributes.select{|attr| attr.reference? }.each do |attribute| -%>
16
- embedded_in :<%= attribute.name%>
16
+ belongs_to :<%= attribute.name%>
17
17
  <% end -%>
18
18
  end
19
19
  <% end -%>
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'app/models/coding/pull_request'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Coding
5
+ class PullRequest
6
+ include Mongoid::Document
7
+
8
+ field :title, type: String
9
+
10
+ has_many :reviews, class_name: 'Publication::Review', as: :reviewable
11
+ end
12
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ class DelegatingPatient
5
+ include Mongoid::Document
6
+
7
+ embeds_one :email
8
+
9
+ # Instance level delegation
10
+ delegate :address, to: :email
11
+
12
+ class << self
13
+ # Class level delegation
14
+ delegate :default_client, to: ::Mongoid
15
+ end
16
+ end
@@ -4,5 +4,6 @@
4
4
  class Passport
5
5
  include Mongoid::Document
6
6
  field :number, type: String
7
+ field :country, type: String
7
8
  embedded_in :person, autobuild: true
8
9
  end
@@ -7,6 +7,7 @@ class Phone
7
7
  field :_id, type: String, overwrite: true, default: ->{ number }
8
8
 
9
9
  field :number
10
+ field :landline, type: Boolean
10
11
  embeds_one :country_code
11
12
  embedded_in :person
12
13
  end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'app/models/publication/encyclopedia'
5
+ require 'app/models/publication/review'
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Publication
5
+ class Encyclopedia
6
+ include Mongoid::Document
7
+
8
+ field :title, type: String
9
+
10
+ has_many :reviews, class_name: 'Publication::Review', as: :reviewable
11
+ end
12
+ end
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ module Publication
5
+ class Review
6
+ include Mongoid::Document
7
+
8
+ field :summary
9
+
10
+ belongs_to :reviewable, polymorphic: true
11
+ belongs_to :reviewer, polymorphic: true
12
+ belongs_to :template
13
+ end
14
+ end
@@ -0,0 +1,254 @@
1
+ # frozen_string_literal: true
2
+ # encoding: utf-8
3
+
4
+ require 'spec_helper'
5
+
6
+ BASE = File.join(File.dirname(__FILE__), '../..')
7
+ TMP_BASE = File.join(BASE, 'tmp')
8
+
9
+ describe 'Mongoid application tests' do
10
+ before(:all) do
11
+ unless SpecConfig.instance.app_tests?
12
+ skip 'Set APP_TESTS=1 in environment to run application tests'
13
+ end
14
+
15
+ require 'fileutils'
16
+ require 'support/child_process_helper'
17
+ require 'open-uri'
18
+
19
+ FileUtils.mkdir_p(TMP_BASE)
20
+ end
21
+
22
+ context 'demo application - sinatra' do
23
+ it 'runs' do
24
+ clone_application(
25
+ 'https://github.com/mongoid/mongoid-demo',
26
+ subdir: 'sinatra-minimal',
27
+ ) do
28
+
29
+ process = ChildProcess.build(*%w(bundle exec ruby app.rb))
30
+ process.environment.update(clean_env)
31
+ process.io.inherit!
32
+ process.start
33
+
34
+ begin
35
+ # JRuby needs a long timeout
36
+ wait_for_port(4567, 20)
37
+ sleep 1
38
+
39
+ uri = URI.parse('http://localhost:4567/posts')
40
+ resp = JSON.parse(uri.open.read)
41
+ ensure
42
+ Process.kill('TERM', process.pid)
43
+ status = process.wait
44
+ end
45
+
46
+ resp.should == []
47
+
48
+ status.should == 0
49
+ end
50
+ end
51
+ end
52
+
53
+ context 'demo application - rails-api' do
54
+ ['~> 6.0.0'].each do |rails_version|
55
+ context "with rails #{rails_version}" do
56
+ it 'runs' do
57
+ clone_application(
58
+ 'https://github.com/mongoid/mongoid-demo',
59
+ subdir: 'rails-api',
60
+ rails_version: rails_version,
61
+ ) do
62
+
63
+ process = ChildProcess.build(*%w(bundle exec rails s))
64
+ process.environment.update(clean_env)
65
+ process.io.inherit!
66
+ process.start
67
+
68
+ begin
69
+ # JRuby needs a long timeout
70
+ wait_for_port(3000, 30)
71
+ sleep 1
72
+
73
+ uri = URI.parse('http://localhost:3000/posts')
74
+ resp = JSON.parse(uri.open.read)
75
+ ensure
76
+ Process.kill('TERM', process.pid)
77
+ status = process.wait
78
+ end
79
+
80
+ resp.should == []
81
+
82
+ # 143 = 128 + 15
83
+ [0, 15, 143].should include(status)
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ context 'new application - rails' do
91
+ ['~> 5.1.0', '~> 5.2.0', '~> 6.0.0'].each do |rails_version|
92
+ context "with rails #{rails_version}" do
93
+ it 'creates' do
94
+ ChildProcessHelper.check_call(%w(gem uni rails -a))
95
+ ChildProcessHelper.check_call(%w(gem install rails --no-document -v) + [rails_version])
96
+
97
+ Dir.chdir(TMP_BASE) do
98
+ FileUtils.rm_rf('mongoid-test')
99
+ ChildProcessHelper.check_call(%w(rails new mongoid-test --skip-spring --skip-active-record), env: clean_env)
100
+
101
+ Dir.chdir('mongoid-test') do
102
+ adjust_app_gemfile
103
+ ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
104
+
105
+ ChildProcessHelper.check_call(%w(rails g model post), env: clean_env)
106
+ ChildProcessHelper.check_call(%w(rails g model comment post:belongs_to), env: clean_env)
107
+
108
+ # https://jira.mongodb.org/browse/MONGOID-4885
109
+ comment_text = File.read('app/models/comment.rb')
110
+ comment_text.should =~ /belongs_to :post/
111
+ comment_text.should_not =~ /embedded_in :post/
112
+ end
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
118
+
119
+ context 'local test applications' do
120
+ let(:client) { Mongoid.default_client }
121
+
122
+ describe 'create_indexes rake task' do
123
+
124
+ APP_PATH = File.join(File.dirname(__FILE__), '../../test-apps/rails-api')
125
+
126
+ %w(development production).each do |rails_env|
127
+ context "in #{rails_env}" do
128
+
129
+ %w(classic zeitwerk).each do |autoloader|
130
+ context "with #{autoloader} autoloader" do
131
+
132
+ let(:env) do
133
+ clean_env.merge(RAILS_ENV: rails_env, AUTOLOADER: autoloader)
134
+ end
135
+
136
+ before do
137
+ Dir.chdir(APP_PATH) do
138
+ remove_bundler_req
139
+ ChildProcessHelper.check_call(%w(bundle install), env: env)
140
+ write_mongoid_yml
141
+ end
142
+
143
+ client['posts'].drop
144
+ client['posts'].create
145
+ end
146
+
147
+ it 'creates an index' do
148
+ index = client['posts'].indexes.detect do |index|
149
+ index['key'] == {'subject' => 1}
150
+ end
151
+ index.should be nil
152
+
153
+ ChildProcessHelper.check_call(%w(rake db:mongoid:create_indexes),
154
+ cwd: APP_PATH, env: env)
155
+
156
+ index = client['posts'].indexes.detect do |index|
157
+ index['key'] == {'subject' => 1}
158
+ end
159
+ index.should be_a(Hash)
160
+ end
161
+ end
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end
167
+
168
+ def clone_application(repo_url, subdir: nil, rails_version: nil)
169
+ Dir.chdir(TMP_BASE) do
170
+ FileUtils.rm_rf(File.basename(repo_url))
171
+ ChildProcessHelper.check_call(%w(git clone) + [repo_url])
172
+ Dir.chdir(File.join(*[File.basename(repo_url), subdir].compact)) do
173
+ adjust_app_gemfile(rails_version: rails_version)
174
+ ChildProcessHelper.check_call(%w(bundle install), env: clean_env)
175
+ puts `git diff`
176
+
177
+ write_mongoid_yml
178
+
179
+ yield
180
+ end
181
+ end
182
+ end
183
+
184
+ def write_mongoid_yml
185
+ env_config = {'clients' => {'default' => {
186
+ # TODO massive hack, will fail if uri specifies a database name or
187
+ # any uri options
188
+ 'uri' => "#{SpecConfig.instance.uri_str}/mongoid_test",
189
+ }}}
190
+ config = {'development' => env_config, 'production' => env_config}
191
+ File.open('config/mongoid.yml', 'w') do |f|
192
+ f << YAML.dump(config)
193
+ end
194
+ end
195
+
196
+ def adjust_app_gemfile(rails_version: nil)
197
+ remove_bundler_req
198
+
199
+ gemfile_lines = IO.readlines('Gemfile')
200
+ gemfile_lines.delete_if do |line|
201
+ line =~ /mongoid/
202
+ end
203
+ gemfile_lines << "gem 'mongoid', path: '#{File.expand_path(BASE)}'\n"
204
+ if rails_version
205
+ gemfile_lines.delete_if do |line|
206
+ line =~ /rails/
207
+ end
208
+ gemfile_lines << "gem 'rails', '#{rails_version}'\n"
209
+ end
210
+ File.open('Gemfile', 'w') do |f|
211
+ f << gemfile_lines.join
212
+ end
213
+ end
214
+
215
+ def remove_bundler_req
216
+ lock_lines = IO.readlines('Gemfile.lock')
217
+ # Get rid of the bundled with line so that whatever bundler is installed
218
+ # on the system is usable with the application.
219
+ if i = lock_lines.index("BUNDLED WITH\n")
220
+ lock_lines.slice!(i, 2)
221
+ File.open('Gemfile.lock', 'w') do |f|
222
+ f << lock_lines.join
223
+ end
224
+ end
225
+ end
226
+
227
+ def remove_spring
228
+ # Spring produces this error in Evergreen:
229
+ # /data/mci/280eb2ecf4fd69208e2106cd3af526f1/src/rubies/ruby-2.7.0/lib/ruby/gems/2.7.0/gems/spring-2.1.0/lib/spring/client/run.rb:26:
230
+ # in `initialize': too long unix socket path (126bytes given but 108bytes max) (ArgumentError)
231
+ # Is it trying to create unix sockets in current directory?
232
+ # https://stackoverflow.com/questions/30302021/rails-runner-without-spring
233
+ ChildProcessHelper.check_call(%w(bin/spring binstub --remove --all), env: clean_env)
234
+ end
235
+
236
+ def clean_env
237
+ @clean_env ||= Hash[ENV.keys.grep(/BUNDLE|RUBYOPT/).map { |k| [k, nil ] }]
238
+ end
239
+
240
+ def wait_for_port(port, timeout)
241
+ deadline = Time.now + timeout
242
+ loop do
243
+ begin
244
+ Socket.tcp('localhost', port, nil, nil, connect_timeout: 0.5) do |socket|
245
+ return
246
+ end
247
+ rescue IOError, SystemCallError
248
+ if Time.now > deadline
249
+ raise
250
+ end
251
+ end
252
+ end
253
+ end
254
+ end