_attributes=+
26 | # method on that object. Within the writer method call +assign_nested_attributes+, passing in
27 | # the association name and attributes.
28 | #
29 | # Let's say we have a parent Recipe with Ingredient children.
30 | #
31 | # Start by defining within the Recipe model:
32 | # * an attr_accessor of +:ingredients+
33 | # * a writer method named +ingredients_attributes=+
34 | # * the +validates_associated+ method can be used to validate the nested objects
35 | #
36 | # Example:
37 | # class Recipe
38 | # attr_accessor :ingredients
39 | # validates :ingredients, presence: true
40 | # validates_associated :ingredients
41 | #
42 | # def ingredients_attributes=(attributes)
43 | # assign_nested_attributes(:ingredients, attributes)
44 | # end
45 | # end
46 | #
47 | # You may also set a +:reject_if+ proc to silently ignore any new record hashes if they fail to
48 | # pass your criteria. For example:
49 | #
50 | # class Recipe
51 | # def ingredients_attributes=(attributes)
52 | # reject_proc = proc { |attributes| attributes['name'].blank? }
53 | # assign_nested_attributes(:ingredients, attributes, reject_if: reject_proc)
54 | # end
55 | # end
56 | #
57 | # Alternatively, +:reject_if+ also accepts a symbol for using methods:
58 | #
59 | # class Recipe
60 | # def ingredients_attributes=(attributes)
61 | # reject_proc = proc { |attributes| attributes['name'].blank? }
62 | # assign_nested_attributes(:ingredients, attributes, reject_if: reject_recipes)
63 | # end
64 | #
65 | # def reject_recipes(attributes)
66 | # attributes['name'].blank?
67 | # end
68 | # end
69 | #
70 | # Within the parent model +valid?+ will validate the parent and associated children and
71 | # +nested_models+ will return the child objects. If the nested form submitted params contained
72 | # a truthy +_destroy+ key, the appropriate nested_models will have +marked_for_destruction+ set
73 | # to True.
74 | #
75 | # Created by Bryce McLean on 2016-12-06.
76 | #
77 | module NestedAttr
78 | extend ActiveSupport::Concern
79 | include ActiveModel::Model
80 |
81 | included do
82 | attr_accessor :nested_attributes, :marked_for_destruction, :_destroy
83 | end
84 |
85 | def mark_for_destruction
86 | @marked_for_destruction = true
87 | end
88 |
89 | def marked_for_destruction?
90 | @marked_for_destruction
91 | end
92 |
93 | def nested_attributes?
94 | nested_attributes.is_a?(Array) && !nested_attributes.empty?
95 | end
96 |
97 | ##
98 | # For each attribute name in nested_attributes extract and return the nested model objects.
99 | #
100 | def nested_models
101 | model_entities = []
102 | nested_attributes.each { |attr| model_entities << send(attr.to_sym) } if nested_attributes?
103 | model_entities.flatten
104 | end
105 |
106 | def nested_model_class_names
107 | entity_kinds = []
108 | nested_models.each { |x| entity_kinds << x.class.name } if nested_attributes?
109 | entity_kinds.uniq
110 | end
111 |
112 | def nested_errors
113 | errors = []
114 | if nested_attributes?
115 | nested_attributes.each do |attr|
116 | send(attr.to_sym).each { |child| errors << child.errors }
117 | end
118 | end
119 | errors
120 | end
121 |
122 | ##
123 | # Assigns the given nested child attributes.
124 | #
125 | # Attribute hashes with an +:id+ value matching an existing associated object will update
126 | # that object. Hashes without an +:id+ value will build a new object for the association.
127 | # Hashes with a matching +:id+ value and a +:_destroy+ key set to a truthy value will mark
128 | # the matched object for destruction.
129 | #
130 | # Pushes a key of the association name onto the parent object's +nested_attributes+ attribute.
131 | # The +nested_attributes+ can be used for determining when the parent has associated children.
132 | #
133 | # @param [Symbol] association_name The attribute name of the associated children.
134 | # @param [ActiveSupport::HashWithIndifferentAccess, ActionController::Parameters] attributes
135 | # The attributes provided by Rails ActionView. Typically new objects will arrive as
136 | # ActiveSupport::HashWithIndifferentAccess and updates as ActionController::Parameters.
137 | # @param [Hash] options The options to control how nested attributes are applied.
138 | #
139 | # @option options [Proc, Symbol] :reject_if Allows you to specify a Proc or a Symbol pointing
140 | # to a method that checks whether a record should be built for a certain attribute
141 | # hash. The hash is passed to the supplied Proc or the method and it should return either
142 | # +true+ or +false+. Passing +:all_blank+ instead of a Proc will create a proc
143 | # that will reject a record where all the attributes are blank.
144 | #
145 | # The following example will update the amount of the ingredient with ID 1, build a new
146 | # associated ingredient with the amount of 45, and mark the associated ingredient
147 | # with ID 2 for destruction.
148 | #
149 | # assign_nested_attributes(:ingredients, {
150 | # '0' => { id: '1', amount: '123' },
151 | # '1' => { amount: '45' },
152 | # '2' => { id: '2', _destroy: true }
153 | # })
154 | #
155 | def assign_nested_attributes(association_name, attributes, options = {})
156 | attributes = validate_attributes(attributes)
157 | association_name = association_name.to_sym
158 | send("#{association_name}=", []) if send(association_name).nil?
159 |
160 | attributes.each_value do |params|
161 | if params['id'].blank?
162 | unless reject_new_record?(params, options)
163 | new = association_name.to_c.new(params.except(*UNASSIGNABLE_KEYS))
164 | send(association_name).push(new)
165 | end
166 | else
167 | existing = send(association_name).detect { |record| record.id.to_s == params['id'].to_s }
168 | assign_to_or_mark_for_destruction(existing, params)
169 | end
170 | end
171 | (self.nested_attributes ||= []).push(association_name)
172 | end
173 |
174 | private
175 |
176 | UNASSIGNABLE_KEYS = %w[id _destroy].freeze
177 |
178 | def validate_attributes(attributes)
179 | attributes = attributes.to_h if attributes.respond_to?(:permitted?)
180 | unless attributes.is_a?(Hash)
181 | raise ArgumentError, "Hash expected, got #{attributes.class.name} (#{attributes.inspect})"
182 | end
183 |
184 | attributes
185 | end
186 |
187 | ##
188 | # Updates an object with attributes or marks it for destruction if has_destroy_flag?.
189 | #
190 | def assign_to_or_mark_for_destruction(record, attributes)
191 | record.assign_attributes(attributes.except(*UNASSIGNABLE_KEYS))
192 | record.mark_for_destruction if destroy_flag?(attributes)
193 | end
194 |
195 | ##
196 | # Determines if a hash contains a truthy _destroy key.
197 | #
198 | def destroy_flag?(hash)
199 | [true, 1, '1', 't', 'T', 'true', 'TRUE'].include?(hash['_destroy'])
200 | end
201 |
202 | ##
203 | # Determines if a new record should be rejected by checking if a :reject_if option
204 | # exists and evaluates to +true+.
205 | #
206 | def reject_new_record?(attributes, options)
207 | call_reject_if(attributes, options)
208 | end
209 |
210 | ##
211 | # Determines if a record with the particular +attributes+ should be rejected by calling the
212 | # reject_if Symbol or Proc (if provided in options).
213 | #
214 | # Returns false if there is a +destroy_flag+ on the attributes.
215 | #
216 | def call_reject_if(attributes, options)
217 | return false if destroy_flag?(attributes)
218 |
219 | attributes = attributes.with_indifferent_access
220 | blank_proc = proc { |attrs| attrs.all? { |_key, value| value.blank? } }
221 | options[:reject_if] = blank_proc if options[:reject_if] == :all_blank
222 | case callback = options[:reject_if]
223 | when Symbol
224 | method(callback).arity.zero? ? send(callback) : send(callback, attributes)
225 | when Proc
226 | callback.call(attributes)
227 | else
228 | false
229 | end
230 | end
231 |
232 | # Methods defined here will be class methods whenever we 'include DatastoreUtils'.
233 | module ClassMethods
234 | ##
235 | # Validates whether the associated object or objects are all valid, typically used with
236 | # nested attributes such as multi-model forms.
237 | #
238 | # NOTE: This validation will not fail if the association hasn't been assigned. If you want
239 | # to ensure that the association is both present and guaranteed to be valid, you also need
240 | # to use validates_presence_of.
241 | #
242 | def validates_associated(*attr_names)
243 | validates_with AssociatedValidator, _merge_attributes(attr_names)
244 | end
245 | end
246 |
247 | class AssociatedValidator < ActiveModel::EachValidator
248 | def validate_each(record, attribute, value)
249 | return unless Array(value).reject(&:valid?).any?
250 |
251 | record.errors.add(attribute, :invalid, **options.merge(value: value))
252 | end
253 | end
254 | end
255 | end
256 |
257 | class Symbol
258 | def to_c
259 | to_s.singularize.camelize.constantize
260 | end
261 | end
262 |
--------------------------------------------------------------------------------
/lib/active_model/datastore/property_values.rb:
--------------------------------------------------------------------------------
1 | require 'active_model/type'
2 |
3 | module ActiveModel::Datastore
4 | module PropertyValues
5 | extend ActiveSupport::Concern
6 |
7 | ##
8 | # Sets a default value for the property if not currently set.
9 | #
10 | # Example:
11 | # default_property_value :state, 0
12 | #
13 | # is equivalent to:
14 | # self.state = state.presence || 0
15 | #
16 | # Example:
17 | # default_property_value :enabled, false
18 | #
19 | # is equivalent to:
20 | # self.enabled = false if enabled.nil?
21 | #
22 | def default_property_value(attr, value)
23 | if value.is_a?(TrueClass) || value.is_a?(FalseClass)
24 | send("#{attr.to_sym}=", value) if send(attr.to_sym).nil?
25 | else
26 | send("#{attr.to_sym}=", send(attr.to_sym).presence || value)
27 | end
28 | end
29 |
30 | ##
31 | # Converts the type of the property.
32 | #
33 | # Example:
34 | # format_property_value :weight, :float
35 | #
36 | # is equivalent to:
37 | # self.weight = weight.to_f if weight.present?
38 | #
39 | def format_property_value(attr, type)
40 | return unless send(attr.to_sym).present?
41 |
42 | case type.to_sym
43 | when :integer
44 | send("#{attr.to_sym}=", send(attr.to_sym).to_i)
45 | when :float
46 | send("#{attr.to_sym}=", send(attr.to_sym).to_f)
47 | when :boolean
48 | send("#{attr.to_sym}=", ActiveModel::Type::Boolean.new.cast(send(attr.to_sym)))
49 | else
50 | raise ArgumentError, 'Supported types are :boolean, :integer, :float'
51 | end
52 | end
53 | end
54 | end
55 |
--------------------------------------------------------------------------------
/lib/active_model/datastore/track_changes.rb:
--------------------------------------------------------------------------------
1 | module ActiveModel::Datastore
2 | module TrackChanges
3 | extend ActiveSupport::Concern
4 |
5 | included do
6 | attr_accessor :exclude_from_save
7 | end
8 |
9 | def tracked_attributes
10 | []
11 | end
12 |
13 | ##
14 | # Resets the ActiveModel::Dirty tracked changes.
15 | #
16 | def reload!
17 | clear_changes_information
18 | self.exclude_from_save = false
19 | end
20 |
21 | def exclude_from_save?
22 | @exclude_from_save.nil? ? false : @exclude_from_save
23 | end
24 |
25 | ##
26 | # Determines if any attribute values have changed using ActiveModel::Dirty.
27 | # For attributes enabled for change tracking compares changed values. All values
28 | # submitted from an HTML form are strings, thus a string of 25.0 doesn't match an
29 | # original float of 25.0. Call this method after valid? to allow for any type coercing
30 | # occurring before saving to datastore.
31 | #
32 | # Consider the scenario in which the user submits an unchanged form value named `area`.
33 | # The initial value from datastore is a float of 25.0, which during assign_attributes
34 | # is set to a string of '25.0'. It is then coerced back to a float of 25.0 during a
35 | # validation callback. The area_changed? will return true, yet the value is back where
36 | # is started.
37 | #
38 | # For example:
39 | #
40 | # class Shapes
41 | # include ActiveModel::Datastore
42 | #
43 | # attr_accessor :area
44 | # enable_change_tracking :area
45 | # after_validation :format_values
46 | #
47 | # def format_values
48 | # format_property_value :area, :float
49 | # end
50 | #
51 | # def update(params)
52 | # assign_attributes(params)
53 | # if valid?
54 | # puts values_changed?
55 | # puts area_changed?
56 | # p area_change
57 | # end
58 | # end
59 | # end
60 | #
61 | # Will result in this:
62 | # values_changed? false
63 | # area_changed? true # This is correct, as area was changed but the value is identical.
64 | # area_change [0, 0]
65 | #
66 | # If none of the tracked attributes have changed, the `exclude_from_save` attribute is
67 | # set to true and the method returns false.
68 | #
69 | def values_changed?
70 | unless tracked_attributes.present?
71 | raise TrackChangesError, 'Object has not been configured for change tracking.'
72 | end
73 |
74 | changed = marked_for_destruction? ? true : false
75 | tracked_attributes.each do |attr|
76 | break if changed
77 |
78 | changed = send(attr) != send("#{attr}_was") if send("#{attr}_changed?")
79 | end
80 | self.exclude_from_save = !changed
81 | changed
82 | end
83 |
84 | def remove_unmodified_children
85 | return unless tracked_attributes.present? && nested_attributes?
86 |
87 | nested_attributes.each do |attr|
88 | with_changes = Array(send(attr.to_sym)).select(&:values_changed?)
89 | send("#{attr}=", with_changes)
90 | end
91 | nested_attributes.delete_if { |attr| Array(send(attr.to_sym)).empty? }
92 | end
93 |
94 | module ClassMethods
95 | ##
96 | # Enables track changes functionality for the provided attributes using ActiveModel::Dirty.
97 | #
98 | # Calls define_attribute_methods for each attribute provided.
99 | #
100 | # Creates a setter for each attribute that will look something like this:
101 | # def name=(value)
102 | # name_will_change! unless value == @name
103 | # @name = value
104 | # end
105 | #
106 | # Overrides tracked_attributes to return an Array of the attributes configured for tracking.
107 | #
108 | def enable_change_tracking(*attributes)
109 | attributes = attributes.collect(&:to_sym)
110 | attributes.each do |attr|
111 | define_attribute_methods attr
112 |
113 | define_method("#{attr}=") do |value|
114 | send("#{attr}_will_change!") unless value == instance_variable_get("@#{attr}")
115 | instance_variable_set("@#{attr}", value)
116 | end
117 | end
118 |
119 | define_method('tracked_attributes') { attributes }
120 | end
121 | end
122 | end
123 | end
124 |
--------------------------------------------------------------------------------
/lib/active_model/datastore/version.rb:
--------------------------------------------------------------------------------
1 | module ActiveModel
2 | module Datastore
3 | VERSION = '0.8.0'
4 | end
5 | end
6 |
--------------------------------------------------------------------------------
/lib/activemodel/datastore.rb:
--------------------------------------------------------------------------------
1 | # Need this file for Bundler auto-require based on gem name.
2 |
3 | require 'google/cloud/datastore'
4 | require 'active_support'
5 | require 'active_support/concern'
6 | require 'active_model'
7 |
8 | require 'active_model/datastore/connection'
9 | require 'active_model/datastore/errors'
10 | require 'active_model/datastore/excluded_indexes'
11 | require 'active_model/datastore/nested_attr'
12 | require 'active_model/datastore/property_values'
13 | require 'active_model/datastore/track_changes'
14 | require 'active_model/datastore'
15 |
--------------------------------------------------------------------------------
/test/cases/active_model_compliance_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ActiveModelComplianceTest < ActiveSupport::TestCase
4 | include ActiveModel::Lint::Tests
5 |
6 | def setup
7 | @model = MockModel.new
8 | end
9 |
10 | def teardown
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/cases/callbacks_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CallbacksTest < ActiveSupport::TestCase
4 | def setup
5 | super
6 | @mock_model = MockModel.new
7 | @mock_model.name = 'Initial Name'
8 | end
9 |
10 | test 'before validation callback on save' do
11 | class << @mock_model
12 | before_validation { self.name = nil }
13 | end
14 | refute @mock_model.save
15 | assert_nil @mock_model.name
16 | assert_equal 0, MockModel.count_test_entities
17 | end
18 |
19 | test 'after validation callback on save' do
20 | class << @mock_model
21 | after_validation { self.name = nil }
22 | end
23 | assert @mock_model.save
24 | assert_nil @mock_model.name
25 | assert_equal 1, MockModel.count_test_entities
26 | end
27 |
28 | test 'before save callback' do
29 | class << @mock_model
30 | before_save { self.name = 'Name changed before save' }
31 | end
32 | assert @mock_model.save
33 | assert_equal 'Name changed before save', @mock_model.name
34 | assert_equal 'Name changed before save', MockModel.all.first.name
35 | end
36 |
37 | test 'after save callback' do
38 | class << @mock_model
39 | after_save { self.name = 'Name changed after save' }
40 | end
41 | assert @mock_model.save
42 | assert_equal 'Name changed after save', @mock_model.name
43 | assert_equal 'Initial Name', MockModel.all.first.name
44 | end
45 |
46 | test 'before validation callback on update' do
47 | class << @mock_model
48 | before_validation { self.name = nil }
49 | end
50 | refute @mock_model.update(name: 'Different Name')
51 | assert_nil @mock_model.name
52 | end
53 |
54 | test 'after validation callback on update' do
55 | class << @mock_model
56 | after_validation { self.name = nil }
57 | end
58 | assert @mock_model.update(name: 'Different Name')
59 | assert_nil @mock_model.name
60 | end
61 |
62 | test 'before update callback' do
63 | class << @mock_model
64 | before_update { self.name = 'Name changed before update' }
65 | end
66 | assert @mock_model.update(name: 'This name should get changed')
67 | assert_equal 'Name changed before update', @mock_model.name
68 | assert_equal 'Name changed before update', MockModel.all.first.name
69 | end
70 |
71 | test 'after update callback' do
72 | class << @mock_model
73 | after_update { self.name = 'Name changed after update' }
74 | end
75 | assert @mock_model.update(name: 'This name should make it into datastore')
76 | assert_equal 'Name changed after update', @mock_model.name
77 | assert_equal 'This name should make it into datastore', MockModel.all.first.name
78 | end
79 | end
80 |
--------------------------------------------------------------------------------
/test/cases/carrier_wave_uploader_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class CarrierWaveUploaderTest < ActiveSupport::TestCase
4 | def setup
5 | super
6 | MockModel.send(:extend, CarrierWaveUploader)
7 | MockModel.send(:attr_accessor, :image)
8 | MockModel.send(:attr_accessor, :images)
9 | @mock_model = MockModel.new(name: 'A Mock Model')
10 | @uploader = Class.new(CarrierWave::Uploader::Base)
11 | image_path = File.join(Dir.pwd, 'test', 'images')
12 | @image_1 = File.join(image_path, 'test-image-1.jpg')
13 | @image_2 = File.join(image_path, 'test-image-2.jpeg')
14 | @image_3 = File.join(image_path, 'test-image-3.png')
15 | end
16 |
17 | test 'should return blank uploader when nothing' do
18 | MockModel.mount_uploader(:image, @uploader)
19 | assert @mock_model.image.blank?
20 | end
21 |
22 | test 'should return blank uploader when empty string' do
23 | MockModel.mount_uploader(:image, @uploader)
24 | @mock_model.image = ''
25 | @mock_model.save
26 | mock_model = MockModel.all.first
27 | assert_instance_of @uploader, mock_model.image
28 | assert mock_model.image.blank?
29 | end
30 |
31 | test 'should retrieve file from storage' do
32 | MockModel.mount_uploader(:image, @uploader)
33 | create_with_image(@image_1)
34 | mock_model = MockModel.all.first
35 | validate_image(mock_model, 'test-image-1.jpg')
36 | end
37 |
38 | test 'should copy a file into the cache directory' do
39 | MockModel.mount_uploader(:image, @uploader)
40 | @mock_model.image = Rack::Test::UploadedFile.new(@image_1, 'image/png')
41 | assert_match '/tmp/carrierwave-tests/carrierwave-cache/', @mock_model.image.current_path
42 | end
43 |
44 | test 'should set the file url on the entity' do
45 | MockModel.mount_uploader(:image, @uploader)
46 | create_with_image(@image_1)
47 | query = CloudDatastore.dataset.query 'MockModel'
48 | entity = CloudDatastore.dataset.run(query).first
49 | assert_equal 'test-image-1.jpg', entity['image']
50 | end
51 |
52 | test 'should retrieve files from storage' do
53 | MockModel.mount_uploaders(:images, @uploader)
54 | create_with_images(@image_1, @image_2)
55 | mock_model = MockModel.all.first
56 | validate_images(mock_model, 'test-image-1.jpg', 'test-image-2.jpeg')
57 | end
58 |
59 | test 'should set an array of file identifiers' do
60 | MockModel.mount_uploaders(:images, @uploader)
61 | create_with_images(@image_1, @image_2)
62 | query = CloudDatastore.dataset.query 'MockModel'
63 | entity = CloudDatastore.dataset.run(query).first
64 | assert entity['images'].is_a? Array
65 | assert_includes entity['images'], 'test-image-1.jpg'
66 | assert_includes entity['images'], 'test-image-2.jpeg'
67 | end
68 |
69 | test 'should retrieve files with multiple uploaders' do
70 | MockModel.mount_uploader(:image, @uploader)
71 | MockModel.mount_uploaders(:images, @uploader)
72 | @mock_model.image = Rack::Test::UploadedFile.new(@image_1, 'image/png')
73 | create_with_images(@image_2, @image_3)
74 | mock_model = MockModel.all.first
75 | validate_image(mock_model, 'test-image-1.jpg')
76 | validate_images(mock_model, 'test-image-2.jpeg', 'test-image-3.png')
77 | end
78 |
79 | test 'should update file' do
80 | MockModel.mount_uploader(:image, @uploader)
81 | create_with_image(@image_1)
82 | @mock_model.update(image: Rack::Test::UploadedFile.new(@image_2, 'image/png'))
83 | mock_model = MockModel.all.first
84 | validate_image(mock_model, 'test-image-2.jpeg')
85 | end
86 |
87 | test 'should update files' do
88 | MockModel.mount_uploaders(:images, @uploader)
89 | create_with_images(@image_2, @image_3)
90 | images = [Rack::Test::UploadedFile.new(@image_1, 'image/png')]
91 | @mock_model.update(images: images)
92 | mock_model = MockModel.all.first
93 | validate_images(mock_model, 'test-image-1.jpg', 'test-image-2.jpeg', 'test-image-3.png')
94 | end
95 |
96 | test 'should update file with multiple uploaders' do
97 | MockModel.mount_uploader(:image, @uploader)
98 | MockModel.mount_uploaders(:images, @uploader)
99 | create_with_image(@image_1)
100 | @mock_model.update(image: Rack::Test::UploadedFile.new(@image_2, 'image/png'))
101 | mock_model = MockModel.all.first
102 | validate_image(mock_model, 'test-image-2.jpeg')
103 | end
104 |
105 | test 'should update files with multiple uploaders' do
106 | MockModel.mount_uploader(:image, @uploader)
107 | MockModel.mount_uploaders(:images, @uploader)
108 | create_with_images(@image_3)
109 | @mock_model.update(images: [Rack::Test::UploadedFile.new(@image_1, 'image/png')])
110 | mock_model = MockModel.all.first
111 | validate_images(mock_model, 'test-image-1.jpg', 'test-image-3.png')
112 | end
113 |
114 | test 'should retain file when not changed' do
115 | MockModel.mount_uploader(:image, @uploader)
116 | create_with_image(@image_2)
117 | @mock_model.update(name: 'No image changes')
118 | mock_model = MockModel.all.first
119 | validate_image(mock_model, 'test-image-2.jpeg')
120 | end
121 |
122 | test 'should retain files when not changed' do
123 | MockModel.mount_uploaders(:images, @uploader)
124 | create_with_images(@image_1, @image_2, @image_3)
125 | @mock_model.update(name: 'No image changes')
126 | mock_model = MockModel.all.first
127 | validate_images(mock_model, 'test-image-1.jpg', 'test-image-2.jpeg', 'test-image-3.png')
128 | end
129 |
130 | test 'deleting entity should delete file' do
131 | MockModel.mount_uploader(:image, @uploader)
132 | create_with_image(@image_1)
133 | @mock_model.destroy
134 | assert_equal 0, Dir[File.join(Dir.pwd, 'tmp', 'carrierwave-tests', 'uploads', '*')].size
135 | end
136 |
137 | private
138 |
139 | def create_with_image(file)
140 | @mock_model.image = Rack::Test::UploadedFile.new(file, 'image/png')
141 | @mock_model.save
142 | @mock_model = MockModel.find(@mock_model.id)
143 | end
144 |
145 | def create_with_images(*files)
146 | images = files.map { |file| Rack::Test::UploadedFile.new(file, 'image/png') }
147 | @mock_model.images = images
148 | @mock_model.save
149 | @mock_model = MockModel.find(@mock_model.id)
150 | end
151 |
152 | def validate_image(mock_model, image_name)
153 | assert_instance_of @uploader, mock_model.image
154 | assert_equal "/uploads/#{image_name}", mock_model.image.url
155 | end
156 |
157 | def validate_images(mock_model, *image_names)
158 | assert mock_model.images.is_a? Array
159 | assert_equal image_names.size, mock_model.images.size
160 | urls = mock_model.images.map(&:url)
161 | image_names.each { |name| assert_includes urls, "/uploads/#{name}" }
162 | end
163 | end
164 |
--------------------------------------------------------------------------------
/test/cases/datastore_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ActiveModel::DatastoreTest < ActiveSupport::TestCase
4 | # Packaging tests.
5 |
6 | test 'test that it has a version number' do
7 | refute_nil ::ActiveModel::Datastore::VERSION
8 | end
9 |
10 | # Instance method tests.
11 |
12 | test 'entity properties' do
13 | class MockModelNoAttr
14 | include ActiveModel::Datastore
15 | end
16 | mock_model = MockModelNoAttr.new
17 | assert_equal [], mock_model.entity_properties
18 | end
19 |
20 | test 'parent?' do
21 | mock_model = MockModel.new
22 | refute mock_model.parent?
23 | mock_model.parent_key_id = 12345
24 | assert mock_model.parent?
25 | end
26 |
27 | test 'persisted?' do
28 | mock_model = MockModel.new
29 | refute mock_model.persisted?
30 | mock_model.id = 1
31 | assert mock_model.persisted?
32 | end
33 |
34 | test 'build entity' do
35 | mock_model = MockModel.new(name: 'Entity Test')
36 | entity = mock_model.build_entity
37 | assert_equal 'Entity Test', entity.properties['name']
38 | assert_equal 'MockModel', entity.key.kind
39 | assert_nil entity.key.id
40 | assert_nil entity.key.name
41 | assert_nil entity.key.parent
42 | end
43 |
44 | test 'build existing entity' do
45 | mock_model = MockModel.new(name: 'Entity Test')
46 | mock_model.id = 12345
47 | entity = mock_model.build_entity
48 | assert_equal 'Entity Test', entity.properties['name']
49 | assert_equal 'MockModel', entity.key.kind
50 | assert_equal 12345, entity.key.id
51 | assert_nil entity.key.name
52 | assert_nil entity.key.parent
53 | end
54 |
55 | test 'build entity with parent' do
56 | mock_model = MockModel.new(name: 'Entity Test')
57 | parent_key = CloudDatastore.dataset.key('Parent', 212121)
58 | entity = mock_model.build_entity(parent_key)
59 | assert_equal 'Entity Test', entity.properties['name']
60 | assert_equal 'MockModel', entity.key.kind
61 | assert_nil entity.key.id
62 | assert_equal 'Parent', entity.key.parent.kind
63 | assert_equal 212121, entity.key.parent.id
64 | end
65 |
66 | test 'build entity with parent key id' do
67 | mock_model = MockModel.new(name: 'Entity Test', parent_key_id: MOCK_PARENT_ID)
68 | entity = mock_model.build_entity
69 | assert_equal 'Entity Test', entity.properties['name']
70 | assert_equal 'MockModel', entity.key.kind
71 | assert_nil entity.key.id
72 | assert_nil entity.key.name
73 | assert_nil entity.key.id
74 | assert_equal 'ParentMockModel', entity.key.parent.kind
75 | assert_equal MOCK_PARENT_ID, entity.key.parent.id
76 | end
77 |
78 | test 'build entity with index exclusion' do
79 | MockModel.no_indexes :name
80 | name = Faker::Lorem.characters(number: 1600)
81 | mock_model = MockModel.new(name: name)
82 | mock_model.save
83 | entity = mock_model.build_entity
84 | assert_equal name, entity.properties['name']
85 | assert entity.exclude_from_indexes? 'name'
86 | assert entity.exclude_from_indexes? :name
87 | refute entity.exclude_from_indexes? :role
88 | end
89 |
90 | test 'save' do
91 | count = MockModel.count_test_entities
92 | mock_model = MockModel.new
93 | refute mock_model.save
94 | assert_equal count, MockModel.count_test_entities
95 | mock_model = MockModel.new(name: 'Save Test')
96 | assert mock_model.save
97 | assert_equal count + 1, MockModel.count_test_entities
98 | assert_not_nil mock_model.id
99 | assert_nil mock_model.parent_key_id
100 | end
101 |
102 | test 'save with parent' do
103 | count = MockModel.count_test_entities
104 | parent_key = CloudDatastore.dataset.key('Company', MOCK_PARENT_ID)
105 | mock_model = MockModel.new(name: 'Save Test')
106 | assert mock_model.save(parent_key)
107 | assert_equal count + 1, MockModel.count_test_entities
108 | assert_not_nil mock_model.id
109 | assert_equal MOCK_PARENT_ID, mock_model.parent_key_id
110 | key = CloudDatastore.dataset.key 'MockModel', mock_model.id
111 | key.parent = parent_key
112 | entity = CloudDatastore.dataset.find key
113 | assert_equal mock_model.id, entity.key.id
114 | assert_equal 'MockModel', entity.key.kind
115 | assert_equal 'Company', entity.key.parent.kind
116 | assert_equal MOCK_PARENT_ID, entity.key.parent.id
117 | end
118 |
119 | test 'save within default entity group' do
120 | count = MockModel.count_test_entities
121 | mock_model = MockModel.new(name: 'Ancestor Test', parent_key_id: MOCK_PARENT_ID)
122 | assert mock_model.save
123 | assert_equal count + 1, MockModel.count_test_entities
124 | assert_not_nil mock_model.id
125 | key = CloudDatastore.dataset.key 'MockModel', mock_model.id
126 | key.parent = CloudDatastore.dataset.key('ParentMockModel', MOCK_PARENT_ID)
127 | entity = CloudDatastore.dataset.find key
128 | assert_equal mock_model.id, entity.key.id
129 | assert_equal 'MockModel', entity.key.kind
130 | assert_equal 'ParentMockModel', entity.key.parent.kind
131 | assert_equal MOCK_PARENT_ID, entity.key.parent.id
132 | end
133 |
134 | test 'update' do
135 | mock_model = create(:mock_model)
136 | id = mock_model.id
137 | count = MockModel.count_test_entities
138 | mock_model.update(name: 'different name')
139 | assert_equal id, mock_model.id
140 | assert_equal count, MockModel.count_test_entities
141 | key = CloudDatastore.dataset.key 'MockModel', mock_model.id
142 | entity = CloudDatastore.dataset.find key
143 | assert_equal id, entity.key.id
144 | assert_equal 'MockModel', entity.key.kind
145 | assert_nil entity.key.parent
146 | assert_equal 'different name', entity['name']
147 | end
148 |
149 | test 'update within entity group' do
150 | mock_model = create(:mock_model, parent_key_id: MOCK_PARENT_ID)
151 | id = mock_model.id
152 | count = MockModel.count_test_entities
153 | mock_model.update(name: 'different name')
154 | assert_equal id, mock_model.id
155 | assert_equal count, MockModel.count_test_entities
156 | key = CloudDatastore.dataset.key 'MockModel', mock_model.id
157 | key.parent = CloudDatastore.dataset.key('ParentMockModel', MOCK_PARENT_ID)
158 | entity = CloudDatastore.dataset.find key
159 | assert_equal id, entity.key.id
160 | assert_equal 'MockModel', entity.key.kind
161 | assert_equal 'ParentMockModel', entity.key.parent.kind
162 | assert_equal 'different name', entity['name']
163 | end
164 |
165 | test 'destroy' do
166 | mock_model = create(:mock_model)
167 | count = MockModel.count_test_entities
168 | mock_model.destroy
169 | assert_equal count - 1, MockModel.count_test_entities
170 | end
171 |
172 | test 'destroy within entity group' do
173 | mock_model = create(:mock_model, parent_key_id: MOCK_PARENT_ID)
174 | count = MockModel.count_test_entities
175 | mock_model.destroy
176 | assert_equal count - 1, MockModel.count_test_entities
177 | end
178 |
179 | # Class method tests.
180 | test 'parent key' do
181 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
182 | assert parent_key.is_a? Google::Cloud::Datastore::Key
183 | assert_equal 'ParentMockModel', parent_key.kind
184 | assert_equal MOCK_PARENT_ID, parent_key.id
185 | end
186 |
187 | test 'all' do
188 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
189 | 15.times do
190 | create(:mock_model, name: Faker::Name.name)
191 | end
192 | 15.times do
193 | attr = attributes_for(:mock_model, name: Faker::Name.name, parent_key_id: MOCK_PARENT_ID)
194 | mock_model = MockModel.new(attr)
195 | mock_model.save
196 | end
197 | objects = MockModel.all
198 | assert_equal 30, objects.size
199 | objects = MockModel.all(ancestor: parent_key)
200 | assert_equal 15, objects.size
201 | name = objects[5].name
202 | objects = MockModel.all(ancestor: parent_key, where: ['name', '=', name])
203 | assert_equal 1, objects.size
204 | assert_equal name, objects.first.name
205 | assert objects.first.is_a?(MockModel)
206 | end
207 |
208 | test 'find in batches' do
209 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
210 | 10.times do
211 | attr = attributes_for(:mock_model, name: Faker::Name.name, parent_key_id: MOCK_PARENT_ID)
212 | mock_model = MockModel.new(attr)
213 | mock_model.save
214 | end
215 | attr = attributes_for(:mock_model, name: 'MockModel', parent_key_id: MOCK_PARENT_ID)
216 | mock_model = MockModel.new(attr)
217 | mock_model.save
218 | create(:mock_model, name: 'MockModel No Ancestor')
219 | objects = MockModel.all
220 | assert_equal MockModel, objects.first.class
221 | assert_equal 12, objects.count
222 | objects = MockModel.all(ancestor: parent_key)
223 | assert_equal 11, objects.count
224 | objects, start_cursor = MockModel.all(ancestor: parent_key, limit: 7)
225 | assert_equal 7, objects.count
226 | refute_nil start_cursor # requested 7 results and there are 4 more
227 | objects = MockModel.all(ancestor: parent_key, cursor: start_cursor)
228 | assert_equal 4, objects.count
229 | objects, cursor = MockModel.all(ancestor: parent_key, cursor: start_cursor, limit: 5)
230 | assert_equal 4, objects.count
231 | assert_nil cursor # query started where we left off, requested 5 results and there were 4 more
232 | objects, cursor = MockModel.all(ancestor: parent_key, cursor: start_cursor, limit: 4)
233 | assert_equal 4, objects.count
234 | refute_nil cursor # query started where we left off, requested 4 results and there were 4 more
235 | objects = MockModel.all(ancestor: parent_key, where: ['name', '=', mock_model.name])
236 | assert_equal 1, objects.count
237 | objects, _cursor = MockModel.all(ancestor: parent_key, select: 'name', limit: 1)
238 | assert_equal 1, objects.count
239 | refute_nil objects.first.name
240 | end
241 |
242 | test 'find entity' do
243 | mock_model_1 = create(:mock_model, name: 'Entity 1')
244 | entity = MockModel.find_entity(mock_model_1.id)
245 | assert entity.is_a?(Google::Cloud::Datastore::Entity)
246 | assert_equal 'Entity 1', entity.properties['name']
247 | assert_equal 'Entity 1', entity['name']
248 | assert_equal 'Entity 1', entity[:name]
249 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
250 | attr = attributes_for(:mock_model, name: 'Entity 2', parent_key_id: MOCK_PARENT_ID)
251 | mock_model_2 = MockModel.new(attr)
252 | mock_model_2.save
253 | entity = MockModel.find_entity(mock_model_2.id)
254 | assert_nil entity
255 | entity = MockModel.find_entity(mock_model_2.id, parent_key)
256 | assert entity.is_a?(Google::Cloud::Datastore::Entity)
257 | assert_equal 'Entity 2', entity.properties['name']
258 | assert_nil MockModel.find_entity(mock_model_2.id + 1)
259 | end
260 |
261 | test 'find entities' do
262 | mock_model_1 = create(:mock_model, name: 'Entity 1')
263 | mock_model_2 = create(:mock_model, name: 'Entity 2')
264 | entities = MockModel.find_entities(mock_model_1.id, mock_model_2.id)
265 | assert_equal 2, entities.size
266 | entities.each { |entity| assert entity.is_a?(Google::Cloud::Datastore::Entity) }
267 | assert_equal 'Entity 1', entities[0][:name]
268 | assert_equal 'Entity 2', entities[1][:name]
269 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
270 | attr = attributes_for(:mock_model, name: 'Entity 3', parent_key_id: MOCK_PARENT_ID)
271 | mock_model_3 = MockModel.new(attr)
272 | mock_model_3.save
273 | entities = MockModel.find_entities([mock_model_1.id, mock_model_2.id, mock_model_3.id])
274 | assert_equal 2, entities.size
275 | entities = MockModel.find_entities(mock_model_2.id, mock_model_3.id, parent: parent_key)
276 | assert_equal 1, entities.size
277 | assert_equal 'Entity 3', entities[0][:name]
278 | assert_empty MockModel.find_entities(mock_model_3.id + 1)
279 | end
280 |
281 | test 'find entities should exclude duplicates' do
282 | mock_model_1 = create(:mock_model, name: 'Entity 1')
283 | entities = MockModel.find_entities(mock_model_1.id, mock_model_1.id, mock_model_1.id)
284 | assert_equal 1, entities.size
285 | end
286 |
287 | test 'find entities should exclude nil ids' do
288 | mock_model_1 = create(:mock_model, name: 'Entity 1')
289 | entities = MockModel.find_entities(mock_model_1.id, nil)
290 | assert_equal 1, entities.size
291 | end
292 |
293 | test 'find' do
294 | mock_model = create(:mock_model, name: 'Entity')
295 | model_entity = MockModel.find(mock_model.id)
296 | assert model_entity.is_a?(MockModel)
297 | assert_equal 'Entity', model_entity.name
298 | end
299 |
300 | test 'find by parent' do
301 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
302 | attr = attributes_for(:mock_model, name: 'Entity With Parent', parent_key_id: MOCK_PARENT_ID)
303 | mock_model = MockModel.new(attr)
304 | mock_model.save
305 | model_entity = MockModel.find(mock_model.id, parent: parent_key)
306 | assert model_entity.is_a?(MockModel)
307 | assert_equal 'Entity With Parent', model_entity.name
308 | end
309 |
310 | test 'find all by parent' do
311 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
312 | attr = attributes_for(:mock_model, name: 'Entity 1 With Parent', parent_key_id: MOCK_PARENT_ID)
313 | mock_model_1 = MockModel.new(attr)
314 | mock_model_1.save
315 | attr = attributes_for(:mock_model, name: 'Entity 2 With Parent', parent_key_id: MOCK_PARENT_ID)
316 | mock_model_2 = MockModel.new(attr)
317 | mock_model_2.save
318 | model_entities = MockModel.find(mock_model_1.id, mock_model_2.id, parent: parent_key)
319 | model_entities.each { |model| assert model.is_a?(MockModel) }
320 | assert_equal 'Entity 1 With Parent', model_entities[0].name
321 | assert_equal 'Entity 2 With Parent', model_entities[1].name
322 | end
323 |
324 | test 'find without result' do
325 | assert_nil MockModel.find(99999)
326 | assert_empty MockModel.find([99999])
327 | end
328 |
329 | test 'find without results' do
330 | assert_empty MockModel.find(99999, 88888)
331 | assert_empty MockModel.find([99999, 88888])
332 | end
333 |
334 | test 'find by' do
335 | model_entity = MockModel.find_by(name: 'Billy Bob')
336 | assert_nil model_entity
337 | create(:mock_model, name: 'Billy Bob')
338 | model_entity = MockModel.find_by(name: 'Billy Bob')
339 | assert model_entity.is_a?(MockModel)
340 | assert_equal 'Billy Bob', model_entity.name
341 | parent_key = MockModel.parent_key(MOCK_PARENT_ID)
342 | attr = attributes_for(:mock_model, name: 'Entity With Parent', parent_key_id: MOCK_PARENT_ID)
343 | mock_model_2 = MockModel.new(attr)
344 | mock_model_2.save
345 | model_entity = MockModel.find_by(name: 'Billy Bob')
346 | assert_equal 'Billy Bob', model_entity.name
347 | model_entity = MockModel.find_by(name: 'Billy Bob', ancestor: parent_key)
348 | assert_nil model_entity
349 | model_entity = MockModel.find_by(name: 'Entity With Parent', ancestor: parent_key)
350 | assert_equal 'Entity With Parent', model_entity.name
351 | end
352 |
353 | test 'from_entity' do
354 | entity = CloudDatastore.dataset.entity
355 | key = CloudDatastore.dataset.key('MockEntity', '12345')
356 | key.parent = CloudDatastore.dataset.key('Parent', 11111)
357 | entity.key = key
358 | entity['name'] = 'A Mock Entity'
359 | entity['role'] = 1
360 | assert_nil MockModel.from_entity(nil)
361 | model_entity = MockModel.from_entity(entity)
362 | assert model_entity.is_a?(MockModel)
363 | refute model_entity.role_changed?
364 | assert model_entity.entity_property_values.is_a? Hash
365 | assert_equal model_entity.entity_property_values['name'], 'A Mock Entity'
366 | end
367 |
368 | test 'build query' do
369 | query = MockModel.build_query(kind: 'MockModel')
370 | assert query.instance_of?(Google::Cloud::Datastore::Query)
371 | grpc = query.to_grpc
372 | assert_equal 'MockModel', grpc.kind[0].name
373 | assert_nil grpc.filter
374 | assert_nil grpc.limit
375 | assert_equal '', grpc.start_cursor
376 | assert_empty grpc.projection
377 | grpc = MockModel.build_query(where: ['name', '=', 'something']).to_grpc
378 | refute_nil grpc.filter
379 | grpc = MockModel.build_query(limit: 5).to_grpc
380 | refute_nil grpc.limit
381 | assert_equal 5, grpc.limit.value
382 | grpc = MockModel.build_query(select: 'name').to_grpc
383 | refute_nil grpc.projection
384 | assert_equal 1, grpc.projection.count
385 | grpc = MockModel.build_query(select: %w[name role]).to_grpc
386 | refute_nil grpc.projection
387 | assert_equal 2, grpc.projection.count
388 | grpc = MockModel.build_query(distinct_on: 'name').to_grpc
389 | refute_nil grpc.distinct_on
390 | assert_equal 1, grpc.distinct_on.count
391 | assert_equal 'name', grpc.distinct_on.first.name
392 | grpc = MockModel.build_query(distinct_on: %w[name role]).to_grpc
393 | refute_nil grpc.distinct_on
394 | assert_equal 2, grpc.distinct_on.count
395 | assert_equal 'role', grpc.distinct_on.last.name
396 | grpc = MockModel.build_query(cursor: 'a_cursor').to_grpc
397 | refute_nil grpc.start_cursor
398 | parent_int_key = CloudDatastore.dataset.key('Parent', MOCK_PARENT_ID)
399 | grpc = MockModel.build_query(ancestor: parent_int_key).to_grpc
400 | ancestor_filter = grpc.filter.composite_filter.filters.first
401 | assert_equal '__key__', ancestor_filter.property_filter.property.name
402 | assert_equal :HAS_ANCESTOR, ancestor_filter.property_filter.op
403 | key = ancestor_filter.property_filter.value.key_value.path[0]
404 | assert_equal parent_int_key.kind, key.kind
405 | assert_equal parent_int_key.id, key.id
406 | assert_equal key.id_type, :id
407 | parent_string_key = CloudDatastore.dataset.key('Parent', 'ABCDEF')
408 | grpc = MockModel.build_query(ancestor: parent_string_key).to_grpc
409 | ancestor_filter = grpc.filter.composite_filter.filters.first
410 | key = ancestor_filter.property_filter.value.key_value.path[0]
411 | assert_equal parent_string_key.kind, key.kind
412 | assert_equal key.id_type, :name
413 | assert_equal parent_string_key.name, key.name
414 | end
415 | end
416 |
--------------------------------------------------------------------------------
/test/cases/excluded_indexes_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class ExcludedIndexesTest < ActiveSupport::TestCase
4 | test 'responds to no index attributes' do
5 | mock_model = MockModel.new
6 | assert mock_model.respond_to? :no_index_attributes
7 | assert_empty mock_model.no_index_attributes
8 | end
9 |
10 | test 'excludes index of single attribute' do
11 | MockModel.no_indexes :name
12 | mock_model = MockModel.new
13 | assert_includes mock_model.no_index_attributes, 'name'
14 | assert_equal 1, mock_model.no_index_attributes.size
15 | end
16 |
17 | test 'excludes index of multiple attributes' do
18 | MockModel.no_indexes :name, :role
19 | mock_model = MockModel.new
20 | assert_includes mock_model.no_index_attributes, 'name'
21 | assert_includes mock_model.no_index_attributes, 'role'
22 | assert_equal 2, mock_model.no_index_attributes.size
23 | end
24 | end
25 |
--------------------------------------------------------------------------------
/test/cases/nested_attr_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class NestedAttrTest < ActiveSupport::TestCase
4 | def setup
5 | super
6 | MockModelParent.clear_validators!
7 | MockModelParent.validates_associated(:mock_models)
8 | @mock_model_parent = MockModelParent.new(name: 'whatever')
9 | MockModel.clear_validators!
10 | MockModel.validates :name, presence: true
11 | end
12 |
13 | # Instance method tests.
14 | test 'nested_attributes?' do
15 | MockModelParent.validates_associated(:mock_models)
16 | @mock_model_parent.mock_models = [MockModel.new(name: 'M1'), MockModel.new(name: 'M2')]
17 | refute @mock_model_parent.nested_attributes?
18 | @mock_model_parent.nested_attributes = :mock_models
19 | refute @mock_model_parent.nested_attributes?
20 | @mock_model_parent.nested_attributes = [:mock_models]
21 | assert @mock_model_parent.nested_attributes?
22 | end
23 |
24 | test 'should extract and return nested models' do
25 | assert_empty @mock_model_parent.nested_models
26 | @mock_model_parent.mock_models = [m1 = MockModel.new(name: 'M'), m2 = MockModel.new(name: 'M2')]
27 | assert_empty @mock_model_parent.nested_models
28 | @mock_model_parent.nested_attributes = [:mock_models]
29 | nested_models = @mock_model_parent.nested_models
30 | assert_equal 2, nested_models.size
31 | assert_equal m1, nested_models[0]
32 | assert_equal m2, nested_models[1]
33 | end
34 |
35 | test 'should return a list of nested model class names' do
36 | @mock_model_parent.mock_models = [MockModel.new(name: 'M'), MockModel.new(name: 'M2')]
37 | @mock_model_parent.nested_attributes = [:mock_models]
38 | classes = @mock_model_parent.nested_model_class_names
39 | assert_equal 1, classes.size
40 | assert_equal ['MockModel'], classes
41 | end
42 |
43 | test 'should return a list of nested error objects' do
44 | @mock_model_parent.mock_models = [MockModel.new, MockModel.new, MockModel.new(name: 'M3')]
45 | @mock_model_parent.nested_attributes = [:mock_models]
46 | errors = @mock_model_parent.nested_errors
47 | assert errors.is_a? Array
48 | # Each model should have an ActiveModel::Errors object, regardless of validation status
49 | assert_equal 3, errors.size
50 | end
51 |
52 | test 'assigns new nested objects with hash attributes' do
53 | assert_nil @mock_model_parent.nested_attributes
54 | params = { '0' => { name: 'Mock Model 1', role: 0 }, '1' => { name: 'Mock Model 2', role: 1 } }
55 | @mock_model_parent.assign_nested_attributes(:mock_models, params)
56 | assert @mock_model_parent.mock_models.is_a? Array
57 | assert_equal 2, @mock_model_parent.mock_models.size
58 | mock_model_1 = @mock_model_parent.mock_models[0]
59 | mock_model_2 = @mock_model_parent.mock_models[1]
60 | assert mock_model_1.is_a? MockModel
61 | assert mock_model_2.is_a? MockModel
62 | assert_equal 'Mock Model 1', mock_model_1.name
63 | assert_equal 0, mock_model_1.role
64 | assert_equal 'Mock Model 2', mock_model_2.name
65 | assert_equal 1, mock_model_2.role
66 | assert_equal [:mock_models], @mock_model_parent.nested_attributes
67 | end
68 |
69 | test 'updates existing nested objects' do
70 | mock_model_1 = create(:mock_model, name: 'Model 1', role: 0)
71 | mock_model_2 = create(:mock_model, name: 'Model 2', role: 0)
72 | @mock_model_parent.mock_models = [mock_model_1, mock_model_2]
73 | params_1 = ActionController::Parameters.new(id: mock_model_1.id, name: 'Model 1A', role: 1)
74 | params_2 = ActionController::Parameters.new(id: mock_model_2.id, name: 'Model 2A', role: 1)
75 | form_params = ActionController::Parameters.new('0' => params_1, '1' => params_2)
76 | params = form_params.permit('0' => [:id, :name, :role], '1' => [:id, :name, :role])
77 | @mock_model_parent.assign_nested_attributes(:mock_models, params)
78 | assert @mock_model_parent.mock_models.is_a? Array
79 | assert_equal 2, @mock_model_parent.mock_models.size
80 | mock_model_1 = @mock_model_parent.mock_models[0]
81 | mock_model_2 = @mock_model_parent.mock_models[1]
82 | assert_equal 'Model 1A', mock_model_1.name
83 | assert_equal 1, mock_model_1.role
84 | assert_equal 'Model 2A', mock_model_2.name
85 | assert_equal 1, mock_model_2.role
86 | end
87 |
88 | test 'marks a deleted object for destruction' do
89 | mock_model_1 = create(:mock_model, name: 'Model 1', role: 0)
90 | mock_model_2 = create(:mock_model, name: 'Model 2', role: 0)
91 | @mock_model_parent.mock_models = [mock_model_1, mock_model_2]
92 | params_1 = ActionController::Parameters.new(id: mock_model_1.id, _destroy: '1')
93 | form_params = ActionController::Parameters.new('0' => params_1)
94 | params = form_params.permit('0' => [:id, :name, :role, :_destroy])
95 | @mock_model_parent.assign_nested_attributes(:mock_models, params)
96 | assert mock_model_1.marked_for_destruction
97 | refute mock_model_2.marked_for_destruction
98 | end
99 |
100 | test 'does not respond to underscore_destroy without id' do
101 | params = { '0' => { name: 'Mock Model 1', role: 0, _destroy: '1' } }
102 | @mock_model_parent.assign_nested_attributes(:mock_models, params)
103 | mock_model_1 = @mock_model_parent.mock_models[0]
104 | refute mock_model_1.marked_for_destruction
105 | end
106 |
107 | test 'rejects new objects if proc supplied' do
108 | @mock_model_parent.nested_attributes = nil
109 | params = { '0' => { name: 'Mock Model 1', role: 0 }, '1' => { name: '', role: 1 } }
110 | reject_proc = proc { |attributes| attributes['name'].blank? }
111 | @mock_model_parent.assign_nested_attributes(:mock_models, params, reject_if: reject_proc)
112 | assert_equal 1, @mock_model_parent.mock_models.size
113 | assert_equal 'Mock Model 1', @mock_model_parent.mock_models[0].name
114 | end
115 |
116 | test 'rejects new objects with all_blank symbol' do
117 | @mock_model_parent.nested_attributes = nil
118 | params = { '0' => { name: '', role: nil }, '1' => { name: '', role: nil } }
119 | @mock_model_parent.assign_nested_attributes(:mock_models, params, reject_if: :all_blank)
120 | assert_equal 0, @mock_model_parent.mock_models.size
121 | end
122 |
123 | # Class method tests.
124 |
125 | test 'validates associated' do
126 | @mock_model_parent.mock_models = [m = MockModel.new, m2 = MockModel.new(name: 'Model 2'),
127 | m3 = MockModel.new, m4 = MockModel.new(name: 'Model 4')]
128 | assert !@mock_model_parent.valid?
129 | assert @mock_model_parent.errors[:mock_models].any?
130 | assert_equal 1, m.errors.count
131 | assert_equal 0, m2.errors.count
132 | assert_equal 1, m3.errors.count
133 | assert_equal 0, m4.errors.count
134 | m.name = m3.name = 'non-empty'
135 | assert @mock_model_parent.valid?
136 | end
137 | end
138 |
139 | class MockModelParent
140 | include ActiveModel::Datastore::NestedAttr
141 | attr_accessor :name
142 | attr_accessor :mock_models
143 | end
144 |
--------------------------------------------------------------------------------
/test/cases/property_values_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class PropertyValuesTest < ActiveSupport::TestCase
4 | test 'default property value' do
5 | mock_model = MockModel.new
6 | mock_model.name = nil
7 | mock_model.default_property_value(:name, 'Default Name')
8 | assert_equal 'Default Name', mock_model.name
9 | mock_model.name = 'A New Name'
10 | mock_model.default_property_value(:name, 'Default Name')
11 | assert_equal 'A New Name', mock_model.name
12 | mock_model.name = ''
13 | mock_model.default_property_value(:name, 'Default Name')
14 | assert_equal 'Default Name', mock_model.name
15 | end
16 |
17 | test 'format integer property value' do
18 | mock_model = MockModel.new(name: '34')
19 | mock_model.format_property_value(:name, :integer)
20 | assert_equal 34, mock_model.name
21 | end
22 |
23 | test 'format float property value' do
24 | mock_model = MockModel.new(name: '34')
25 | mock_model.format_property_value(:name, :float)
26 | assert_equal 34.0, mock_model.name
27 | end
28 |
29 | test 'format boolean property value' do
30 | mock_model = MockModel.new(role: '0')
31 | mock_model.format_property_value(:role, :boolean)
32 | refute mock_model.role
33 | mock_model.role = 0
34 | mock_model.format_property_value(:role, :boolean)
35 | refute mock_model.role
36 | mock_model.role = '1'
37 | mock_model.format_property_value(:role, :boolean)
38 | assert mock_model.role
39 | mock_model.role = 1
40 | mock_model.format_property_value(:role, :boolean)
41 | assert mock_model.role
42 | mock_model.role = true
43 | mock_model.format_property_value(:role, :boolean)
44 | assert mock_model.role
45 | mock_model.role = false
46 | mock_model.format_property_value(:role, :boolean)
47 | refute mock_model.role
48 | end
49 | end
50 |
--------------------------------------------------------------------------------
/test/cases/track_changes_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class TrackChangesTest < ActiveSupport::TestCase
4 | def setup
5 | super
6 | create(:mock_model, name: 25.5, role: 1)
7 | end
8 |
9 | test 'track changes with single attribute' do
10 | mock_model = MockModel.all.first
11 | refute mock_model.exclude_from_save?
12 | refute mock_model.values_changed?
13 | assert mock_model.exclude_from_save?
14 |
15 | mock_model.name = '25.5'
16 | assert mock_model.changed?
17 | mock_model.name = 25.5
18 | assert mock_model.changed?
19 | refute mock_model.values_changed?
20 | assert mock_model.exclude_from_save?
21 |
22 | mock_model.name = 25.4
23 | assert mock_model.values_changed?
24 | refute mock_model.exclude_from_save?
25 | end
26 |
27 | test 'track changes with multiple attributes' do
28 | mock_model = MockModel.all.first
29 | refute mock_model.exclude_from_save?
30 | refute mock_model.changed?
31 | mock_model.name = 20
32 | mock_model.role = '1'
33 | mock_model.role = 1
34 | assert mock_model.values_changed?
35 | refute mock_model.exclude_from_save?
36 | end
37 |
38 | test 'track changes with marked for destruction' do
39 | mock_model = MockModel.all.first
40 | mock_model.marked_for_destruction = true
41 | assert mock_model.values_changed?
42 | refute mock_model.exclude_from_save?
43 | mock_model.name = '75'
44 | mock_model.name = 75
45 | assert mock_model.values_changed?
46 | refute mock_model.exclude_from_save?
47 | end
48 |
49 | test 'remove unmodified children' do
50 | class MockModelParentWithTracking
51 | include ActiveModel::Datastore
52 | attr_accessor :name
53 | attr_accessor :mock_models
54 | enable_change_tracking :name
55 | end
56 | mock_model_parent = MockModelParentWithTracking.new(name: 'whatever')
57 | mock_model_parent.mock_models = [MockModel.new(name: 'M1'), MockModel.new(name: 'M2')]
58 | mock_model_parent.nested_attributes = [:mock_models]
59 | mock_model_parent.reload!
60 | mock_model_parent.mock_models.each(&:reload!)
61 | refute mock_model_parent.values_changed?
62 | mock_model_parent.remove_unmodified_children
63 | assert_equal 0, mock_model_parent.mock_models.size
64 | mock_model_parent.mock_models = [MockModel.new(name: 'M1'), MockModel.new(name: 'M2')]
65 | mock_model_parent.nested_attributes = [:mock_models]
66 | mock_model_parent.mock_models.each(&:reload!)
67 | mock_model_parent.mock_models.first.name = 'M1 Modified'
68 | mock_model_parent.remove_unmodified_children
69 | assert_equal 1, mock_model_parent.mock_models.size
70 | end
71 |
72 | test 'change tracking on new object' do
73 | mock_model = MockModel.new
74 | refute mock_model.changed?
75 | mock_model.name = 'Bryce'
76 | assert mock_model.changed?
77 | assert mock_model.name_changed?
78 | assert mock_model.name_changed?(from: nil, to: 'Bryce')
79 | assert_nil mock_model.name_was
80 | assert_equal [nil, 'Bryce'], mock_model.name_change
81 | mock_model.name = 'Billy'
82 | assert_equal [nil, 'Billy'], mock_model.name_change
83 | end
84 |
85 | test 'change tracking on existing object' do
86 | mock_model = MockModel.all.first
87 | refute mock_model.changed?
88 | mock_model.name = 'Billy'
89 | assert mock_model.changed?
90 | assert mock_model.name_changed?
91 | assert mock_model.name_changed?(from: 25.5, to: 'Billy')
92 | assert_equal 25.5, mock_model.name_was
93 | end
94 | end
95 |
--------------------------------------------------------------------------------
/test/entity_class_method_extensions.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # Additional methods added for testing only.
3 | #
4 | module EntityClassMethodExtensions
5 | def all_test_entities
6 | query = CloudDatastore.dataset.query(name)
7 | CloudDatastore.dataset.run(query)
8 | end
9 |
10 | def count_test_entities
11 | all_test_entities.length
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/factories.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :mock_model do
3 | sequence :name do |n|
4 | "Test Mock Model #{n}"
5 | end
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/images/test-image-1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/images/test-image-1.jpg
--------------------------------------------------------------------------------
/test/images/test-image-2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/images/test-image-2.jpeg
--------------------------------------------------------------------------------
/test/images/test-image-3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/images/test-image-3.png
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/.ruby-gemset:
--------------------------------------------------------------------------------
1 | datastore-example-rails-app
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/.ruby-version:
--------------------------------------------------------------------------------
1 | 3.3.0
2 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/Gemfile:
--------------------------------------------------------------------------------
1 | source 'https://rubygems.org'
2 | ruby File.read('.ruby-version').strip
3 |
4 | ###########################################################################################
5 | git_source(:github) { |repo| "https://github.com/#{repo}.git" }
6 | # gem 'foo_bar', :github => 'foo/bar'
7 | # Bundler would attempt to download the gem from https://github.com/foo/bar.git.
8 | ###########################################################################################
9 |
10 | gem 'rails', '~> 6.1.4'
11 | gem 'sass-rails', '>= 6'
12 | gem 'uglifier', '>= 1.3.0'
13 | gem 'coffee-rails', '~> 5.0'
14 |
15 | gem 'jquery-rails'
16 | gem 'turbolinks', '~> 5'
17 |
18 | gem 'puma', '~> 5.0'
19 | gem 'rack-timeout'
20 |
21 | gem 'activemodel-datastore', path: File.expand_path('../../../..', __FILE__)
22 |
23 | # Image storage
24 | gem 'carrierwave', '~> 2.1'
25 | gem 'mini_magick', '~> 4.7'
26 | gem 'fog-google', '~> 1.11'
27 |
28 | group :development, :test do
29 | gem 'byebug', platforms: [:mri, :mingw, :x64_mingw]
30 | gem 'capybara', '>= 2.15'
31 | gem 'selenium-webdriver'
32 | end
33 |
34 | group :development do
35 | gem 'web-console', '>= 3.3.0'
36 | gem 'listen'
37 | gem 'better_errors'
38 | end
39 |
40 | group :test do
41 | gem 'faker'
42 | gem 'factory_bot_rails'
43 | end
44 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/Gemfile.lock:
--------------------------------------------------------------------------------
1 | PATH
2 | remote: /Users/bryce/workspace/activemodel-datastore
3 | specs:
4 | activemodel-datastore (0.7.0)
5 | activemodel (>= 5.0.0)
6 | activesupport (>= 5.0.0)
7 | google-cloud-datastore (~> 2.0)
8 |
9 | GEM
10 | remote: https://rubygems.org/
11 | specs:
12 | actioncable (6.1.7.7)
13 | actionpack (= 6.1.7.7)
14 | activesupport (= 6.1.7.7)
15 | nio4r (~> 2.0)
16 | websocket-driver (>= 0.6.1)
17 | actionmailbox (6.1.7.7)
18 | actionpack (= 6.1.7.7)
19 | activejob (= 6.1.7.7)
20 | activerecord (= 6.1.7.7)
21 | activestorage (= 6.1.7.7)
22 | activesupport (= 6.1.7.7)
23 | mail (>= 2.7.1)
24 | actionmailer (6.1.7.7)
25 | actionpack (= 6.1.7.7)
26 | actionview (= 6.1.7.7)
27 | activejob (= 6.1.7.7)
28 | activesupport (= 6.1.7.7)
29 | mail (~> 2.5, >= 2.5.4)
30 | rails-dom-testing (~> 2.0)
31 | actionpack (6.1.7.7)
32 | actionview (= 6.1.7.7)
33 | activesupport (= 6.1.7.7)
34 | rack (~> 2.0, >= 2.0.9)
35 | rack-test (>= 0.6.3)
36 | rails-dom-testing (~> 2.0)
37 | rails-html-sanitizer (~> 1.0, >= 1.2.0)
38 | actiontext (6.1.7.7)
39 | actionpack (= 6.1.7.7)
40 | activerecord (= 6.1.7.7)
41 | activestorage (= 6.1.7.7)
42 | activesupport (= 6.1.7.7)
43 | nokogiri (>= 1.8.5)
44 | actionview (6.1.7.7)
45 | activesupport (= 6.1.7.7)
46 | builder (~> 3.1)
47 | erubi (~> 1.4)
48 | rails-dom-testing (~> 2.0)
49 | rails-html-sanitizer (~> 1.1, >= 1.2.0)
50 | activejob (6.1.7.7)
51 | activesupport (= 6.1.7.7)
52 | globalid (>= 0.3.6)
53 | activemodel (6.1.7.7)
54 | activesupport (= 6.1.7.7)
55 | activerecord (6.1.7.7)
56 | activemodel (= 6.1.7.7)
57 | activesupport (= 6.1.7.7)
58 | activestorage (6.1.7.7)
59 | actionpack (= 6.1.7.7)
60 | activejob (= 6.1.7.7)
61 | activerecord (= 6.1.7.7)
62 | activesupport (= 6.1.7.7)
63 | marcel (~> 1.0)
64 | mini_mime (>= 1.1.0)
65 | activesupport (6.1.7.7)
66 | concurrent-ruby (~> 1.0, >= 1.0.2)
67 | i18n (>= 1.6, < 2)
68 | minitest (>= 5.1)
69 | tzinfo (~> 2.0)
70 | zeitwerk (~> 2.3)
71 | addressable (2.8.6)
72 | public_suffix (>= 2.0.2, < 6.0)
73 | base64 (0.2.0)
74 | better_errors (2.10.1)
75 | erubi (>= 1.0.0)
76 | rack (>= 0.9.0)
77 | rouge (>= 1.0.0)
78 | bindex (0.8.1)
79 | builder (3.2.4)
80 | byebug (11.1.3)
81 | capybara (3.40.0)
82 | addressable
83 | matrix
84 | mini_mime (>= 0.1.3)
85 | nokogiri (~> 1.11)
86 | rack (>= 1.6.0)
87 | rack-test (>= 0.6.3)
88 | regexp_parser (>= 1.5, < 3.0)
89 | xpath (~> 3.2)
90 | carrierwave (2.2.5)
91 | activemodel (>= 5.0.0)
92 | activesupport (>= 5.0.0)
93 | addressable (~> 2.6)
94 | image_processing (~> 1.1)
95 | marcel (~> 1.0.0)
96 | mini_mime (>= 0.1.3)
97 | ssrf_filter (~> 1.0)
98 | coffee-rails (5.0.0)
99 | coffee-script (>= 2.2.0)
100 | railties (>= 5.2.0)
101 | coffee-script (2.4.1)
102 | coffee-script-source
103 | execjs
104 | coffee-script-source (1.12.2)
105 | concurrent-ruby (1.2.3)
106 | crass (1.0.6)
107 | date (3.3.4)
108 | declarative (0.0.20)
109 | erubi (1.12.0)
110 | excon (0.109.0)
111 | execjs (2.9.1)
112 | factory_bot (6.4.6)
113 | activesupport (>= 5.0.0)
114 | factory_bot_rails (6.4.3)
115 | factory_bot (~> 6.4)
116 | railties (>= 5.0.0)
117 | faker (3.2.3)
118 | i18n (>= 1.8.11, < 2)
119 | faraday (2.9.0)
120 | faraday-net_http (>= 2.0, < 3.2)
121 | faraday-net_http (3.1.0)
122 | net-http
123 | faraday-retry (2.2.0)
124 | faraday (~> 2.0)
125 | ffi (1.16.3)
126 | fog-core (2.2.4)
127 | builder
128 | excon (~> 0.71)
129 | formatador (~> 0.2)
130 | mime-types
131 | fog-google (1.23.0)
132 | addressable (>= 2.7.0)
133 | fog-core (< 2.3)
134 | fog-json (~> 1.2)
135 | fog-xml (~> 0.1.0)
136 | google-apis-compute_v1 (~> 0.53)
137 | google-apis-dns_v1 (~> 0.28)
138 | google-apis-iamcredentials_v1 (~> 0.15)
139 | google-apis-monitoring_v3 (~> 0.37)
140 | google-apis-pubsub_v1 (~> 0.30)
141 | google-apis-sqladmin_v1beta4 (~> 0.38)
142 | google-apis-storage_v1 (>= 0.19, < 1)
143 | google-cloud-env (~> 1.2)
144 | fog-json (1.2.0)
145 | fog-core
146 | multi_json (~> 1.10)
147 | fog-xml (0.1.4)
148 | fog-core
149 | nokogiri (>= 1.5.11, < 2.0.0)
150 | formatador (0.3.0)
151 | gapic-common (0.20.0)
152 | faraday (>= 1.9, < 3.a)
153 | faraday-retry (>= 1.0, < 3.a)
154 | google-protobuf (~> 3.14)
155 | googleapis-common-protos (>= 1.3.12, < 2.a)
156 | googleapis-common-protos-types (>= 1.3.1, < 2.a)
157 | googleauth (~> 1.0)
158 | grpc (~> 1.36)
159 | globalid (1.2.1)
160 | activesupport (>= 6.1)
161 | google-apis-compute_v1 (0.86.0)
162 | google-apis-core (>= 0.11.0, < 2.a)
163 | google-apis-core (0.11.3)
164 | addressable (~> 2.5, >= 2.5.1)
165 | googleauth (>= 0.16.2, < 2.a)
166 | httpclient (>= 2.8.1, < 3.a)
167 | mini_mime (~> 1.0)
168 | representable (~> 3.0)
169 | retriable (>= 2.0, < 4.a)
170 | rexml
171 | google-apis-dns_v1 (0.36.0)
172 | google-apis-core (>= 0.11.0, < 2.a)
173 | google-apis-iamcredentials_v1 (0.17.0)
174 | google-apis-core (>= 0.11.0, < 2.a)
175 | google-apis-monitoring_v3 (0.54.0)
176 | google-apis-core (>= 0.11.0, < 2.a)
177 | google-apis-pubsub_v1 (0.45.0)
178 | google-apis-core (>= 0.11.0, < 2.a)
179 | google-apis-sqladmin_v1beta4 (0.61.0)
180 | google-apis-core (>= 0.11.0, < 2.a)
181 | google-apis-storage_v1 (0.32.0)
182 | google-apis-core (>= 0.11.0, < 2.a)
183 | google-cloud-core (1.6.1)
184 | google-cloud-env (>= 1.0, < 3.a)
185 | google-cloud-errors (~> 1.0)
186 | google-cloud-datastore (2.8.0)
187 | google-cloud-core (~> 1.5)
188 | google-cloud-datastore-v1 (~> 0.0)
189 | google-cloud-datastore-v1 (0.15.0)
190 | gapic-common (>= 0.20.0, < 2.a)
191 | google-cloud-errors (~> 1.0)
192 | google-cloud-env (1.6.0)
193 | faraday (>= 0.17.3, < 3.0)
194 | google-cloud-errors (1.3.1)
195 | google-protobuf (3.25.3)
196 | googleapis-common-protos (1.5.0)
197 | google-protobuf (~> 3.18)
198 | googleapis-common-protos-types (~> 1.7)
199 | grpc (~> 1.41)
200 | googleapis-common-protos-types (1.13.0)
201 | google-protobuf (~> 3.18)
202 | googleauth (1.8.1)
203 | faraday (>= 0.17.3, < 3.a)
204 | jwt (>= 1.4, < 3.0)
205 | multi_json (~> 1.11)
206 | os (>= 0.9, < 2.0)
207 | signet (>= 0.16, < 2.a)
208 | grpc (1.62.0)
209 | google-protobuf (~> 3.25)
210 | googleapis-common-protos-types (~> 1.0)
211 | httpclient (2.8.3)
212 | i18n (1.14.1)
213 | concurrent-ruby (~> 1.0)
214 | image_processing (1.12.2)
215 | mini_magick (>= 4.9.5, < 5)
216 | ruby-vips (>= 2.0.17, < 3)
217 | jquery-rails (4.6.0)
218 | rails-dom-testing (>= 1, < 3)
219 | railties (>= 4.2.0)
220 | thor (>= 0.14, < 2.0)
221 | jwt (2.8.1)
222 | base64
223 | listen (3.9.0)
224 | rb-fsevent (~> 0.10, >= 0.10.3)
225 | rb-inotify (~> 0.9, >= 0.9.10)
226 | loofah (2.22.0)
227 | crass (~> 1.0.2)
228 | nokogiri (>= 1.12.0)
229 | mail (2.8.1)
230 | mini_mime (>= 0.1.1)
231 | net-imap
232 | net-pop
233 | net-smtp
234 | marcel (1.0.4)
235 | matrix (0.4.2)
236 | method_source (1.0.0)
237 | mime-types (3.5.2)
238 | mime-types-data (~> 3.2015)
239 | mime-types-data (3.2024.0206)
240 | mini_magick (4.12.0)
241 | mini_mime (1.1.5)
242 | mini_portile2 (2.8.5)
243 | minitest (5.22.2)
244 | multi_json (1.15.0)
245 | net-http (0.4.1)
246 | uri
247 | net-imap (0.4.10)
248 | date
249 | net-protocol
250 | net-pop (0.1.2)
251 | net-protocol
252 | net-protocol (0.2.2)
253 | timeout
254 | net-smtp (0.4.0.1)
255 | net-protocol
256 | nio4r (2.7.0)
257 | nokogiri (1.16.2)
258 | mini_portile2 (~> 2.8.2)
259 | racc (~> 1.4)
260 | os (1.1.4)
261 | public_suffix (5.0.4)
262 | puma (5.6.8)
263 | nio4r (~> 2.0)
264 | racc (1.7.3)
265 | rack (2.2.8.1)
266 | rack-test (2.1.0)
267 | rack (>= 1.3)
268 | rack-timeout (0.6.3)
269 | rails (6.1.7.7)
270 | actioncable (= 6.1.7.7)
271 | actionmailbox (= 6.1.7.7)
272 | actionmailer (= 6.1.7.7)
273 | actionpack (= 6.1.7.7)
274 | actiontext (= 6.1.7.7)
275 | actionview (= 6.1.7.7)
276 | activejob (= 6.1.7.7)
277 | activemodel (= 6.1.7.7)
278 | activerecord (= 6.1.7.7)
279 | activestorage (= 6.1.7.7)
280 | activesupport (= 6.1.7.7)
281 | bundler (>= 1.15.0)
282 | railties (= 6.1.7.7)
283 | sprockets-rails (>= 2.0.0)
284 | rails-dom-testing (2.2.0)
285 | activesupport (>= 5.0.0)
286 | minitest
287 | nokogiri (>= 1.6)
288 | rails-html-sanitizer (1.6.0)
289 | loofah (~> 2.21)
290 | nokogiri (~> 1.14)
291 | railties (6.1.7.7)
292 | actionpack (= 6.1.7.7)
293 | activesupport (= 6.1.7.7)
294 | method_source
295 | rake (>= 12.2)
296 | thor (~> 1.0)
297 | rake (13.1.0)
298 | rb-fsevent (0.11.2)
299 | rb-inotify (0.10.1)
300 | ffi (~> 1.0)
301 | regexp_parser (2.9.0)
302 | representable (3.2.0)
303 | declarative (< 0.1.0)
304 | trailblazer-option (>= 0.1.1, < 0.2.0)
305 | uber (< 0.2.0)
306 | retriable (3.1.2)
307 | rexml (3.2.6)
308 | rouge (4.2.0)
309 | ruby-vips (2.2.1)
310 | ffi (~> 1.12)
311 | rubyzip (2.3.2)
312 | sass-rails (6.0.0)
313 | sassc-rails (~> 2.1, >= 2.1.1)
314 | sassc (2.4.0)
315 | ffi (~> 1.9)
316 | sassc-rails (2.1.2)
317 | railties (>= 4.0.0)
318 | sassc (>= 2.0)
319 | sprockets (> 3.0)
320 | sprockets-rails
321 | tilt
322 | selenium-webdriver (4.18.1)
323 | base64 (~> 0.2)
324 | rexml (~> 3.2, >= 3.2.5)
325 | rubyzip (>= 1.2.2, < 3.0)
326 | websocket (~> 1.0)
327 | signet (0.19.0)
328 | addressable (~> 2.8)
329 | faraday (>= 0.17.5, < 3.a)
330 | jwt (>= 1.5, < 3.0)
331 | multi_json (~> 1.10)
332 | sprockets (4.2.1)
333 | concurrent-ruby (~> 1.0)
334 | rack (>= 2.2.4, < 4)
335 | sprockets-rails (3.4.2)
336 | actionpack (>= 5.2)
337 | activesupport (>= 5.2)
338 | sprockets (>= 3.0.0)
339 | ssrf_filter (1.1.2)
340 | thor (1.3.1)
341 | tilt (2.3.0)
342 | timeout (0.4.1)
343 | trailblazer-option (0.1.2)
344 | turbolinks (5.2.1)
345 | turbolinks-source (~> 5.2)
346 | turbolinks-source (5.2.0)
347 | tzinfo (2.0.6)
348 | concurrent-ruby (~> 1.0)
349 | uber (0.1.0)
350 | uglifier (4.2.0)
351 | execjs (>= 0.3.0, < 3)
352 | uri (0.13.0)
353 | web-console (4.2.1)
354 | actionview (>= 6.0.0)
355 | activemodel (>= 6.0.0)
356 | bindex (>= 0.4.0)
357 | railties (>= 6.0.0)
358 | websocket (1.2.10)
359 | websocket-driver (0.7.6)
360 | websocket-extensions (>= 0.1.0)
361 | websocket-extensions (0.1.5)
362 | xpath (3.2.0)
363 | nokogiri (~> 1.8)
364 | zeitwerk (2.6.13)
365 |
366 | PLATFORMS
367 | ruby
368 |
369 | DEPENDENCIES
370 | activemodel-datastore!
371 | better_errors
372 | byebug
373 | capybara (>= 2.15)
374 | carrierwave (~> 2.1)
375 | coffee-rails (~> 5.0)
376 | factory_bot_rails
377 | faker
378 | fog-google (~> 1.11)
379 | jquery-rails
380 | listen
381 | mini_magick (~> 4.7)
382 | puma (~> 5.0)
383 | rack-timeout
384 | rails (~> 6.1.4)
385 | sass-rails (>= 6)
386 | selenium-webdriver
387 | turbolinks (~> 5)
388 | uglifier (>= 1.3.0)
389 | web-console (>= 3.3.0)
390 |
391 | RUBY VERSION
392 | ruby 3.3.0p0
393 |
394 | BUNDLED WITH
395 | 2.2.30
396 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/Procfile:
--------------------------------------------------------------------------------
1 | web: bundle exec puma -C config/puma.rb
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/Rakefile:
--------------------------------------------------------------------------------
1 | # Add your own tasks in files placed in lib/tasks ending in .rake,
2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3 |
4 | require File.expand_path('config/application', __dir__)
5 |
6 | Rails.application.load_tasks
7 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/config/manifest.js:
--------------------------------------------------------------------------------
1 | //= link_tree ../images
2 | //= link_directory ../javascripts .js
3 | //= link_directory ../stylesheets .css
4 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/images/fallback_user.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/app/assets/images/fallback_user.png
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/javascripts/active_model_nested_attr.coffee:
--------------------------------------------------------------------------------
1 | initializeNestedAttributes = (name) ->
2 | if $(".duplicatable_nested_#{name}_form").length
3 | forms_on_page = $(".duplicatable_nested_#{name}_form").length
4 |
5 | $('body').on 'click', ".destroy_nested_#{name}_form", (e) ->
6 | e.preventDefault()
7 | if $(".duplicatable_nested_#{name}_form:visible").length > 1
8 | $(this).closest(".duplicatable_nested_#{name}_form").slideUp().remove()
9 |
10 | $('body').on 'click', ".mark_nested_#{name}_form_as_destroyed", (e) ->
11 | e.preventDefault()
12 | form = $(this).closest(".duplicatable_nested_#{name}_form")
13 | form.find('input[id*="_destroy"]').val('true')
14 | form.slideUp().hide()
15 |
16 | $(".insert_nested_#{name}_form").on 'click', (e) ->
17 | e.preventDefault()
18 | last_nested_form = $(".duplicatable_nested_#{name}_form").last()
19 | new_nested_form = $(last_nested_form).clone(true)
20 | new_nested_form.show()
21 | forms_on_page += 1
22 |
23 | $(new_nested_form).find(".mark_nested_#{name}_form_as_destroyed").each ->
24 | $(this).toggleClass("mark_nested_#{name}_form_as_destroyed destroy_nested_#{name}_form")
25 |
26 | $(new_nested_form).find('label').each ->
27 | old_label = $(this).attr 'for'
28 | if old_label?
29 | new_label = old_label.replace(new RegExp(/_[0-9]+_/), "_#{forms_on_page - 1}_")
30 | $(this).attr 'for', new_label
31 |
32 | $(new_nested_form).find('select, input').each ->
33 | $(this).removeData()
34 | if $(this).is(':checkbox')
35 | $(this).prop('checked', false)
36 | else if $(this).is('select')
37 | $(this).find('option:eq(0)').prop('selected', true)
38 | else
39 | $(this).val('')
40 | old_id = $(this).attr 'id'
41 | if old_id?
42 | new_id = old_id.replace(new RegExp(/_[0-9]+_/), "_#{forms_on_page - 1}_")
43 | $(this).attr 'id', new_id
44 |
45 | old_name = $(this).attr 'name'
46 | new_name = old_name.replace(new RegExp(/\[[0-9]+]/), "[#{forms_on_page - 1}]")
47 | $(this).attr 'name', new_name
48 |
49 | $(new_nested_form).insertAfter(last_nested_form)
50 | else
51 | $('body').on 'click', ".destroy_nested_#{name}_form", (e) ->
52 | e.preventDefault()
53 | $('body').on 'click',".mark_nested_#{name}_form_as_destroyed", (e) ->
54 | e.preventDefault()
55 | $(".insert_nested_#{name}_form").on 'click', (e) ->
56 | e.preventDefault()
57 | window.initializeNestedAttributes = initializeNestedAttributes
58 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/javascripts/application.js:
--------------------------------------------------------------------------------
1 | // This is a manifest file that'll be compiled into application.js, which will include all the files
2 | // listed below.
3 | //
4 | // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5 | // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6 | //
7 | // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8 | // compiled file.
9 | //
10 | // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11 | // about supported directives.
12 | //
13 | //= require jquery
14 | //= require jquery_ujs
15 |
16 | // Active Model Nested Attributes
17 | //= require active_model_nested_attr
18 |
19 | //= require turbolinks
20 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/stylesheets/application.scss:
--------------------------------------------------------------------------------
1 | /*
2 | * This is a manifest file that'll be compiled into application.css, which will include all the files
3 | * listed below.
4 | *
5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7 | *
8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10 | * files in this directory. Styles in this file should be added after the last require_* statement.
11 | * It is generally better to create a new file per style scope.
12 | */
13 |
14 | @import "scaffolds";
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/assets/stylesheets/scaffolds.scss:
--------------------------------------------------------------------------------
1 | body {
2 | background-color: #fff;
3 | color: #333;
4 | font-family: verdana, arial, helvetica, sans-serif;
5 | font-size: 13px;
6 | line-height: 18px;
7 | }
8 |
9 | p, ol, ul, td {
10 | font-family: verdana, arial, helvetica, sans-serif;
11 | font-size: 13px;
12 | line-height: 18px;
13 | }
14 |
15 | pre {
16 | background-color: #eee;
17 | padding: 10px;
18 | font-size: 11px;
19 | }
20 |
21 | a {
22 | color: #000;
23 |
24 | &:visited {
25 | color: #666;
26 | }
27 |
28 | &:hover {
29 | color: #fff;
30 | background-color: #000;
31 | }
32 | }
33 |
34 | div {
35 | &.field, &.actions {
36 | margin-bottom: 10px;
37 | }
38 | }
39 |
40 | #notice {
41 | color: green;
42 | }
43 |
44 | .field_with_errors {
45 | padding: 2px;
46 | background-color: red;
47 | display: table;
48 | }
49 |
50 | #error_explanation {
51 | width: 450px;
52 | border: 2px solid red;
53 | padding: 7px;
54 | padding-bottom: 0;
55 | margin-bottom: 20px;
56 | background-color: #f0f0f0;
57 |
58 | h2 {
59 | text-align: left;
60 | font-weight: bold;
61 | padding: 5px 5px 5px 15px;
62 | font-size: 12px;
63 | margin: -7px;
64 | margin-bottom: 0px;
65 | background-color: #c00;
66 | color: #fff;
67 | }
68 |
69 | ul li {
70 | font-size: 12px;
71 | list-style: square;
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/controllers/application_controller.rb:
--------------------------------------------------------------------------------
1 | class ApplicationController < ActionController::Base
2 | # Prevent CSRF attacks by raising an exception.
3 | # For APIs, you may want to use :null_session instead.
4 | protect_from_forgery with: :exception
5 |
6 | ##
7 | # This could be the id of an Account, Company, etc.
8 | #
9 | def fake_ancestor
10 | 12345
11 | end
12 | end
13 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/controllers/concerns/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/app/controllers/concerns/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/controllers/home_controller.rb:
--------------------------------------------------------------------------------
1 | class HomeController < ApplicationController
2 | def index
3 | end
4 | end
5 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/controllers/users_controller.rb:
--------------------------------------------------------------------------------
1 | class UsersController < ApplicationController
2 | before_action :set_user, only: [:show, :edit, :update, :destroy]
3 |
4 | def index
5 | @users = User.all(ancestor: User.parent_key(fake_ancestor))
6 | end
7 |
8 | def show
9 | end
10 |
11 | def new
12 | @user = User.new
13 | end
14 |
15 | def edit
16 | end
17 |
18 | def create
19 | @user = User.new(user_params)
20 | @user.parent_key_id = fake_ancestor
21 | respond_to do |format|
22 | if @user.save
23 | format.html { redirect_to @user, notice: 'User was successfully created.' }
24 | else
25 | format.html { render :new }
26 | end
27 | end
28 | end
29 |
30 | def update
31 | respond_to do |format|
32 | if @user.update(user_params)
33 | format.html { redirect_to @user, notice: 'User was successfully updated.' }
34 | else
35 | format.html { render :edit }
36 | end
37 | end
38 | end
39 |
40 | def destroy
41 | @user.destroy
42 | respond_to do |format|
43 | format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
44 | end
45 | end
46 |
47 | private
48 |
49 | def set_user
50 | @user = User.find(params[:id], parent: User.parent_key(fake_ancestor))
51 | end
52 |
53 | def user_params
54 | params.require(:user).permit(:email, :name, :profile_image)
55 | end
56 | end
57 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/helpers/application_helper.rb:
--------------------------------------------------------------------------------
1 | module ApplicationHelper
2 | end
3 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/helpers/users_helper.rb:
--------------------------------------------------------------------------------
1 | module UsersHelper
2 | end
3 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/app/mailers/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/models/recipe.rb:
--------------------------------------------------------------------------------
1 | class Recipe
2 | include ActiveModel::Datastore
3 |
4 | attr_accessor :amount # Float
5 | attr_accessor :name # String
6 | attr_accessor :ingredients # gives us 'accepts_nested_attributes_for' like functionality
7 |
8 | before_validation :set_default_values, :set_nested_recipe_ids
9 | after_validation :format_values
10 |
11 | validates :amount, numericality: { greater_than_or_equal_to: 1 }
12 | validates :name, presence: true
13 |
14 | validates :ingredients, presence: true # Recipes must have at least one RecipeContent.
15 | validates_associated :ingredients
16 |
17 | enable_change_tracking :amount, :name
18 |
19 | def entity_properties
20 | %w[amount name]
21 | end
22 |
23 | def set_default_values
24 | default_property_value :amount, 100.0
25 | end
26 |
27 | def format_values
28 | format_property_value :amount, :float
29 | end
30 |
31 | def ingredients_attributes=(attributes)
32 | assign_nested_attributes(:ingredients, attributes)
33 | end
34 |
35 | def build_ingredients
36 | return unless ingredients.nil? || ingredients.empty?
37 |
38 | self.ingredients = [Ingredient.new(order: 1)]
39 | end
40 |
41 | def set_ingredient
42 | content = Ingredient.find_latest(account_id, where: ['recipeId', '=', id])
43 | content.sort! { |a, b| a.order <=> b.order }
44 | self.ingredients = content
45 | end
46 |
47 | private
48 |
49 | ##
50 | # For each associated Ingredient sets the recipeId to the id of the Recipe.
51 | #
52 | def set_nested_recipe_ids
53 | nested_models.each { |ingredient| ingredient.recipeId = id }
54 | end
55 | end
56 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/models/user.rb:
--------------------------------------------------------------------------------
1 | require 'active_model/datastore/carrier_wave_uploader'
2 |
3 | class User
4 | include ActiveModel::Datastore
5 | extend CarrierWaveUploader
6 |
7 | attr_accessor :email, :enabled, :name, :profile_image, :role, :state
8 |
9 | mount_uploader :profile_image, ProfileImageUploader
10 |
11 | before_validation :set_default_values
12 | after_validation :format_values
13 |
14 | validates :email, format: { with: /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i }
15 | validates :name, presence: true, length: { maximum: 30 }
16 | validates :role, presence: true
17 |
18 | def entity_properties
19 | %w[email enabled name profile_image role]
20 | end
21 |
22 | def set_default_values
23 | default_property_value :enabled, true
24 | default_property_value :role, 1
25 | end
26 |
27 | def format_values
28 | format_property_value :role, :integer
29 | end
30 | end
31 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/uploaders/profile_image_uploader.rb:
--------------------------------------------------------------------------------
1 | class ProfileImageUploader < CarrierWave::Uploader::Base
2 | include CarrierWave::MiniMagick
3 |
4 | storage :fog if Rails.env.production?
5 |
6 | # Override the directory where uploaded files will be stored.
7 | # This is a sensible default for uploaders that are meant to be mounted:
8 | def store_dir
9 | "uploads/#{model.class.to_s.underscore}/#{mounted_as}/#{model.id}"
10 | end
11 |
12 | def default_url(*)
13 | ActionController::Base.helpers.asset_path([version_name, 'fallback_user.png'].compact.join('_'))
14 | end
15 |
16 | # Override as we don't want the files deleted from Cloud Storage.
17 | def remove!
18 | return unless model.respond_to?(:keep_file) && model.keep_file
19 |
20 | super
21 | end
22 |
23 | # Process files as they are uploaded:
24 | # Resize the image to fit within the specified dimensions while retaining the original aspect
25 | # ratio. The image may be shorter or narrower than specified in the smaller dimension but will
26 | # not be larger than the specified values.
27 | process resize_to_fit: [300, 200]
28 |
29 | def extension_whitelist
30 | %w[jpg jpeg gif png]
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/home/index.html.erb:
--------------------------------------------------------------------------------
1 | ActiveModel Datastore Demo
2 | Home page
3 |
4 | <%= link_to('Click here to view Users in datastore', users_path) %>
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/layouts/application.html.erb:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Cloud Datastore Example
5 | <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
6 | <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
7 | <%= csrf_meta_tags %>
8 |
9 |
10 |
11 | <%= yield %>
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/users/_form.html.erb:
--------------------------------------------------------------------------------
1 | <%= form_for(@user) do |f| %>
2 | <% if @user.errors.any? %>
3 |
4 |
<%= pluralize(@user.errors.count, 'error') %> prohibited this user from being saved:
5 |
6 |
7 | <% @user.errors.full_messages.each do |message| %>
8 | - <%= message %>
9 | <% end %>
10 |
11 |
12 | <% end %>
13 |
14 |
15 | <%= f.label :name, 'Name:' %>
16 | <%= f.text_field :name, size: 40 %>
17 |
18 |
19 |
20 | <%= f.label :email, 'Email:' %>
21 | <%= f.text_field :email, size: 40 %>
22 |
23 |
24 |
25 | <%= f.label :profile_image, class: 'form-control-label' %>
26 | <%= f.file_field :profile_image %>
27 |
28 | <% if f.object.profile_image? && f.object.errors[:profile_image].blank? %>
29 | <%= image_tag f.object.profile_image.url %>
30 | <% end %>
31 | <%= f.hidden_field :profile_image_cache unless f.object.errors[:profile_image].any? %>
32 |
33 |
34 | <%= f.submit %>
35 |
36 | <% end %>
37 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/users/edit.html.erb:
--------------------------------------------------------------------------------
1 | Editing User
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Show', @user %> |
6 | <%= link_to 'Back', users_path %>
7 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/users/index.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 | Listing Users
4 |
5 |
6 |
7 |
8 | |
9 |
10 |
11 |
12 |
13 | <% @users.each do |user| %>
14 |
15 | <%= image_tag user.profile_image.url, style: 'width: 50px;' %> |
16 | <%= user.name %> |
17 | <%= user.email %> |
18 | <%= link_to 'Show', user %> |
19 | <%= link_to 'Edit', edit_user_path(user) %> |
20 | <%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %> |
21 |
22 | <% end %>
23 |
24 |
25 |
26 |
27 |
28 | <%= link_to 'Create User', new_user_path %>
29 |
30 |
31 |
32 |
33 | <%= link_to 'Back to Home', root_path %>
34 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/users/new.html.erb:
--------------------------------------------------------------------------------
1 | New User
2 |
3 | <%= render 'form' %>
4 |
5 | <%= link_to 'Back', users_path %>
6 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/app/views/users/show.html.erb:
--------------------------------------------------------------------------------
1 | <%= notice %>
2 |
3 |
4 | Name:
5 | <%= @user.name %>
6 |
7 |
8 | Email:
9 | <%= @user.email %>
10 |
11 |
12 | <%= link_to 'Edit', edit_user_path(@user) %> |
13 | <%= link_to 'Back', users_path %>
14 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/bundle:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
3 | load Gem.bin_path('bundler', 'bundle')
4 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/rails:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_PATH = File.expand_path('../config/application', __dir__)
3 | require_relative '../config/boot'
4 | require 'rails/commands'
5 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/rake:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require_relative '../config/boot'
3 | require 'rake'
4 | Rake.application.run
5 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/setup:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require "fileutils"
3 |
4 | # path to your application root.
5 | APP_ROOT = File.expand_path('..', __dir__)
6 |
7 | def system!(*args)
8 | system(*args) || abort("\n== Command #{args} failed ==")
9 | end
10 |
11 | FileUtils.chdir APP_ROOT do
12 | # This script is a way to set up or update your development environment automatically.
13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome.
14 | # Add necessary setup steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies
21 | system! 'bin/yarn'
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system! 'bin/rails log:clear tmp:clear'
25 |
26 | puts "\n== Restarting application server =="
27 | system! 'bin/rails restart'
28 | end
29 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/spring:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 |
3 | # This file loads spring without using Bundler, in order to be fast.
4 | # It gets overwritten when you run the `spring binstub` command.
5 |
6 | unless defined?(Spring)
7 | require 'rubygems'
8 | require 'bundler'
9 |
10 | if (match = Bundler.default_lockfile.read.match(/^GEM$.*?^ (?: )*spring \((.*?)\)$.*?^$/m))
11 | Gem.paths = { 'GEM_PATH' => [Bundler.bundle_path.to_s, *Gem.path].uniq.join(Gem.path_separator) }
12 | gem 'spring', match[1]
13 | require 'spring/binstub'
14 | end
15 | end
16 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/update:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | require 'fileutils'
3 | include FileUtils
4 |
5 | # path to your application root.
6 | APP_ROOT = File.expand_path('..', __dir__)
7 |
8 | def system!(*args)
9 | system(*args) || abort("\n== Command #{args} failed ==")
10 | end
11 |
12 | chdir APP_ROOT do
13 | # This script is a way to update your development environment automatically.
14 | # Add necessary update steps to this file.
15 |
16 | puts '== Installing dependencies =='
17 | system! 'gem install bundler --conservative'
18 | system('bundle check') || system!('bundle install')
19 |
20 | # Install JavaScript dependencies if using Yarn
21 | # system('bin/yarn')
22 |
23 | puts "\n== Removing old logs and tempfiles =="
24 | system! 'bin/rails log:clear tmp:clear'
25 |
26 | puts "\n== Restarting application server =="
27 | system! 'bin/rails restart'
28 | end
29 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/bin/yarn:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env ruby
2 | APP_ROOT = File.expand_path('..', __dir__)
3 | Dir.chdir(APP_ROOT) do
4 | yarn = ENV["PATH"].split(File::PATH_SEPARATOR).
5 | select { |dir| File.expand_path(dir) != __dir__ }.
6 | product(["yarn", "yarn.cmd", "yarn.ps1"]).
7 | map { |dir, file| File.expand_path(file, dir) }.
8 | find { |file| File.executable?(file) }
9 |
10 | if yarn
11 | exec yarn, *ARGV
12 | else
13 | $stderr.puts "Yarn executable was not detected in the system."
14 | $stderr.puts "Download Yarn at https://yarnpkg.com/en/docs/install"
15 | exit 1
16 | end
17 | end
18 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config.ru:
--------------------------------------------------------------------------------
1 | # This file is used by Rack-based servers to start the application.
2 |
3 | require_relative 'config/environment'
4 |
5 | run Rails.application
6 | Rails.application.load_server
7 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/application.rb:
--------------------------------------------------------------------------------
1 | require_relative 'boot'
2 |
3 | require 'rails'
4 | # Pick the frameworks you want:
5 | require 'active_model/railtie'
6 | require 'active_job/railtie'
7 | # require 'active_record/railtie'
8 | # require 'active_storage/engine'
9 | require 'action_controller/railtie'
10 | require 'action_mailer/railtie'
11 | # require 'action_mailbox/engine'
12 | # require 'action_text/engine'
13 | require 'action_view/railtie'
14 | require 'action_cable/engine'
15 | require 'sprockets/railtie'
16 | require 'rails/test_unit/railtie'
17 |
18 | # Require the gems listed in Gemfile, including any gems
19 | # you've limited to :test, :development, or :production.
20 | Bundler.require(*Rails.groups)
21 |
22 | module DatastoreExampleRailsApp
23 | class Application < Rails::Application
24 | # Initialize configuration defaults for originally generated Rails version.
25 | config.load_defaults 6.1
26 |
27 | # Configuration for the application, engines, and railties goes here.
28 | #
29 | # These settings can be overridden in specific environments using the files
30 | # in config/environments, which are processed later.
31 | #
32 | # config.time_zone = 'Central Time (US & Canada)'
33 | # config.eager_load_paths << Rails.root.join('extras')
34 | end
35 | end
36 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/boot.rb:
--------------------------------------------------------------------------------
1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__)
2 |
3 | require 'bundler/setup' # Set up gems listed in the Gemfile.
4 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/cable.yml:
--------------------------------------------------------------------------------
1 | development:
2 | adapter: async
3 |
4 | test:
5 | adapter: test
6 |
7 | production:
8 | adapter: redis
9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %>
10 | channel_prefix: datastore_example_rails_app_production
11 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/environment.rb:
--------------------------------------------------------------------------------
1 | # Load the Rails application.
2 | require_relative 'application'
3 |
4 | # Initialize the Rails application.
5 | Rails.application.initialize!
6 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/environments/development.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # In the development environment your application's code is reloaded any time
7 | # it changes. This slows down response time but is perfect for development
8 | # since you don't have to restart the web server when you make code changes.
9 | config.cache_classes = false
10 |
11 | # Do not eager load code on boot.
12 | config.eager_load = false
13 |
14 | # Show full error reports.
15 | config.consider_all_requests_local = true
16 |
17 | # Enable/disable caching. By default caching is disabled.
18 | # Run rails dev:cache to toggle caching.
19 | if Rails.root.join('tmp', 'caching-dev.txt').exist?
20 | config.action_controller.perform_caching = true
21 | config.action_controller.enable_fragment_cache_logging = true
22 |
23 | config.cache_store = :memory_store
24 | config.public_file_server.headers = {
25 | 'Cache-Control' => "public, max-age=#{2.days.to_i}"
26 | }
27 | else
28 | config.action_controller.perform_caching = false
29 |
30 | config.cache_store = :null_store
31 | end
32 |
33 | # Don't care if the mailer can't send.
34 | config.action_mailer.raise_delivery_errors = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Print deprecation notices to the Rails logger.
39 | config.active_support.deprecation = :log
40 |
41 | # Raise exceptions for disallowed deprecations.
42 | config.active_support.disallowed_deprecation = :raise
43 |
44 | # Tell Active Support which deprecation messages to disallow.
45 | config.active_support.disallowed_deprecation_warnings = []
46 |
47 | # Debug mode disables concatenation and preprocessing of assets.
48 | # This option may cause significant delays in view rendering with a large
49 | # number of complex assets.
50 | config.assets.debug = true
51 |
52 | # Suppress logger output for asset requests.
53 | config.assets.quiet = true
54 |
55 | # Raises error for missing translations.
56 | # config.i18n.raise_on_missing_translations = true
57 |
58 | # Annotate rendered view with file names.
59 | # config.action_view.annotate_rendered_view_with_filenames = true
60 |
61 | # Use an evented file watcher to asynchronously detect changes in source code,
62 | # routes, locales, etc. This feature depends on the listen gem.
63 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker
64 |
65 | # Uncomment if you wish to allow Action Cable access from any origin.
66 | # config.action_cable.disable_request_forgery_protection = true
67 | end
68 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/environments/production.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | Rails.application.configure do
4 | # Settings specified here will take precedence over those in config/application.rb.
5 |
6 | # Code is not reloaded between requests.
7 | config.cache_classes = true
8 |
9 | # Eager load code on boot. This eager loads most of Rails and
10 | # your application in memory, allowing both threaded web servers
11 | # and those relying on copy on write to perform better.
12 | # Rake tasks automatically ignore this option for performance.
13 | config.eager_load = true
14 |
15 | # Full error reports are disabled and caching is turned on.
16 | config.consider_all_requests_local = false
17 | config.action_controller.perform_caching = true
18 |
19 | # Ensures that a master key has been made available in either ENV["RAILS_MASTER_KEY"]
20 | # or in config/master.key. This key is used to decrypt credentials (and other encrypted files).
21 | config.require_master_key = true
22 |
23 | # Disable serving static files from the `/public` folder by default since
24 | # Apache or NGINX already handles this.
25 | config.public_file_server.enabled = ENV['RAILS_SERVE_STATIC_FILES'].present?
26 |
27 | # Compress CSS using a preprocessor.
28 | # config.assets.css_compressor = :sass
29 |
30 | # Do not fallback to assets pipeline if a precompiled asset is missed.
31 | config.assets.compile = false
32 |
33 | # Enable serving of images, stylesheets, and JavaScripts from an asset server.
34 | # config.asset_host = 'http://assets.example.com'
35 |
36 | # Specifies the header that your server uses for sending files.
37 | # config.action_dispatch.x_sendfile_header = 'X-Sendfile' # for Apache
38 | # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
39 |
40 | # Mount Action Cable outside main process or domain.
41 | # config.action_cable.mount_path = nil
42 | # config.action_cable.url = 'wss://example.com/cable'
43 | # config.action_cable.allowed_request_origins = [ 'http://example.com', /http:\/\/example.*/ ]
44 |
45 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies.
46 | config.force_ssl = true
47 |
48 | # Include generic and useful information about system operation, but avoid logging too much
49 | # information to avoid inadvertent exposure of personally identifiable information (PII).
50 | config.log_level = :info
51 |
52 | # Prepend all log lines with the following tags.
53 | config.log_tags = [ :request_id ]
54 |
55 | # Use a different cache store in production.
56 | # config.cache_store = :mem_cache_store
57 |
58 | # Use a real queuing backend for Active Job (and separate queues per environment).
59 | # config.active_job.queue_adapter = :resque
60 | # config.active_job.queue_name_prefix = "datastore_example_rails_app_production"
61 |
62 | config.action_mailer.perform_caching = false
63 |
64 | # Ignore bad email addresses and do not raise email delivery errors.
65 | # Set this to true and configure the email server for immediate delivery to raise delivery errors.
66 | # config.action_mailer.raise_delivery_errors = false
67 |
68 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to
69 | # the I18n.default_locale when a translation cannot be found).
70 | config.i18n.fallbacks = true
71 |
72 | # Send deprecation notices to registered listeners.
73 | config.active_support.deprecation = :notify
74 |
75 | # Log disallowed deprecations.
76 | config.active_support.disallowed_deprecation = :log
77 |
78 | # Tell Active Support which deprecation messages to disallow.
79 | config.active_support.disallowed_deprecation_warnings = []
80 |
81 | # Use default logging formatter so that PID and timestamp are not suppressed.
82 | config.log_formatter = ::Logger::Formatter.new
83 |
84 | # Use a different logger for distributed setups.
85 | # require "syslog/logger"
86 | # config.logger = ActiveSupport::TaggedLogging.new(Syslog::Logger.new 'app-name')
87 |
88 | if ENV["RAILS_LOG_TO_STDOUT"].present?
89 | logger = ActiveSupport::Logger.new(STDOUT)
90 | logger.formatter = config.log_formatter
91 | config.logger = ActiveSupport::TaggedLogging.new(logger)
92 | end
93 |
94 | # Inserts middleware to perform automatic connection switching.
95 | # The `database_selector` hash is used to pass options to the DatabaseSelector
96 | # middleware. The `delay` is used to determine how long to wait after a write
97 | # to send a subsequent read to the primary.
98 | #
99 | # The `database_resolver` class is used by the middleware to determine which
100 | # database is appropriate to use based on the time delay.
101 | #
102 | # The `database_resolver_context` class is used by the middleware to set
103 | # timestamps for the last write to the primary. The resolver uses the context
104 | # class timestamps to determine how long to wait before reading from the
105 | # replica.
106 | #
107 | # By default Rails will store a last write timestamp in the session. The
108 | # DatabaseSelector middleware is designed as such you can define your own
109 | # strategy for connection switching and pass that into the middleware through
110 | # these configuration options.
111 | # config.active_record.database_selector = { delay: 2.seconds }
112 | # config.active_record.database_resolver = ActiveRecord::Middleware::DatabaseSelector::Resolver
113 | # config.active_record.database_resolver_context = ActiveRecord::Middleware::DatabaseSelector::Resolver::Session
114 | end
115 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/environments/test.rb:
--------------------------------------------------------------------------------
1 | require "active_support/core_ext/integer/time"
2 |
3 | # The test environment is used exclusively to run your application's
4 | # test suite. You never need to work with it otherwise. Remember that
5 | # your test database is "scratch space" for the test suite and is wiped
6 | # and recreated between test runs. Don't rely on the data there!
7 |
8 | Rails.application.configure do
9 | # Settings specified here will take precedence over those in config/application.rb.
10 |
11 | config.cache_classes = false
12 | config.action_view.cache_template_loading = true
13 |
14 | # Do not eager load code on boot. This avoids loading your whole application
15 | # just for the purpose of running a single test. If you are using a tool that
16 | # preloads Rails for running tests, you may have to set it to true.
17 | config.eager_load = false
18 |
19 | # Configure public file server for tests with Cache-Control for performance.
20 | config.public_file_server.enabled = true
21 | config.public_file_server.headers = {
22 | 'Cache-Control' => "public, max-age=#{1.hour.to_i}"
23 | }
24 |
25 | # Show full error reports and disable caching.
26 | config.consider_all_requests_local = true
27 | config.action_controller.perform_caching = false
28 | config.cache_store = :null_store
29 |
30 | # Raise exceptions instead of rendering exception templates.
31 | config.action_dispatch.show_exceptions = false
32 |
33 | # Disable request forgery protection in test environment.
34 | config.action_controller.allow_forgery_protection = false
35 |
36 | config.action_mailer.perform_caching = false
37 |
38 | # Tell Action Mailer not to deliver emails to the real world.
39 | # The :test delivery method accumulates sent emails in the
40 | # ActionMailer::Base.deliveries array.
41 | config.action_mailer.delivery_method = :test
42 |
43 | # Print deprecation notices to the stderr.
44 | config.active_support.deprecation = :stderr
45 |
46 | # Raise exceptions for disallowed deprecations.
47 | config.active_support.disallowed_deprecation = :raise
48 |
49 | # Tell Active Support which deprecation messages to disallow.
50 | config.active_support.disallowed_deprecation_warnings = []
51 |
52 | # Raises error for missing translations.
53 | # config.i18n.raise_on_missing_translations = true
54 |
55 | # Annotate rendered view with file names.
56 | # config.action_view.annotate_rendered_view_with_filenames = true
57 | end
58 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/application_controller_renderer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # ActiveSupport::Reloader.to_prepare do
4 | # ApplicationController.renderer.defaults.merge!(
5 | # http_host: 'example.org',
6 | # https: false
7 | # )
8 | # end
9 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/assets.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Version of your assets, change this if you want to expire all your assets.
4 | Rails.application.config.assets.version = '1.0'
5 |
6 | # Add additional assets to the asset load path.
7 | # Rails.application.config.assets.paths << Emoji.images_path
8 | # Add Yarn node_modules folder to the asset load path.
9 | Rails.application.config.assets.paths << Rails.root.join('node_modules')
10 |
11 | # Precompile additional assets.
12 | # application.js, application.css, and all non-JS/CSS in the app/assets
13 | # folder are already added.
14 | # Rails.application.config.assets.precompile += %w( admin.js admin.css )
15 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/backtrace_silencers.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces.
4 | # Rails.backtrace_cleaner.add_silencer { |line| /my_noisy_library/.match?(line) }
5 |
6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code
7 | # by setting BACKTRACE=1 before calling your invocation, like "BACKTRACE=1 ./bin/rails runner 'MyClass.perform'".
8 | Rails.backtrace_cleaner.remove_silencers! if ENV["BACKTRACE"]
9 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/carrierwave.rb:
--------------------------------------------------------------------------------
1 | if Rails.env.development?
2 | # SERVICE_ACCOUNT = YAML.load_file(Rails.root.join('config', 'service_account.yml'))[Rails.env]
3 | #
4 | # CarrierWave.configure do |config|
5 | # config.fog_provider = 'fog/google'
6 | # config.fog_credentials = {
7 | # provider: 'Google',
8 | # google_project: SERVICE_ACCOUNT['gcloud_project'],
9 | # google_client_email: SERVICE_ACCOUNT['client_email'],
10 | # google_json_key_string: '{"private_key": "' + SERVICE_ACCOUNT['private_key'] + '",
11 | # "client_email": "' + SERVICE_ACCOUNT['client_email'] + '"}'
12 | # }
13 | # config.fog_directory = SERVICE_ACCOUNT['cloud_storage_bucket_name']
14 | # config.fog_public = false
15 | # config.fog_attributes = { 'Cache-Control' => 'max-age=31536000' } # one year
16 | # end
17 | CarrierWave.configure do |config|
18 | config.storage = :file
19 | config.root = Rails.root.join('tmp')
20 | config.cache_dir = 'carrierwave-cache'
21 | end
22 |
23 | elsif Rails.env.test?
24 | CarrierWave.configure do |config|
25 | config.storage = :file
26 | config.enable_processing = false
27 | config.root = Rails.root.join('tmp')
28 | config.cache_dir = 'carrierwave-cache'
29 | end
30 |
31 | elsif Rails.env.production?
32 | CarrierWave.configure do |config|
33 | config.fog_provider = 'fog/google'
34 | config.fog_credentials = {
35 | provider: 'Google',
36 | google_project: ENV['GCLOUD_PROJECT'],
37 | google_client_email: ENV['SERVICE_ACCOUNT_CLIENT_EMAIL'],
38 | google_json_key_string: '{"private_key": "' + ENV['SERVICE_ACCOUNT_PRIVATE_KEY'] + '",
39 | "client_email": "' + ENV['SERVICE_ACCOUNT_CLIENT_EMAIL'] + '"}'
40 | }
41 | config.fog_directory = ENV['CLOUD_STORAGE_BUCKET_NAME']
42 | config.asset_host = "https://storage.googleapis.com/#{ENV['CLOUD_STORAGE_BUCKET_NAME']}"
43 | config.fog_public = true
44 | config.fog_attributes = { cache_control: 'max-age=31536000' } # one year
45 | config.root = Rails.root.join('tmp')
46 | config.cache_dir = 'carrierwave-cache'
47 | end
48 | end
49 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/content_security_policy.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Define an application-wide content security policy
4 | # For further information see the following documentation
5 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
6 |
7 | # Rails.application.config.content_security_policy do |policy|
8 | # policy.default_src :self, :https
9 | # policy.font_src :self, :https, :data
10 | # policy.img_src :self, :https, :data
11 | # policy.object_src :none
12 | # policy.script_src :self, :https
13 | # policy.style_src :self, :https
14 | # # If you are using webpack-dev-server then specify webpack-dev-server host
15 | # policy.connect_src :self, :https, "http://localhost:3035", "ws://localhost:3035" if Rails.env.development?
16 |
17 | # # Specify URI for violation reports
18 | # # policy.report_uri "/csp-violation-report-endpoint"
19 | # end
20 |
21 | # If you are using UJS then enable automatic nonce generation
22 | # Rails.application.config.content_security_policy_nonce_generator = -> request { SecureRandom.base64(16) }
23 |
24 | # Set the nonce only to specific directives
25 | # Rails.application.config.content_security_policy_nonce_directives = %w(script-src)
26 |
27 | # Report CSP violations to a specified URI
28 | # For further information see the following documentation:
29 | # https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy-Report-Only
30 | # Rails.application.config.content_security_policy_report_only = true
31 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/cookies_serializer.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Specify a serializer for the signed and encrypted cookie jars.
4 | # Valid options are :json, :marshal, and :hybrid.
5 | Rails.application.config.action_dispatch.cookies_serializer = :json
6 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/filter_parameter_logging.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Configure sensitive parameters which will be filtered from the log file.
4 | Rails.application.config.filter_parameters += [
5 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn
6 | ]
7 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/inflections.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new inflection rules using the following format. Inflections
4 | # are locale specific, and you may define rules for as many different
5 | # locales as you wish. All of these examples are active by default:
6 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
7 | # inflect.plural /^(ox)$/i, '\1en'
8 | # inflect.singular /^(ox)en/i, '\1'
9 | # inflect.irregular 'person', 'people'
10 | # inflect.uncountable %w( fish sheep )
11 | # end
12 |
13 | # These inflection rules are supported but not enabled by default:
14 | # ActiveSupport::Inflector.inflections(:en) do |inflect|
15 | # inflect.acronym 'RESTful'
16 | # end
17 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/mime_types.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Add new mime types for use in respond_to blocks:
4 | # Mime::Type.register "text/richtext", :rtf
5 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/new_framework_defaults_5_1.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 | #
3 | # This file contains migration options to ease your Rails 5.1 upgrade.
4 | #
5 | # Once upgraded flip defaults one by one to migrate to the new default.
6 | #
7 | # Read the Guide for Upgrading Ruby on Rails for more info on each option.
8 |
9 | # Make `form_with` generate non-remote forms.
10 | Rails.application.config.action_view.form_with_generates_remote_forms = false
11 |
12 | # Unknown asset fallback will return the path passed in when the given
13 | # asset is not present in the asset pipeline.
14 | # Rails.application.config.assets.unknown_asset_fallback = false
15 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/permissions_policy.rb:
--------------------------------------------------------------------------------
1 | # Define an application-wide HTTP permissions policy. For further
2 | # information see https://developers.google.com/web/updates/2018/06/feature-policy
3 | #
4 | # Rails.application.config.permissions_policy do |f|
5 | # f.camera :none
6 | # f.gyroscope :none
7 | # f.microphone :none
8 | # f.usb :none
9 | # f.fullscreen :self
10 | # f.payment :self, "https://secure.example.com"
11 | # end
12 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/rack_timeout.rb:
--------------------------------------------------------------------------------
1 | # Timeout
2 | # Is the time taken from when a request first enters rack to when its response is sent back. When
3 | # the application takes longer than the time specified below to process a request, the request's
4 | # status is logged as timed_out and Rack::Timeout::RequestTimeoutException or
5 | # Rack::Timeout::RequestTimeoutError is raised on the application thread.
6 | Rack::Timeout::Logger.disable
7 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/session_store.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | Rails.application.config.session_store :cookie_store, key: '_datastore_example_rails_app_session'
4 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/initializers/wrap_parameters.rb:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # This file contains settings for ActionController::ParamsWrapper which
4 | # is enabled by default.
5 |
6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array.
7 | ActiveSupport.on_load(:action_controller) do
8 | wrap_parameters format: [:json]
9 | end
10 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/locales/en.yml:
--------------------------------------------------------------------------------
1 | # Files in the config/locales directory are used for internationalization
2 | # and are automatically loaded by Rails. If you want to use locales other
3 | # than English, add the necessary files in this directory.
4 | #
5 | # To use the locales, use `I18n.t`:
6 | #
7 | # I18n.t 'hello'
8 | #
9 | # In views, this is aliased to just `t`:
10 | #
11 | # <%= t('hello') %>
12 | #
13 | # To use a different locale, set it with `I18n.locale`:
14 | #
15 | # I18n.locale = :es
16 | #
17 | # This would use the information in config/locales/es.yml.
18 | #
19 | # The following keys must be escaped otherwise they will not be retrieved by
20 | # the default I18n backend:
21 | #
22 | # true, false, on, off, yes, no
23 | #
24 | # Instead, surround them with single quotes.
25 | #
26 | # en:
27 | # 'true': 'foo'
28 | #
29 | # To learn more, please read the Rails Internationalization guide
30 | # available at https://guides.rubyonrails.org/i18n.html.
31 |
32 | en:
33 | hello: "Hello world"
34 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/puma.rb:
--------------------------------------------------------------------------------
1 | # Puma can serve each request in a thread from an internal thread pool.
2 | # The `threads` method setting takes two numbers a minimum and maximum.
3 | # On MRI, there is a Global Interpreter Lock (GIL) that ensures only one
4 | # thread can be run at any time. IO operations such as database calls,
5 | # interacting with the file system, or making external http calls will not
6 | # lock the GIL. Most Rails applications heavily use IO, so adding additional
7 | # threads will allow Puma to process multiple threads.
8 | #
9 | threads_count = ENV.fetch('RAILS_MAX_THREADS', 5)
10 | threads threads_count, threads_count
11 |
12 | rackup DefaultRackup
13 | port ENV.fetch('PORT', 3000)
14 | environment ENV.fetch('RAILS_ENV', 'development')
15 |
16 | if ENV.fetch('WEB_CONCURRENCY', 0).to_i > 1
17 | # Puma can fork multiple OS processes within each instance to allow Rails
18 | # to support multiple concurrent requests (Cluster Mode). In Puma terminology
19 | # these are referred to as worker processes. Worker processes are isolated from
20 | # one another at the OS level, therefore not needing to be thread safe. Rule of
21 | # thumb is to set the number of workers equal to the number of CPU cores.
22 | #
23 | workers ENV.fetch('WEB_CONCURRENCY').to_i
24 |
25 | # Use the `preload_app!` method when specifying a `workers` number.
26 | # This directive tells Puma to first boot the application and load code
27 | # before forking the application. This takes advantage of Copy On Write
28 | # process behavior so workers use less memory. When you use preload_app,
29 | # your new code goes all in the master process, and is then copied to
30 | # the workers (meaning preload_app is only compatible with cluster mode).
31 | # If you use this option you need to make sure to reconnect any threads in
32 | # the `on_worker_boot` block.
33 | #
34 | preload_app!
35 |
36 | # Code to run in the master immediately before the master starts workers. As the master process
37 | # boots the rails application (and executes the initializers) before forking workers, it's
38 | # recommended to close any connections that were automatically established in the master to
39 | # prevent connection leakage.
40 | #
41 | before_fork do
42 | CloudDatastore.reset_dataset
43 | end
44 |
45 | # The code in the `on_worker_boot` will be called if you are using
46 | # clustered mode by specifying a number of `workers`. After each worker
47 | # process is booted this block will be run, if you are using `preload_app!`
48 | # option you will want to use this block to reconnect to any threads
49 | # or connections that may have been created at application boot, Ruby
50 | # cannot share connections between processes. Code in the block is run before
51 | # it starts serving requests. This is called every time a worker is to be started.
52 | #
53 | on_worker_boot do
54 | CloudDatastore.dataset
55 | end
56 | end
57 |
58 | # Code to run before doing a restart. This code should close log files, database connections, etc
59 | # so that their file descriptors don't leak into the restarted process.
60 | #
61 | on_restart do
62 | CloudDatastore.reset_dataset
63 | end
64 |
65 | # Allow puma to be restarted by `rails restart` command.
66 | plugin :tmp_restart
67 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/routes.rb:
--------------------------------------------------------------------------------
1 | Rails.application.routes.draw do
2 | get 'home/index'
3 | resources :users
4 |
5 | # You can have the root of your site routed with "root"
6 | root 'home#index'
7 | end
8 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/config/secrets.yml:
--------------------------------------------------------------------------------
1 | # Be sure to restart your server when you modify this file.
2 |
3 | # Your secret key is used for verifying the integrity of signed cookies.
4 | # If you change this key, all old signed cookies will become invalid!
5 |
6 | # Make sure the secret is at least 30 characters and all random,
7 | # no regular words or you'll be exposed to dictionary attacks.
8 | # You can use `rails secret` to generate a secure secret key.
9 |
10 | # Make sure the secrets in this file are kept private
11 | # if you're sharing your code publicly.
12 |
13 | # Shared secrets are available across all environments.
14 |
15 | # shared:
16 | # api_key: a1B2c3D4e5F6
17 |
18 | # Environmental secrets are only available for that specific environment.
19 |
20 | development:
21 | secret_key_base: 321cd3431e9d85306ff737cfe960d0b806b2abd9e14e532393ac59ed1ac70f82791e0fcb0fccb1faec75db5e6a95c0050d6e4093576278a6560ea41602a21f40
22 |
23 | test:
24 | secret_key_base: a8d233a823b5120651c07a19d6c51d13e4b9f248ad38b949af9524b06bde12c9e841bafedb643deb46f3e4e81508572215d2acfe00543824d59197c2d5804dd8
25 |
26 | # Do not keep production secrets in the unencrypted secrets file.
27 | # Instead, either read values from the environment.
28 | # Or, use `bin/rails secrets:setup` to configure encrypted secrets
29 | # and move the `production:` environment over there.
30 |
31 | production:
32 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %>
33 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/lib/assets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/lib/assets/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/lib/tasks/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/lib/tasks/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/log/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/log/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/public/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The page you were looking for doesn't exist (404)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The page you were looking for doesn't exist.
62 |
You may have mistyped the address or the page may have moved.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/public/422.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | The change you wanted was rejected (422)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
The change you wanted was rejected.
62 |
Maybe you tried to change something you didn't have access to.
63 |
64 |
If you are the application owner check the logs for more information.
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/public/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | We're sorry, but something went wrong (500)
5 |
6 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
We're sorry, but something went wrong.
62 |
63 |
If you are the application owner check the logs for more information.
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/public/favicon.ico
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/public/robots.txt:
--------------------------------------------------------------------------------
1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2 | #
3 | # To ban all spiders from the entire site uncomment the next two lines:
4 | # User-agent: *
5 | # Disallow: /
6 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/start-local-datastore.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | cloud_datastore_emulator start --port=8180 tmp/local_datastore
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/controllers/users_controller_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UsersControllerTest < ActionDispatch::IntegrationTest
4 | def setup
5 | super
6 | @user = create(:user, parent_key_id: 12345)
7 | end
8 |
9 | test 'should get index' do
10 | get users_url
11 | assert_response :success
12 | end
13 |
14 | test 'should get new' do
15 | get new_user_url
16 | assert_response :success
17 | end
18 |
19 | test 'should create user' do
20 | assert_difference('User.count_test_entities') do
21 | post users_url, params: { user: { name: 'User 2', email: 'user_2@test.com' } }
22 | end
23 | assert_redirected_to user_url(@user.id + 1)
24 | end
25 |
26 | test 'should show user' do
27 | get user_url(@user)
28 | assert_response :success
29 | end
30 |
31 | test 'should get edit' do
32 | get edit_user_url(@user)
33 | assert_response :success
34 | end
35 |
36 | test 'should update user' do
37 | patch user_url(@user), params: { user: { name: 'Updated User' } }
38 | assert_redirected_to user_url(@user)
39 | end
40 |
41 | test 'should destroy user' do
42 | assert_difference('User.count_test_entities', -1) do
43 | delete user_url(@user)
44 | end
45 | assert_redirected_to users_url
46 | end
47 | end
48 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/entity_class_method_extensions.rb:
--------------------------------------------------------------------------------
1 | ##
2 | # Additional methods added for testing only.
3 | #
4 | module EntityClassMethodExtensions
5 | def all_test_entities
6 | query = CloudDatastore.dataset.query(name)
7 | CloudDatastore.dataset.run(query)
8 | end
9 |
10 | def count_test_entities
11 | all_test_entities.length
12 | end
13 | end
14 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/factories/user_factories.rb:
--------------------------------------------------------------------------------
1 | FactoryBot.define do
2 | factory :user do
3 | name { 'A Test User' }
4 | email { Faker::Internet.email }
5 | role { 1 }
6 | end
7 | end
8 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/helpers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/test/helpers/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/integration/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/test/integration/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/mailers/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/test/mailers/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/models/user_test.rb:
--------------------------------------------------------------------------------
1 | require 'test_helper'
2 |
3 | class UserTest < ActiveSupport::TestCase
4 | test 'user attributes must not be empty' do
5 | attr = attributes_for(:user).except(:email, :enabled, :name, :role)
6 | user = User.new(attr)
7 | assert user.invalid?
8 | assert user.errors[:name].any?
9 | assert user.errors[:email].any?
10 | assert_equal 2, user.errors.messages.size
11 | assert user.enabled
12 | assert_equal 1, user.role
13 | end
14 |
15 | test 'user values should be formatted correctly' do
16 | user = User.new(attributes_for(:user))
17 | assert user.valid?, user.errors.messages
18 | user.role = 1.to_s
19 | assert user.valid?
20 | assert_equal 1, user.role
21 | assert user.role.is_a?(Integer)
22 | end
23 |
24 | test 'user entity properties include' do
25 | assert User.method_defined? :entity_properties
26 | user = User.new
27 | assert user.entity_properties.include? 'email'
28 | assert user.entity_properties.include? 'enabled'
29 | assert user.entity_properties.include? 'name'
30 | assert user.entity_properties.include? 'role'
31 | end
32 | end
33 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | ENV['RAILS_ENV'] ||= 'test'
2 | require File.expand_path('../config/environment', __dir__)
3 | require 'entity_class_method_extensions'
4 | require 'rails/test_help'
5 |
6 | MOCK_ACCOUNT_ID = 1010101010101010
7 |
8 | class MockModel
9 | include ActiveModel::Datastore
10 | attr_accessor :name, :role
11 | validates :name, presence: true
12 | enable_change_tracking :name, :role
13 |
14 | def entity_properties
15 | %w[name role]
16 | end
17 | end
18 |
19 | class MockModelParent
20 | include ActiveModel::Datastore::NestedAttr
21 | attr_accessor :name
22 | attr_accessor :mock_models
23 | end
24 |
25 | # Make the methods within EntityTestExtensions available as class methods.
26 | MockModel.send :extend, EntityClassMethodExtensions
27 | MockModelParent.send :extend, EntityClassMethodExtensions
28 | User.send :extend, EntityClassMethodExtensions
29 |
30 | class ActiveSupport::TestCase
31 | include FactoryBot::Syntax::Methods
32 |
33 | def setup
34 | if `lsof -t -i TCP:8181`.to_i.zero?
35 | puts 'Starting the cloud datastore emulator in test mode.'
36 | data_dir = Rails.root.join('tmp', 'test_datastore')
37 | spawn "cloud_datastore_emulator start --port=8181 --testing #{data_dir} > /dev/null 2>&1"
38 | loop do
39 | Net::HTTP.get('localhost', '/', '8181').include? 'Ok'
40 | break
41 | rescue Errno::ECONNREFUSED
42 | sleep 0.2
43 | end
44 | end
45 | CloudDatastore.dataset
46 | end
47 |
48 | def teardown
49 | delete_all_test_entities!
50 | end
51 |
52 | def delete_all_test_entities!
53 | entity_kinds = %w[MockModelParent MockModel User]
54 | entity_kinds.each do |kind|
55 | query = CloudDatastore.dataset.query(kind)
56 | loop do
57 | entities = CloudDatastore.dataset.run(query)
58 | break if entities.empty?
59 |
60 | CloudDatastore.dataset.delete(*entities)
61 | end
62 | end
63 | end
64 | end
65 |
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/tmp/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/vendor/assets/javascripts/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/vendor/assets/javascripts/.keep
--------------------------------------------------------------------------------
/test/support/datastore_example_rails_app/vendor/assets/stylesheets/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/test/support/datastore_example_rails_app/vendor/assets/stylesheets/.keep
--------------------------------------------------------------------------------
/test/test_helper.rb:
--------------------------------------------------------------------------------
1 | require 'bundler/setup'
2 | require 'active_support'
3 | require 'active_support/testing/autorun'
4 | require 'entity_class_method_extensions'
5 | require 'factory_bot'
6 | require 'faker'
7 |
8 | require 'google/cloud/datastore'
9 | require 'active_model'
10 | require 'carrierwave'
11 | require 'active_model/datastore/carrier_wave_uploader'
12 | require 'active_model/datastore/connection'
13 | require 'active_model/datastore/errors'
14 | require 'active_model/datastore/excluded_indexes'
15 | require 'active_model/datastore/nested_attr'
16 | require 'active_model/datastore/property_values'
17 | require 'active_model/datastore/track_changes'
18 | require 'active_model/datastore'
19 | require 'action_controller/metal/strong_parameters'
20 |
21 | FactoryBot.find_definitions
22 |
23 | MOCK_PARENT_ID = 1010101010101010
24 |
25 | class MockModel
26 | include ActiveModel::Datastore
27 | attr_accessor :name, :role, :image, :images
28 | validates :name, presence: true
29 | enable_change_tracking :name, :role
30 |
31 | def entity_properties
32 | %w[name role image images]
33 | end
34 | end
35 |
36 | class MockModelParent
37 | include ActiveModel::Datastore::NestedAttr
38 | attr_accessor :name
39 | attr_accessor :mock_models
40 | end
41 |
42 | # Make the methods within EntityTestExtensions available as class methods.
43 | MockModel.send :extend, EntityClassMethodExtensions
44 | MockModelParent.send :extend, EntityClassMethodExtensions
45 |
46 | class ActiveSupport::TestCase
47 | include FactoryBot::Syntax::Methods
48 |
49 | def setup
50 | if `lsof -t -i TCP:8181`.to_i.zero?
51 | puts 'Starting the cloud datastore emulator in test mode.'
52 | data_dir = File.join(File.expand_path('..', __dir__), 'tmp', 'test_datastore')
53 | spawn "cloud_datastore_emulator start --port=8181 --testing #{data_dir} > /dev/null 2>&1"
54 | loop do
55 | Net::HTTP.get('localhost', '/', '8181').include? 'Ok'
56 | break
57 | rescue Errno::ECONNREFUSED
58 | sleep 0.2
59 | end
60 | end
61 | if defined?(Rails) != 'constant'
62 | ENV['DATASTORE_EMULATOR_HOST'] = 'localhost:8181'
63 | ENV['GCLOUD_PROJECT'] = 'test-datastore'
64 | end
65 | CloudDatastore.dataset
66 | carrierwave_init
67 | end
68 |
69 | def carrierwave_init
70 | CarrierWave.configure do |config|
71 | config.reset_config
72 | config.storage = :file
73 | config.enable_processing = false
74 | config.root = File.join(Dir.pwd, 'tmp', 'carrierwave-tests')
75 | config.cache_dir = 'carrierwave-cache'
76 | end
77 | end
78 |
79 | def teardown
80 | delete_all_test_entities!
81 | FileUtils.rm_rf(CarrierWave::Uploader::Base.root)
82 | CarrierWave.configure(&:reset_config)
83 | MockModel.clear_index_exclusions!
84 | end
85 |
86 | def delete_all_test_entities!
87 | entity_kinds = %w[MockModelParent MockModel]
88 | entity_kinds.each do |kind|
89 | query = CloudDatastore.dataset.query(kind)
90 | loop do
91 | entities = CloudDatastore.dataset.run(query)
92 | break if entities.empty?
93 |
94 | CloudDatastore.dataset.delete(*entities)
95 | end
96 | end
97 | end
98 | end
99 |
--------------------------------------------------------------------------------
/tmp/.keep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Agrimatics/activemodel-datastore/107c0d32c1c61c58a5595e0313e9a0b5f9b1c509/tmp/.keep
--------------------------------------------------------------------------------