anonymous_active_record 1.0.4 → 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4505db5d93ffbdacfffc19d27c14e1d98ca11a889660ff9587bea7590a99566
4
- data.tar.gz: 9afc59dff158716e7e1b303ab5e59978813857869170c14f672106e81b291424
3
+ metadata.gz: 4ca6f2f21fd0dcb0c001dec3dc5e95d3964677c7aff7f3541a99b4c606072b59
4
+ data.tar.gz: a6fb0dde8a98a253a57b9b4f24776fe935d4ad54ace7d553f6bdab272c207020
5
5
  SHA512:
6
- metadata.gz: 16dfd759bc5c9ec9ca06c116585cf9a20c62d2991cf627315a369a4e6fabce2c671c8b360dd77ef38de80efa4581e46652fbd2535d17e2d2e67274a897e5209f
7
- data.tar.gz: 0aaa90c93370fa1fab73b6c894f2eb0f00106706be3e772b490b8c2efc51533f132795b7b465890c7886b173a11db66d282e546f4ea02fe21aa7a5a31a751358
6
+ metadata.gz: cb02baf587332716753c3fa25a32d79c518ad2f7ed5777b2649b898c5882850f4bff0c3d5a28896927d5f57da0811df5e950b1a05f23407499408a77aefb6d1a
7
+ data.tar.gz: 60b28ea2ac93dc90df1e850902de70d7055c333749a3a0f55ece2ef557fbd74466de36febcbc193676de6039f809f7628e66c3daa7ab25177d83191f7e10460b
data/CODE_OF_CONDUCT.md CHANGED
@@ -1,74 +1,133 @@
1
+
1
2
  # Contributor Covenant Code of Conduct
2
3
 
3
4
  ## Our Pledge
4
5
 
5
- In the interest of fostering an open and welcoming environment, we as
6
- contributors and maintainers pledge to making participation in our project and
7
- our community a harassment-free experience for everyone, regardless of age, body
8
- size, disability, ethnicity, gender identity and expression, level of experience,
9
- nationality, personal appearance, race, religion, or sexual identity and
10
- orientation.
6
+ We as members, contributors, and leaders pledge to make participation in our
7
+ community a harassment-free experience for everyone, regardless of age, body
8
+ size, visible or invisible disability, ethnicity, sex characteristics, gender
9
+ identity and expression, level of experience, education, socio-economic status,
10
+ nationality, personal appearance, race, religion, or sexual identity
11
+ and orientation.
12
+
13
+ We pledge to act and interact in ways that contribute to an open, welcoming,
14
+ diverse, inclusive, and healthy community.
11
15
 
12
16
  ## Our Standards
13
17
 
14
- Examples of behavior that contributes to creating a positive environment
15
- include:
18
+ Examples of behavior that contributes to a positive environment for our
19
+ community include:
16
20
 
17
- * Using welcoming and inclusive language
18
- * Being respectful of differing viewpoints and experiences
19
- * Gracefully accepting constructive criticism
20
- * Focusing on what is best for the community
21
- * Showing empathy towards other community members
21
+ * Demonstrating empathy and kindness toward other people
22
+ * Being respectful of differing opinions, viewpoints, and experiences
23
+ * Giving and gracefully accepting constructive feedback
24
+ * Accepting responsibility and apologizing to those affected by our mistakes,
25
+ and learning from the experience
26
+ * Focusing on what is best not just for us as individuals, but for the
27
+ overall community
22
28
 
23
- Examples of unacceptable behavior by participants include:
29
+ Examples of unacceptable behavior include:
24
30
 
25
- * The use of sexualized language or imagery and unwelcome sexual attention or
26
- advances
27
- * Trolling, insulting/derogatory comments, and personal or political attacks
31
+ * The use of sexualized language or imagery, and sexual attention or
32
+ advances of any kind
33
+ * Trolling, insulting or derogatory comments, and personal or political attacks
28
34
  * Public or private harassment
29
- * Publishing others' private information, such as a physical or electronic
30
- address, without explicit permission
35
+ * Publishing others' private information, such as a physical or email
36
+ address, without their explicit permission
31
37
  * Other conduct which could reasonably be considered inappropriate in a
32
38
  professional setting
33
39
 
34
- ## Our Responsibilities
40
+ ## Enforcement Responsibilities
35
41
 
36
- Project maintainers are responsible for clarifying the standards of acceptable
37
- behavior and are expected to take appropriate and fair corrective action in
38
- response to any instances of unacceptable behavior.
42
+ Community leaders are responsible for clarifying and enforcing our standards of
43
+ acceptable behavior and will take appropriate and fair corrective action in
44
+ response to any behavior that they deem inappropriate, threatening, offensive,
45
+ or harmful.
39
46
 
40
- Project maintainers have the right and responsibility to remove, edit, or
41
- reject comments, commits, code, wiki edits, issues, and other contributions
42
- that are not aligned to this Code of Conduct, or to ban temporarily or
43
- permanently any contributor for other behaviors that they deem inappropriate,
44
- threatening, offensive, or harmful.
47
+ Community leaders have the right and responsibility to remove, edit, or reject
48
+ comments, commits, code, wiki edits, issues, and other contributions that are
49
+ not aligned to this Code of Conduct, and will communicate reasons for moderation
50
+ decisions when appropriate.
45
51
 
46
52
  ## Scope
47
53
 
48
- This Code of Conduct applies both within project spaces and in public spaces
49
- when an individual is representing the project or its community. Examples of
50
- representing a project or community include using an official project e-mail
51
- address, posting via an official social media account, or acting as an appointed
52
- representative at an online or offline event. Representation of a project may be
53
- further defined and clarified by project maintainers.
54
+ This Code of Conduct applies within all community spaces, and also applies when
55
+ an individual is officially representing the community in public spaces.
56
+ Examples of representing our community include using an official e-mail address,
57
+ posting via an official social media account, or acting as an appointed
58
+ representative at an online or offline event.
54
59
 
55
60
  ## Enforcement
56
61
 
57
62
  Instances of abusive, harassing, or otherwise unacceptable behavior may be
58
- reported by contacting the project team at peter.boling@gmail.com. All
59
- complaints will be reviewed and investigated and will result in a response that
60
- is deemed necessary and appropriate to the circumstances. The project team is
61
- obligated to maintain confidentiality with regard to the reporter of an incident.
62
- Further details of specific enforcement policies may be posted separately.
63
+ reported to the community leaders responsible for enforcement at
64
+ [INSERT CONTACT METHOD].
65
+ All complaints will be reviewed and investigated promptly and fairly.
66
+
67
+ All community leaders are obligated to respect the privacy and security of the
68
+ reporter of any incident.
69
+
70
+ ## Enforcement Guidelines
71
+
72
+ Community leaders will follow these Community Impact Guidelines in determining
73
+ the consequences for any action they deem in violation of this Code of Conduct:
74
+
75
+ ### 1. Correction
76
+
77
+ **Community Impact**: Use of inappropriate language or other behavior deemed
78
+ unprofessional or unwelcome in the community.
79
+
80
+ **Consequence**: A private, written warning from community leaders, providing
81
+ clarity around the nature of the violation and an explanation of why the
82
+ behavior was inappropriate. A public apology may be requested.
83
+
84
+ ### 2. Warning
63
85
 
64
- Project maintainers who do not follow or enforce the Code of Conduct in good
65
- faith may face temporary or permanent repercussions as determined by other
66
- members of the project's leadership.
86
+ **Community Impact**: A violation through a single incident or series
87
+ of actions.
88
+
89
+ **Consequence**: A warning with consequences for continued behavior. No
90
+ interaction with the people involved, including unsolicited interaction with
91
+ those enforcing the Code of Conduct, for a specified period of time. This
92
+ includes avoiding interactions in community spaces as well as external channels
93
+ like social media. Violating these terms may lead to a temporary or
94
+ permanent ban.
95
+
96
+ ### 3. Temporary Ban
97
+
98
+ **Community Impact**: A serious violation of community standards, including
99
+ sustained inappropriate behavior.
100
+
101
+ **Consequence**: A temporary ban from any sort of interaction or public
102
+ communication with the community for a specified period of time. No public or
103
+ private interaction with the people involved, including unsolicited interaction
104
+ with those enforcing the Code of Conduct, is allowed during this period.
105
+ Violating these terms may lead to a permanent ban.
106
+
107
+ ### 4. Permanent Ban
108
+
109
+ **Community Impact**: Demonstrating a pattern of violation of community
110
+ standards, including sustained inappropriate behavior, harassment of an
111
+ individual, or aggression toward or disparagement of classes of individuals.
112
+
113
+ **Consequence**: A permanent ban from any sort of public interaction within
114
+ the community.
67
115
 
68
116
  ## Attribution
69
117
 
70
- This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71
- available at [http://contributor-covenant.org/version/1/4][version]
118
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
119
+ version 2.0, available at
120
+ [https://www.contributor-covenant.org/version/2/0/code_of_conduct.html][v2.0].
121
+
122
+ Community Impact Guidelines were inspired by
123
+ [Mozilla's code of conduct enforcement ladder][Mozilla CoC].
124
+
125
+ For answers to common questions about this code of conduct, see the FAQ at
126
+ [https://www.contributor-covenant.org/faq][FAQ]. Translations are available
127
+ at [https://www.contributor-covenant.org/translations][translations].
72
128
 
73
- [homepage]: http://contributor-covenant.org
74
- [version]: http://contributor-covenant.org/version/1/4/
129
+ [homepage]: https://www.contributor-covenant.org
130
+ [v2.0]: https://www.contributor-covenant.org/version/2/0/code_of_conduct.html
131
+ [Mozilla CoC]: https://github.com/mozilla/diversity
132
+ [FAQ]: https://www.contributor-covenant.org/faq
133
+ [translations]: https://www.contributor-covenant.org/translations
data/README.md CHANGED
@@ -11,14 +11,14 @@ Warning: Use of this gem is a **security risk**, due to the use of Ruby's `eval`
11
11
  | download rank | [![Downloads Today](https://img.shields.io/gem/rd/anonymous_active_record.svg)](https://github.com/pboling/anonymous_active_record) |
12
12
  | version | [![Version](https://img.shields.io/gem/v/anonymous_active_record.svg)](https://rubygems.org/gems/anonymous_active_record) |
13
13
  | dependencies | [![Depfu](https://badges.depfu.com/badges/272ce0df3bc6df5cbea9354e2c3b65af/count.svg)](https://depfu.com/github/pboling/anonymous_active_record?project_id=5614) |
14
- | continuous integration | [![Build Status](https://travis-ci.org/pboling/anonymous_active_record.svg?branch=master)](https://travis-ci.org/pboling/anonymous_active_record) |
14
+ | continuous integration | [![Build Status](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fpboling%2Fanonymous_active_record%2Fbadge&style=flat)](https://actions-badge.atrox.dev/pboling/anonymous_active_record/goto) |
15
15
  | test coverage | [![Test Coverage](https://api.codeclimate.com/v1/badges/fe504d4ab2fb77cecf7d/test_coverage)](https://codeclimate.com/github/pboling/anonymous_active_record/test_coverage) |
16
16
  | maintainability | [![Maintainability](https://api.codeclimate.com/v1/badges/fe504d4ab2fb77cecf7d/maintainability)](https://codeclimate.com/github/pboling/anonymous_active_record/maintainability) |
17
17
  | code triage | [![Open Source Helpers](https://www.codetriage.com/pboling/anonymous_active_record/badges/users.svg)](https://www.codetriage.com/pboling/anonymous_active_record) |
18
18
  | FOSSA Licenses | [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fpboling%2Fanonymous_active_record.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2Fpboling%2Fanonymous_active_record?ref=badge_shield) |
19
19
  | homepage | [on Github.com][homepage], [on Railsbling.com][blogpage] |
20
20
  | documentation | [on RDoc.info][documentation] |
21
- | Spread ~♡ⓛⓞⓥⓔ♡~ | [🌍 🌎 🌏](https://about.me/peter.boling), [🍚](https://www.crowdrise.com/helprefugeeswithhopefortomorrowliberia/fundraiser/peterboling), [➕](https://plus.google.com/+PeterBoling/posts), [👼](https://angel.co/peter-boling), [🐛](https://www.topcoder.com/members/pboling/), [:shipit:](http://coderwall.com/pboling), [![Tweet Peter](https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow)](http://twitter.com/galtzo) |
21
+ | Spread ~♡ⓛⓞⓥⓔ♡~ | [🌏](https://about.me/peter.boling), [👼](https://angel.co/peter-boling), [:shipit:](http://coderwall.com/pboling), [![Tweet Peter](https://img.shields.io/twitter/follow/galtzo.svg?style=social&label=Follow)](http://twitter.com/galtzo), [🌹](https://nationalprogressiveparty.org) |
22
22
 
23
23
  ## Installation
24
24
 
@@ -44,8 +44,8 @@ module AnonymousActiveRecord
44
44
  }.freeze
45
45
 
46
46
  # Defines a pseudo anonymous class in a particular namespace of your choosing.
47
- def generate(table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, connection_params: DEFAULT_CONNECTION_PARAMS, &block)
48
- gen = AnonymousActiveRecord::Generator.new(table_name, klass_namespaces, klass_basename)
47
+ def generate(table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, parent_klass: "ActiveRecord::Base", connection_params: DEFAULT_CONNECTION_PARAMS, &block)
48
+ gen = AnonymousActiveRecord::Generator.new(table_name, klass_namespaces, klass_basename, parent_klass)
49
49
  klass = gen.generate(&block)
50
50
  connection_params = YAML.load_file(connection_params) if connection_params.is_a?(String)
51
51
  klass.establish_connection(connection_params.dup)
@@ -70,7 +70,7 @@ module AnonymousActiveRecord
70
70
  if idx_options.length == 1
71
71
  t.index idx_options[0]
72
72
  else
73
- t.index idx_options[0], idx_options[-1]
73
+ t.index idx_options[0], **idx_options[-1]
74
74
  end
75
75
  else
76
76
  t.index idx_options
@@ -82,21 +82,21 @@ module AnonymousActiveRecord
82
82
  end
83
83
 
84
84
  # Initializes instances of a pseudo anonymous class in a particular namespace of your choosing.
85
- def factory(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, connection_params: DEFAULT_CONNECTION_PARAMS, &block)
86
- factory = _factory(source_data: source_data, table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, indexes: indexes, timestamps: timestamps, connection_params: connection_params, &block)
85
+ def factory(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, parent_klass: "ActiveRecord::Base", connection_params: DEFAULT_CONNECTION_PARAMS, &block)
86
+ factory = _factory(source_data: source_data, table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, indexes: indexes, timestamps: timestamps, parent_klass: parent_klass, connection_params: connection_params, &block)
87
87
  factory.run
88
88
  end
89
89
 
90
90
  # Initializes instances of a pseudo anonymous class in a particular namespace of your choosing.
91
- def factory!(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, connection_params: DEFAULT_CONNECTION_PARAMS, &block)
92
- factory = _factory(source_data: source_data, table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, indexes: indexes, timestamps: timestamps, connection_params: connection_params, &block)
91
+ def factory!(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, parent_klass: "ActiveRecord::Base", connection_params: DEFAULT_CONNECTION_PARAMS, &block)
92
+ factory = _factory(source_data: source_data, table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, indexes: indexes, timestamps: timestamps, parent_klass: parent_klass, connection_params: connection_params, &block)
93
93
  factory.run!
94
94
  end
95
95
 
96
96
  private
97
97
 
98
- def _factory(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, connection_params: DEFAULT_CONNECTION_PARAMS, &block)
99
- klass = generate(table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, timestamps: timestamps, indexes: indexes, connection_params: connection_params, &block)
98
+ def _factory(source_data: [], table_name: nil, klass_namespaces: [], klass_basename: nil, columns: [], indexes: [], timestamps: true, parent_klass: "ActiveRecord::Base", connection_params: DEFAULT_CONNECTION_PARAMS, &block)
99
+ klass = generate(table_name: table_name, klass_namespaces: klass_namespaces, klass_basename: klass_basename, columns: columns, timestamps: timestamps, parent_klass: parent_klass, indexes: indexes, connection_params: connection_params, &block)
100
100
  AnonymousActiveRecord::Factory.new(source_data, klass)
101
101
  end
102
102
 
@@ -16,14 +16,15 @@ module AnonymousActiveRecord
16
16
  class Generator
17
17
  DEFAULT_NAME = 'anons'
18
18
 
19
- attr_reader :klass_namespaces, :table_name, :klass_name
19
+ attr_reader :klass_namespaces, :table_name, :klass_name, :parent_klass
20
20
 
21
- def initialize(table_name, klass_namespaces = [], klass_basename = nil)
21
+ def initialize(table_name, klass_namespaces = [], klass_basename = nil, parent_klass = "ActiveRecord::Base")
22
22
  @klass_namespaces = Array(klass_namespaces).map(&:to_s).map { |x| x.gsub(/[^a-z0-9]+/i, '_') }
23
23
  klass_context = [(klass_basename || DEFAULT_NAME).gsub(/[^a-z0-9]+/i, '_').capitalize, SecureRandom.uuid.gsub(/[^a-z0-9]+/i, '_')].join('_')
24
24
  @table_name = (table_name || klass_context).downcase.to_s
25
25
  # String#capitalize is Ruby >= 2.4, https://stackoverflow.com/a/3725154/213191
26
26
  @klass_name = (klass_namespaces << klass_context).join('::')
27
+ @parent_klass = parent_klass
27
28
  end
28
29
 
29
30
  def generate(&block)
@@ -31,7 +32,7 @@ module AnonymousActiveRecord
31
32
  # Class.new(ActiveRecord::Base)
32
33
  # which was useful for specs, and other use cases. But it is now explicitly not supported, see
33
34
  # https://github.com/rails/rails/issues/8934
34
- eval "class ::#{klass_name} < ActiveRecord::Base; end"
35
+ eval "class ::#{klass_name} < #{parent_klass}; end"
35
36
  klass = Kernel.const_get(klass_name, true)
36
37
  klass.class_eval(&block) if block_given?
37
38
  klass.table_name = table_name
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module AnonymousActiveRecord
4
- VERSION = '1.0.4'
4
+ VERSION = '1.0.5'
5
5
  end
@@ -0,0 +1,977 @@
1
+ # frozen_string_literal: true
2
+
3
+ RSpec.describe AnonymousActiveRecord do
4
+ it 'has a version number' do
5
+ expect(AnonymousActiveRecord::VERSION).not_to be nil
6
+ end
7
+
8
+ describe '.generate' do
9
+ context 'minimal params' do
10
+ subject { described_class.generate }
11
+
12
+ it 'does not error' do
13
+ expect { subject }.not_to raise_error
14
+ end
15
+ context 'instance' do
16
+ subject { super().new }
17
+
18
+ it 'can be instantiated' do
19
+ expect(subject).to be_a(ActiveRecord::Base)
20
+ end
21
+ context 'timestamps' do
22
+ it 'has' do
23
+ expect(subject.created_at).to be_nil
24
+ expect(subject.updated_at).to be_nil
25
+ end
26
+ end
27
+
28
+ context 'saving' do
29
+ subject do
30
+ i = super()
31
+ i.save
32
+ i
33
+ end
34
+
35
+ it 'does not error' do
36
+ expect { subject }.not_to raise_error
37
+ end
38
+ it 'does not have name' do
39
+ expect(subject).not_to respond_to(:name)
40
+ end
41
+ it 'sets timestamps' do
42
+ expect(subject.created_at).not_to be_nil
43
+ expect(subject.updated_at).not_to be_nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ context 'all params' do
50
+ subject do
51
+ described_class.generate(
52
+ table_name: table_name,
53
+ klass_namespaces: klass_namespaces,
54
+ klass_basename: klass_basename,
55
+ columns: columns,
56
+ indexes: indexes,
57
+ timestamps: timestamps,
58
+ connection_params: connection_params
59
+ )
60
+ end
61
+
62
+ let!(:farm_animal) do
63
+ module Farm
64
+ module Animal
65
+ end
66
+ end
67
+ end
68
+ let(:table_name) { 'dogs' }
69
+ let(:klass_namespaces) { %w[Farm Animal] }
70
+ let(:klass_basename) { 'my' }
71
+ let(:columns) { ['name'] }
72
+ let(:indexes) { [{ columns: ['name'] }] }
73
+ let(:timestamps) { true }
74
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
75
+
76
+ it 'does not error' do
77
+ expect { subject }.not_to raise_error
78
+ end
79
+ context 'instance' do
80
+ subject { super().new }
81
+
82
+ it 'can be instantiated' do
83
+ expect(subject).to be_a(ActiveRecord::Base)
84
+ end
85
+ context 'name' do
86
+ it 'has' do
87
+ expect(subject.name).to be_nil
88
+ end
89
+ end
90
+
91
+ context 'timestamps' do
92
+ it 'has' do
93
+ expect(subject.created_at).to be_nil
94
+ expect(subject.updated_at).to be_nil
95
+ end
96
+ end
97
+
98
+ context 'saving' do
99
+ subject do
100
+ i = super()
101
+ i.name = 'Bobo'
102
+ i.save
103
+ i
104
+ end
105
+
106
+ it 'does not error' do
107
+ expect { subject }.not_to raise_error
108
+ end
109
+ it 'sets name' do
110
+ expect(subject.name).to eq('Bobo')
111
+ end
112
+ it 'sets timestamps' do
113
+ expect(subject.created_at).not_to be_nil
114
+ expect(subject.updated_at).not_to be_nil
115
+ end
116
+ end
117
+ end
118
+ end
119
+
120
+ context 'designating type' do
121
+ subject do
122
+ described_class.generate(
123
+ table_name: table_name,
124
+ klass_namespaces: klass_namespaces,
125
+ klass_basename: klass_basename,
126
+ columns: columns,
127
+ indexes: indexes,
128
+ timestamps: timestamps,
129
+ connection_params: connection_params
130
+ )
131
+ end
132
+
133
+ let!(:farm_animal) do
134
+ module Farm
135
+ module Animal
136
+ end
137
+ end
138
+ end
139
+ let(:table_name) { 'dogs' }
140
+ let(:klass_namespaces) { %w[Farm Animal] }
141
+ let(:klass_basename) { 'my' }
142
+ let(:columns) { [{ name: 'name', type: 'string' }, { name: 'baked_at', type: 'time' }] }
143
+ let(:indexes) { [] }
144
+ let(:timestamps) { true }
145
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
146
+
147
+ it 'does not error' do
148
+ expect { subject }.not_to raise_error
149
+ end
150
+ context 'instance' do
151
+ subject { super().new }
152
+
153
+ it 'can be instantiated' do
154
+ expect(subject).to be_a(ActiveRecord::Base)
155
+ end
156
+ context 'name' do
157
+ it 'has' do
158
+ expect(subject.name).to be_nil
159
+ end
160
+ end
161
+
162
+ context 'baked_at' do
163
+ it 'has' do
164
+ expect(subject.baked_at).to be_nil
165
+ end
166
+ end
167
+
168
+ context 'timestamps' do
169
+ it 'has' do
170
+ expect(subject.created_at).to be_nil
171
+ expect(subject.updated_at).to be_nil
172
+ end
173
+ end
174
+
175
+ context 'saving' do
176
+ subject do
177
+ i = super()
178
+ i.name = 'Bobo'
179
+ i.baked_at = Time.now
180
+ i.save
181
+ i
182
+ end
183
+
184
+ it 'does not error' do
185
+ expect { subject }.not_to raise_error
186
+ end
187
+ it 'sets name' do
188
+ expect(subject.name).to eq('Bobo')
189
+ end
190
+ it 'sets baked_at' do
191
+ expect([ActiveRecord::Type::Time::Value, Time]).to include(subject.baked_at.class)
192
+ end
193
+ it 'sets timestamps' do
194
+ expect(subject.created_at).not_to be_nil
195
+ expect(subject.updated_at).not_to be_nil
196
+ end
197
+ end
198
+ end
199
+ end
200
+
201
+ context 'designating default' do
202
+ subject do
203
+ described_class.generate(
204
+ table_name: table_name,
205
+ klass_namespaces: klass_namespaces,
206
+ klass_basename: klass_basename,
207
+ columns: columns,
208
+ indexes: indexes,
209
+ timestamps: timestamps,
210
+ connection_params: connection_params
211
+ )
212
+ end
213
+
214
+ let!(:farm_animal) do
215
+ module Farm
216
+ module Animal
217
+ end
218
+ end
219
+ end
220
+ let(:table_name) { 'dogs' }
221
+ let(:klass_namespaces) { %w[Farm Animal] }
222
+ let(:klass_basename) { 'my' }
223
+ let(:columns) { [{ name: 'name', type: 'string', default: 'Bird Man' }, { name: 'number', type: 'integer', default: 0 }] }
224
+ let(:indexes) { [] }
225
+ let(:timestamps) { true }
226
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
227
+
228
+ it 'does not error' do
229
+ expect { subject }.not_to raise_error
230
+ end
231
+ context 'instance' do
232
+ subject { super().new }
233
+
234
+ it 'can be instantiated' do
235
+ expect(subject).to be_a(ActiveRecord::Base)
236
+ end
237
+ context 'name' do
238
+ it 'has' do
239
+ expect(subject.name).to eq('Bird Man')
240
+ end
241
+ end
242
+
243
+ context 'number' do
244
+ it 'has' do
245
+ expect(subject.number).to eq(0)
246
+ end
247
+ end
248
+
249
+ context 'timestamps' do
250
+ it 'has' do
251
+ expect(subject.created_at).to be_nil
252
+ expect(subject.updated_at).to be_nil
253
+ end
254
+ end
255
+
256
+ context 'saving' do
257
+ subject do
258
+ i = super()
259
+ i.name = 'Bobo'
260
+ i.number += 111
261
+ i.save
262
+ i
263
+ end
264
+
265
+ it 'does not error' do
266
+ expect { subject }.not_to raise_error
267
+ end
268
+ it 'sets name' do
269
+ expect(subject.name).to eq('Bobo')
270
+ end
271
+ it 'sets number' do
272
+ expect(subject.number).to eq(111)
273
+ end
274
+ it 'sets timestamps' do
275
+ expect(subject.created_at).not_to be_nil
276
+ expect(subject.updated_at).not_to be_nil
277
+ end
278
+ end
279
+ end
280
+ end
281
+
282
+ context 'with unique index as options' do
283
+ subject do
284
+ described_class.generate(
285
+ table_name: table_name,
286
+ klass_namespaces: klass_namespaces,
287
+ klass_basename: klass_basename,
288
+ columns: columns,
289
+ indexes: indexes,
290
+ timestamps: timestamps,
291
+ connection_params: connection_params
292
+ )
293
+ end
294
+
295
+ let!(:farm_animal) do
296
+ module Farm
297
+ module Animal
298
+ end
299
+ end
300
+ end
301
+ let(:table_name) { 'dogs' }
302
+ let(:klass_namespaces) { %w[Farm Animal] }
303
+ let(:klass_basename) { 'my' }
304
+ let(:columns) { [{ name: 'name', type: 'string' }, { name: 'baked_at', type: 'time' }] }
305
+ let(:indexes) { [{ columns: ['name'], unique: true }, { columns: ['baked_at'] }] }
306
+ let(:timestamps) { true }
307
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
308
+
309
+ it 'does not error' do
310
+ expect { subject }.not_to raise_error
311
+ end
312
+ context 'instance' do
313
+ subject { super().new }
314
+
315
+ it 'can be instantiated' do
316
+ expect(subject).to be_a(ActiveRecord::Base)
317
+ end
318
+ context 'name' do
319
+ it 'has' do
320
+ expect(subject.name).to be_nil
321
+ end
322
+ end
323
+
324
+ context 'baked_at' do
325
+ it 'has' do
326
+ expect(subject.baked_at).to be_nil
327
+ end
328
+ end
329
+
330
+ context 'timestamps' do
331
+ it 'has' do
332
+ expect(subject.created_at).to be_nil
333
+ expect(subject.updated_at).to be_nil
334
+ end
335
+ end
336
+
337
+ context 'saving' do
338
+ subject do
339
+ i = super()
340
+ i.name = 'Bobo'
341
+ i.baked_at = Time.now
342
+ i.save
343
+ i
344
+ end
345
+
346
+ it 'does not error' do
347
+ expect { subject }.not_to raise_error
348
+ end
349
+ it 'sets name' do
350
+ expect(subject.name).to eq('Bobo')
351
+ end
352
+ it 'sets baked_at' do
353
+ expect([ActiveRecord::Type::Time::Value, Time]).to include(subject.baked_at.class)
354
+ end
355
+ it 'sets timestamps' do
356
+ expect(subject.created_at).not_to be_nil
357
+ expect(subject.updated_at).not_to be_nil
358
+ end
359
+ end
360
+
361
+ context 'uniqueness enforced by index' do
362
+ subject do
363
+ i = model.new
364
+ i.name = 'Bobo'
365
+ i.baked_at = Time.now
366
+ i.save!
367
+ end
368
+
369
+ let(:model) do
370
+ described_class.generate(
371
+ table_name: table_name,
372
+ klass_namespaces: klass_namespaces,
373
+ klass_basename: klass_basename,
374
+ columns: columns,
375
+ indexes: indexes,
376
+ timestamps: timestamps,
377
+ connection_params: connection_params
378
+ )
379
+ end
380
+
381
+ before do
382
+ i = model.new
383
+ i.name = 'Bobo'
384
+ i.baked_at = Time.now
385
+ i.save
386
+ end
387
+
388
+ it 'raises error' do
389
+ block_is_expected.to raise_error(ActiveRecord::RecordNotUnique)
390
+ end
391
+ end
392
+ end
393
+ end
394
+
395
+ context 'with unique index as implicit' do
396
+ subject do
397
+ described_class.generate(
398
+ table_name: table_name,
399
+ klass_namespaces: klass_namespaces,
400
+ klass_basename: klass_basename,
401
+ columns: columns,
402
+ indexes: indexes,
403
+ timestamps: timestamps,
404
+ connection_params: connection_params
405
+ )
406
+ end
407
+
408
+ let!(:farm_animal) do
409
+ module Farm
410
+ module Animal
411
+ end
412
+ end
413
+ end
414
+ let(:table_name) { 'dogs' }
415
+ let(:klass_namespaces) { %w[Farm Animal] }
416
+ let(:klass_basename) { 'my' }
417
+ let(:columns) { [{ name: 'name', type: 'string' }, { name: 'baked_at', type: 'time' }] }
418
+ let(:indexes) { [[['name'], unique: true], 'baked_at'] }
419
+ let(:timestamps) { true }
420
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
421
+
422
+ it 'does not error' do
423
+ expect { subject }.not_to raise_error
424
+ end
425
+ context 'instance' do
426
+ subject { super().new }
427
+
428
+ it 'can be instantiated' do
429
+ expect(subject).to be_a(ActiveRecord::Base)
430
+ end
431
+ context 'name' do
432
+ it 'has' do
433
+ expect(subject.name).to be_nil
434
+ end
435
+ end
436
+
437
+ context 'baked_at' do
438
+ it 'has' do
439
+ expect(subject.baked_at).to be_nil
440
+ end
441
+ end
442
+
443
+ context 'timestamps' do
444
+ it 'has' do
445
+ expect(subject.created_at).to be_nil
446
+ expect(subject.updated_at).to be_nil
447
+ end
448
+ end
449
+
450
+ context 'saving' do
451
+ subject do
452
+ i = super()
453
+ i.name = 'Bobo'
454
+ i.baked_at = Time.now
455
+ i.save
456
+ i
457
+ end
458
+
459
+ it 'does not error' do
460
+ expect { subject }.not_to raise_error
461
+ end
462
+ it 'sets name' do
463
+ expect(subject.name).to eq('Bobo')
464
+ end
465
+ it 'sets baked_at' do
466
+ expect([ActiveRecord::Type::Time::Value, Time]).to include(subject.baked_at.class)
467
+ end
468
+ it 'sets timestamps' do
469
+ expect(subject.created_at).not_to be_nil
470
+ expect(subject.updated_at).not_to be_nil
471
+ end
472
+ end
473
+
474
+ context 'uniqueness enforced by index' do
475
+ subject do
476
+ i = model.new
477
+ i.name = 'Bobo'
478
+ i.baked_at = Time.now
479
+ i.save!
480
+ end
481
+
482
+ let(:model) do
483
+ described_class.generate(
484
+ table_name: table_name,
485
+ klass_namespaces: klass_namespaces,
486
+ klass_basename: klass_basename,
487
+ columns: columns,
488
+ indexes: indexes,
489
+ timestamps: timestamps,
490
+ connection_params: connection_params
491
+ )
492
+ end
493
+
494
+ before do
495
+ i = model.new
496
+ i.name = 'Bobo'
497
+ i.baked_at = Time.now
498
+ i.save
499
+ end
500
+
501
+ it 'raises error' do
502
+ block_is_expected.to raise_error(ActiveRecord::RecordNotUnique)
503
+ end
504
+ end
505
+ end
506
+ end
507
+
508
+ context 'no timestamps' do
509
+ subject do
510
+ described_class.generate(
511
+ table_name: table_name,
512
+ klass_basename: klass_basename,
513
+ columns: columns,
514
+ timestamps: timestamps,
515
+ connection_params: connection_params
516
+ )
517
+ end
518
+
519
+ let(:table_name) { 'dogs' }
520
+ let(:klass_basename) { 'my' }
521
+ let(:columns) { ['name'] }
522
+ let(:timestamps) { false }
523
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
524
+
525
+ it 'does not error' do
526
+ expect { subject }.not_to raise_error
527
+ end
528
+ context 'instance' do
529
+ subject { super().new }
530
+
531
+ it 'can be instantiated' do
532
+ expect(subject).to be_a(ActiveRecord::Base)
533
+ end
534
+ context 'timestamps' do
535
+ it 'has not' do
536
+ expect(subject).not_to respond_to(:created_at)
537
+ expect(subject).not_to respond_to(:updated_at)
538
+ end
539
+ end
540
+
541
+ context 'saving' do
542
+ subject do
543
+ i = super()
544
+ i.name = 'Bobo'
545
+ i.save
546
+ i
547
+ end
548
+
549
+ it 'does not error' do
550
+ expect { subject }.not_to raise_error
551
+ end
552
+ it 'sets name' do
553
+ expect(subject.name).to eq('Bobo')
554
+ end
555
+ it 'has no timestamps' do
556
+ expect(subject).not_to respond_to(:created_at)
557
+ expect(subject).not_to respond_to(:updated_at)
558
+ end
559
+ end
560
+ end
561
+ end
562
+
563
+ context 'with block' do
564
+ subject do
565
+ described_class.generate(
566
+ table_name: table_name,
567
+ klass_basename: klass_basename,
568
+ columns: columns,
569
+ timestamps: timestamps,
570
+ connection_params: connection_params
571
+ ) do
572
+ def eat_pie
573
+ 'eating'
574
+ end
575
+
576
+ def flowery_name
577
+ "🌸#{name}🌸"
578
+ end
579
+ end
580
+ end
581
+
582
+ let(:table_name) { 'dogs' }
583
+ let(:klass_basename) { 'my' }
584
+ let(:columns) { ['name'] }
585
+ let(:timestamps) { false }
586
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
587
+
588
+ it 'does not error' do
589
+ expect { subject }.not_to raise_error
590
+ end
591
+ context 'instance' do
592
+ subject { super().new(name: 'Marty McFly') }
593
+
594
+ it 'can be instantiated' do
595
+ expect(subject).to be_a(ActiveRecord::Base)
596
+ end
597
+ context 'block' do
598
+ it 'defines method' do
599
+ expect(subject.eat_pie).to eq('eating')
600
+ end
601
+ it 'has access to class context' do
602
+ expect(subject.flowery_name).to eq('🌸Marty McFly🌸')
603
+ end
604
+ end
605
+
606
+ context 'timestamps' do
607
+ it 'has not' do
608
+ expect(subject).not_to respond_to(:created_at)
609
+ expect(subject).not_to respond_to(:updated_at)
610
+ end
611
+ end
612
+
613
+ context 'saving' do
614
+ subject do
615
+ i = super()
616
+ i.name = 'Bobo'
617
+ i.save
618
+ i
619
+ end
620
+
621
+ it 'does not error' do
622
+ expect { subject }.not_to raise_error
623
+ end
624
+ it 'sets name' do
625
+ expect(subject.name).to eq('Bobo')
626
+ end
627
+ it 'has access to class context' do
628
+ expect(subject.flowery_name).to eq('🌸Bobo🌸')
629
+ end
630
+ it 'has no timestamps' do
631
+ expect(subject).not_to respond_to(:created_at)
632
+ expect(subject).not_to respond_to(:updated_at)
633
+ end
634
+ end
635
+ end
636
+ end
637
+
638
+ context 'testing a module' do
639
+ let!(:has_balloon) do
640
+ module HasBalloon
641
+ def has_balloon?
642
+ name == 'Spot' # only Spot has a balloon
643
+ end
644
+ end
645
+ end
646
+ let(:ar_with_balloon) do
647
+ described_class.generate(columns: ['name']) do
648
+ include HasBalloon
649
+ def flowery_name
650
+ "#{b_f}#{name}#{b_f}"
651
+ end
652
+
653
+ def b_f
654
+ has_balloon? ? '🎈' : '🌸'
655
+ end
656
+ end
657
+ end
658
+
659
+ it 'can test the module' do
660
+ expect(ar_with_balloon.new(name: 'Spot').flowery_name).to eq('🎈Spot🎈')
661
+ expect(ar_with_balloon.new(name: 'Not Spot').flowery_name).to eq('🌸Not Spot🌸')
662
+ end
663
+ end
664
+ end
665
+
666
+ describe '.factory' do
667
+ context 'minimal params' do
668
+ context 'returns array' do
669
+ subject { described_class.factory }
670
+
671
+ it 'be an array' do
672
+ expect(subject).to be_a(Array)
673
+ end
674
+ it 'has length 0' do
675
+ expect(subject.length).to eq(0)
676
+ end
677
+ end
678
+ end
679
+
680
+ context 'all params' do
681
+ subject do
682
+ described_class.factory(
683
+ source_data: source_data,
684
+ table_name: table_name,
685
+ klass_namespaces: klass_namespaces,
686
+ klass_basename: klass_basename,
687
+ columns: columns,
688
+ indexes: indexes,
689
+ timestamps: timestamps,
690
+ connection_params: connection_params
691
+ )
692
+ end
693
+
694
+ let!(:farm_animal) do
695
+ module Zoo
696
+ module Animal
697
+ end
698
+ end
699
+ end
700
+ let(:table_name) { 'dogs' }
701
+ let(:klass_namespaces) { %w[Zoo Animal] }
702
+ let(:klass_basename) { 'my' }
703
+ let(:columns) { ['name'] }
704
+ let(:indexes) { [{ columns: ['name'] }] }
705
+ let(:timestamps) { true }
706
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
707
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
708
+
709
+ context 'returns array' do
710
+ it 'be an array' do
711
+ expect(subject).to be_a(Array)
712
+ end
713
+ it 'has length 2' do
714
+ expect(subject.length).to eq(2)
715
+ end
716
+ end
717
+
718
+ context 'sets attributes' do
719
+ subject { super().map(&:name) }
720
+
721
+ it 'be an array' do
722
+ expect(subject).to eq(['Gru Banksy', 'Herlina Termalina'])
723
+ end
724
+ end
725
+ end
726
+
727
+ context 'no timestamps' do
728
+ subject do
729
+ described_class.factory(
730
+ source_data: source_data,
731
+ table_name: table_name,
732
+ klass_basename: klass_basename,
733
+ columns: columns,
734
+ indexes: indexes,
735
+ timestamps: timestamps,
736
+ connection_params: connection_params
737
+ )
738
+ end
739
+
740
+ let(:table_name) { 'dogs' }
741
+ let(:klass_basename) { 'my' }
742
+ let(:columns) { ['name'] }
743
+ let(:indexes) { [{ columns: ['name'] }] }
744
+ let(:timestamps) { false }
745
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
746
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
747
+
748
+ context 'returns array' do
749
+ it 'be an array' do
750
+ expect(subject).to be_a(Array)
751
+ end
752
+ it 'has length 2' do
753
+ expect(subject.length).to eq(2)
754
+ end
755
+ end
756
+
757
+ context 'does not have timestamps' do
758
+ subject { super().map { |anon| anon.respond_to?(:created_at) } }
759
+
760
+ it 'be an array' do
761
+ expect(subject).to eq([false, false])
762
+ end
763
+ end
764
+ end
765
+
766
+ context 'with block' do
767
+ subject do
768
+ described_class.factory(
769
+ source_data: source_data,
770
+ table_name: table_name,
771
+ klass_basename: klass_basename,
772
+ columns: columns,
773
+ indexes: indexes,
774
+ timestamps: timestamps,
775
+ connection_params: connection_params
776
+ ) do
777
+ def eat_pie
778
+ 'eating'
779
+ end
780
+
781
+ def flowery_name
782
+ "🌸#{name}🌸"
783
+ end
784
+ end
785
+ end
786
+
787
+ let(:table_name) { 'dogs' }
788
+ let(:klass_basename) { 'my' }
789
+ let(:columns) { ['name'] }
790
+ let(:indexes) { [{ columns: ['name'] }] }
791
+ let(:timestamps) { false }
792
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
793
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
794
+
795
+ context 'returns array' do
796
+ it 'be an array' do
797
+ expect(subject).to be_a(Array)
798
+ end
799
+ it 'has length 2' do
800
+ expect(subject.length).to eq(2)
801
+ end
802
+ end
803
+
804
+ context 'defines method' do
805
+ subject { super().map(&:eat_pie) }
806
+
807
+ it 'defines method' do
808
+ expect(subject).to eq(%w[eating eating])
809
+ end
810
+ end
811
+
812
+ context 'sets attributes' do
813
+ subject { super().map(&:flowery_name) }
814
+
815
+ it 'be an array' do
816
+ expect(subject).to eq(['🌸Gru Banksy🌸', '🌸Herlina Termalina🌸'])
817
+ end
818
+ end
819
+ end
820
+ end
821
+
822
+ describe '.factory!' do
823
+ context 'minimal params' do
824
+ context 'returns array' do
825
+ subject { described_class.factory! }
826
+
827
+ it 'be an array' do
828
+ expect(subject).to be_a(Array)
829
+ end
830
+ it 'has length 0' do
831
+ expect(subject.length).to eq(0)
832
+ end
833
+ end
834
+ end
835
+
836
+ context 'all params' do
837
+ subject do
838
+ described_class.factory!(
839
+ source_data: source_data,
840
+ table_name: table_name,
841
+ klass_namespaces: klass_namespaces,
842
+ klass_basename: klass_basename,
843
+ columns: columns,
844
+ indexes: indexes,
845
+ timestamps: timestamps,
846
+ connection_params: connection_params
847
+ )
848
+ end
849
+
850
+ let!(:farm_animal) do
851
+ module Zoo
852
+ module Animal
853
+ end
854
+ end
855
+ end
856
+ let(:table_name) { 'dogs' }
857
+ let(:klass_namespaces) { %w[Zoo Animal] }
858
+ let(:klass_basename) { 'my' }
859
+ let(:columns) { ['name'] }
860
+ let(:indexes) { [{ columns: ['name'] }] }
861
+ let(:timestamps) { true }
862
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
863
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
864
+
865
+ context 'returns array' do
866
+ it 'be an array' do
867
+ expect(subject).to be_a(Array)
868
+ end
869
+ it 'has length 2' do
870
+ expect(subject.length).to eq(2)
871
+ end
872
+ end
873
+
874
+ context 'sets attributes' do
875
+ subject { super().map(&:name) }
876
+
877
+ it 'be an array' do
878
+ expect(subject).to eq(['Gru Banksy', 'Herlina Termalina'])
879
+ end
880
+ end
881
+ end
882
+
883
+ context 'no timestamps' do
884
+ subject do
885
+ described_class.factory!(
886
+ source_data: source_data,
887
+ table_name: table_name,
888
+ klass_basename: klass_basename,
889
+ columns: columns,
890
+ indexes: indexes,
891
+ timestamps: timestamps,
892
+ connection_params: connection_params
893
+ )
894
+ end
895
+
896
+ let(:table_name) { 'dogs' }
897
+ let(:klass_basename) { 'my' }
898
+ let(:columns) { ['name'] }
899
+ let(:indexes) { [{ columns: ['name'] }] }
900
+ let(:timestamps) { false }
901
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
902
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
903
+
904
+ context 'returns array' do
905
+ it 'be an array' do
906
+ expect(subject).to be_a(Array)
907
+ end
908
+ it 'has length 2' do
909
+ expect(subject.length).to eq(2)
910
+ end
911
+ end
912
+
913
+ context 'does not have timestamps' do
914
+ subject { super().map { |anon| anon.respond_to?(:created_at) } }
915
+
916
+ it 'be an array' do
917
+ expect(subject).to eq([false, false])
918
+ end
919
+ end
920
+ end
921
+
922
+ context 'with block' do
923
+ subject do
924
+ described_class.factory!(
925
+ source_data: source_data,
926
+ table_name: table_name,
927
+ klass_basename: klass_basename,
928
+ columns: columns,
929
+ indexes: indexes,
930
+ timestamps: timestamps,
931
+ connection_params: connection_params
932
+ ) do
933
+ def eat_pie
934
+ 'eating'
935
+ end
936
+
937
+ def flowery_name
938
+ "🌸#{name}🌸"
939
+ end
940
+ end
941
+ end
942
+
943
+ let(:table_name) { 'dogs' }
944
+ let(:klass_basename) { 'my' }
945
+ let(:columns) { ['name'] }
946
+ let(:indexes) { [{ columns: ['name'] }] }
947
+ let(:timestamps) { false }
948
+ let(:source_data) { [{ name: 'Gru Banksy' }, { name: 'Herlina Termalina' }] }
949
+ let(:connection_params) { AnonymousActiveRecord::DEFAULT_CONNECTION_PARAMS }
950
+
951
+ context 'returns array' do
952
+ it 'be an array' do
953
+ expect(subject).to be_a(Array)
954
+ end
955
+ it 'has length 2' do
956
+ expect(subject.length).to eq(2)
957
+ end
958
+ end
959
+
960
+ context 'defines method' do
961
+ subject { super().map(&:eat_pie) }
962
+
963
+ it 'defines method' do
964
+ expect(subject).to eq(%w[eating eating])
965
+ end
966
+ end
967
+
968
+ context 'sets attributes' do
969
+ subject { super().map(&:flowery_name) }
970
+
971
+ it 'be an array' do
972
+ expect(subject).to eq(['🌸Gru Banksy🌸', '🌸Herlina Termalina🌸'])
973
+ end
974
+ end
975
+ end
976
+ end
977
+ end