temping 3.9.0 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/lib/temping/model_factory.rb +69 -0
- data/lib/temping/namespace_factory.rb +24 -0
- data/lib/temping.rb +116 -61
- metadata +89 -29
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 17a3e61694c280ee41ecaba6d9ba29f7815dd0ae7126bb931872819669ee03e7
|
4
|
+
data.tar.gz: ce7e225a2e34ed393ca11fe8d5cb1a5695806142282d940339fbf991f24a45b9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9aaf7aee2a796eb3a19df188097c548619665b1d37923d86a702657a28d9f0339daa0e48d62dfd2b2b27b31fe5a27704ff3103b6c342ecd3eaeacaadc3c46906
|
7
|
+
data.tar.gz: f0406caf8f32fcf950a5328eae4f46bb46573d50e0b1e58247fdeb0622552471c1a6ed0faa047b0535e35674fd79fd1b8d6ea0a73a200cf158fb4b3dfc7c4675
|
@@ -0,0 +1,69 @@
|
|
1
|
+
class Temping::ModelFactory
|
2
|
+
DEFAULT_OPTIONS = {temporary: true}
|
3
|
+
|
4
|
+
def initialize(name, namespace, options = {}, &block)
|
5
|
+
@name = name
|
6
|
+
@namespace = namespace
|
7
|
+
@options = options
|
8
|
+
klass.class_eval(&block) if block
|
9
|
+
klass.reset_column_information
|
10
|
+
end
|
11
|
+
|
12
|
+
def klass
|
13
|
+
@klass ||= @namespace.const_get(@name)
|
14
|
+
rescue NameError
|
15
|
+
@klass = build
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
|
20
|
+
def build
|
21
|
+
Class.new(parent_class_name).tap do |klass|
|
22
|
+
@namespace.const_set(@name, klass)
|
23
|
+
klass.primary_key = @options[:primary_key] || :id
|
24
|
+
create_table(@options)
|
25
|
+
add_methods
|
26
|
+
klass.namespace = @namespace
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def parent_class_name
|
31
|
+
@options.fetch(:parent_class, default_parent_class_name)
|
32
|
+
end
|
33
|
+
|
34
|
+
def default_parent_class_name
|
35
|
+
if defined?(ApplicationRecord)
|
36
|
+
ApplicationRecord
|
37
|
+
else
|
38
|
+
ActiveRecord::Base
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def create_table(options = {})
|
43
|
+
connection.create_table(table_name, **DEFAULT_OPTIONS.merge(options))
|
44
|
+
end
|
45
|
+
|
46
|
+
def add_methods
|
47
|
+
class << klass
|
48
|
+
attr_accessor :namespace
|
49
|
+
|
50
|
+
def with_columns
|
51
|
+
connection.change_table(table_name) do |table|
|
52
|
+
yield(table)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def table_exists?
|
57
|
+
true
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def connection
|
63
|
+
klass.connection
|
64
|
+
end
|
65
|
+
|
66
|
+
def table_name
|
67
|
+
klass.table_name
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
class Temping::NamespaceFactory
|
2
|
+
def initialize(name)
|
3
|
+
@name = name
|
4
|
+
end
|
5
|
+
|
6
|
+
def klass
|
7
|
+
@klass ||= @name.split("::").reduce(Object) { |parent, name_part| build(parent, name_part) }
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def build(parent, name_part)
|
13
|
+
parent.const_get(name_part)
|
14
|
+
rescue NameError
|
15
|
+
parent.const_set(
|
16
|
+
name_part,
|
17
|
+
Module.new do
|
18
|
+
def self.defined_by_temping?
|
19
|
+
true
|
20
|
+
end
|
21
|
+
end
|
22
|
+
)
|
23
|
+
end
|
24
|
+
end
|
data/lib/temping.rb
CHANGED
@@ -1,94 +1,149 @@
|
|
1
1
|
require "active_record"
|
2
2
|
require "active_support/core_ext/string"
|
3
3
|
|
4
|
+
class Temping; end
|
5
|
+
|
6
|
+
require "temping/namespace_factory"
|
7
|
+
require "temping/model_factory"
|
8
|
+
|
4
9
|
class Temping
|
5
|
-
@
|
10
|
+
@namespaces = []
|
11
|
+
@models = []
|
6
12
|
|
7
13
|
class << self
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
14
|
+
# Create a new temporary ActiveRecord model with a name specified by `name`.
|
15
|
+
#
|
16
|
+
# Provided `options` are all passed to the inner `create_table` call so anything
|
17
|
+
# acceptable by `create_table` method can be passed here.
|
18
|
+
# In addition `options` can include `parent_class` key to specify parent class for the model.
|
19
|
+
# When `block` is passed, it is evaluated in the context of the class. This means anything you
|
20
|
+
# do in an ActiveRecord model class body can be accomplished in `block` including method
|
21
|
+
# definitions, validations, module includes, etc.
|
22
|
+
# Additional database columns can be specified via `with_columns` method inside `block`,
|
23
|
+
# which uses Rails migration syntax.
|
24
|
+
def create(name, options = {}, &block)
|
25
|
+
namespace_name, model_name = split_name(name)
|
26
|
+
namespace = namespace_name ? NamespaceFactory.new(namespace_name).klass : Object
|
27
|
+
@namespaces << namespace if namespace_name
|
28
|
+
model = ModelFactory.new(model_name, namespace, options, &block).klass
|
29
|
+
@models << model
|
30
|
+
model
|
13
31
|
end
|
14
32
|
|
33
|
+
# Completely destroy everything created by Temping.
|
34
|
+
#
|
35
|
+
# This includes:
|
36
|
+
# * removing all the records in the models created by Temping and dropping their tables
|
37
|
+
# from the database;
|
38
|
+
# * undefining model constants so they cannot be pointed to anymore in the code.
|
15
39
|
def teardown
|
16
|
-
if @
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
Object.send(:remove_const, klass.name)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
@model_klasses.clear
|
24
|
-
ActiveSupport::Dependencies::Reference.clear!
|
40
|
+
if @models.any?
|
41
|
+
teardown_models
|
42
|
+
teardown_namespaces
|
43
|
+
ActiveSupport::Dependencies::Reference.clear! if ActiveRecord::VERSION::MAJOR < 7
|
25
44
|
end
|
26
45
|
end
|
27
46
|
|
47
|
+
# Destroy all records from each of the models created by Temping.
|
48
|
+
#
|
49
|
+
# This does not undefine the models themselves or drop their tables.
|
50
|
+
# This method is an alternative to `teardown` if you want to keep the models and tables.
|
28
51
|
def cleanup
|
29
|
-
@
|
52
|
+
@models.reverse_each(&:destroy_all)
|
30
53
|
end
|
31
|
-
end
|
32
54
|
|
33
|
-
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
55
|
+
# Split the provided name finding the namespace (if any) and the model name without namespace.
|
56
|
+
def split_name(name)
|
57
|
+
classified_name = name.to_s.classify
|
58
|
+
name_parts = classified_name.split("::")
|
59
|
+
namespace_name = name_parts[0...-1].join("::")
|
60
|
+
return [nil, classified_name] if namespace_name.empty?
|
40
61
|
|
41
|
-
|
42
|
-
@klass ||= Object.const_get(@model_name)
|
43
|
-
rescue NameError
|
44
|
-
@klass = build
|
62
|
+
[namespace_name, name_parts.last]
|
45
63
|
end
|
64
|
+
private :split_name
|
46
65
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
66
|
+
# Iterate over `@models`, undefine model constants, drop tables, and remove the constants
|
67
|
+
# from the array one by one starting with the models defined last. (Models defined later
|
68
|
+
# can point to older models by using foreign keys so they have to be removed first).
|
69
|
+
def teardown_models
|
70
|
+
@models.reverse_each do |model|
|
71
|
+
model_name_without_namespace = model.name.split("::").last
|
72
|
+
if model.namespace.const_defined?(model_name_without_namespace)
|
73
|
+
model.connection.drop_table(model.table_name)
|
74
|
+
model.namespace.send(:remove_const, model_name_without_namespace)
|
75
|
+
end
|
56
76
|
end
|
77
|
+
@models.clear
|
57
78
|
end
|
79
|
+
private :teardown_models
|
58
80
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
81
|
+
# Iterate over `@namespaces`, undefine modules and remove them from the array one by one
|
82
|
+
# starting with the deepest ones first.
|
83
|
+
def teardown_namespaces
|
84
|
+
@namespaces.select! { |namespace| namespace_still_defined?(namespace) }
|
85
|
+
until @namespaces.empty?
|
86
|
+
namespace, index = @namespaces.each_with_index.max_by { |n, _i| n.name.split("::").length }
|
87
|
+
parts = namespace.name.split("::")
|
88
|
+
parent = parts.length == 1 ? Object : parts[0...-1].join("::").constantize
|
89
|
+
parent.send(:remove_const, parts.last) if namespace_removable?(namespace, parts, parent)
|
90
|
+
delete_or_trim_in_namespaces(parent, parts, index)
|
64
91
|
end
|
65
92
|
end
|
93
|
+
private :teardown_namespaces
|
66
94
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
yield(table)
|
77
|
-
end
|
78
|
-
end
|
95
|
+
# Check if namespace is still defined.
|
96
|
+
# It could be already removed if it were inside a model created by Temping.
|
97
|
+
# Since `@models` are teared down first, it means that in such a case all modules that were
|
98
|
+
# inside that model are no longer defined.
|
99
|
+
def namespace_still_defined?(namespace)
|
100
|
+
parent = Object
|
101
|
+
outer_namespace_parts = []
|
102
|
+
namespace.to_s.split("::").each do |part|
|
103
|
+
return false unless parent.const_defined?(part)
|
79
104
|
|
80
|
-
|
81
|
-
|
82
|
-
end
|
105
|
+
outer_namespace_parts.push(part)
|
106
|
+
parent = outer_namespace_parts.join("::").constantize
|
83
107
|
end
|
108
|
+
true
|
84
109
|
end
|
110
|
+
private :namespace_still_defined?
|
85
111
|
|
86
|
-
|
87
|
-
|
112
|
+
# Namespace can be removed only if it's still defined and if it was created by Temping,
|
113
|
+
# we use `defined_by_temping?` to indicate the latter.
|
114
|
+
def namespace_removable?(namespace, parts, parent)
|
115
|
+
parent.const_defined?(parts.last) && namespace.defined_by_temping?
|
116
|
+
rescue NoMethodError
|
117
|
+
false
|
88
118
|
end
|
119
|
+
private :namespace_removable?
|
120
|
+
|
121
|
+
# Clean `@namespaces` array by either removing current namespace or replacing it with its
|
122
|
+
# parent.
|
123
|
+
#
|
124
|
+
# Case 1: @namespaces = [A, B, C, D]; index = 3
|
125
|
+
# This is an outer-most module, we just remove it from `@namespaces`.
|
126
|
+
#
|
127
|
+
# Case 2: @namespaces = [A::B, A::B::C, A::D]; index = 1
|
128
|
+
# Once we remove C from A::B::C, it becomes A::B, but we already have A::B, so just remove it.
|
129
|
+
#
|
130
|
+
# Case 3: @namespaces = [A::B, A::D]; index = 1
|
131
|
+
# Once we remove D from A::D, it becomes A, replace A::D with A.
|
132
|
+
def delete_or_trim_in_namespaces(parent, parts, index)
|
133
|
+
is_last_module = parts.length == 1
|
134
|
+
if is_last_module
|
135
|
+
@namespaces.delete_at(index)
|
136
|
+
return
|
137
|
+
end
|
138
|
+
|
139
|
+
parent_already_in_namespaces = @namespaces.include?(parent)
|
140
|
+
if parent_already_in_namespaces
|
141
|
+
@namespaces.delete_at(index)
|
142
|
+
return
|
143
|
+
end
|
89
144
|
|
90
|
-
|
91
|
-
klass.table_name
|
145
|
+
@namespaces[index] = parent
|
92
146
|
end
|
147
|
+
private :delete_or_trim_in_namespaces
|
93
148
|
end
|
94
149
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: temping
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 4.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- John Pignata
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-12-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -16,112 +16,170 @@ dependencies:
|
|
16
16
|
requirements:
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '5.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '7.1'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
23
26
|
requirements:
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
29
|
+
version: '5.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '7.1'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: activesupport
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
30
36
|
requirements:
|
31
37
|
- - ">="
|
32
38
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
39
|
+
version: '5.2'
|
40
|
+
- - "<"
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
version: '7.1'
|
34
43
|
type: :runtime
|
35
44
|
prerelease: false
|
36
45
|
version_requirements: !ruby/object:Gem::Requirement
|
37
46
|
requirements:
|
38
47
|
- - ">="
|
39
48
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
49
|
+
version: '5.2'
|
50
|
+
- - "<"
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '7.1'
|
41
53
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
54
|
+
name: appraisal
|
43
55
|
requirement: !ruby/object:Gem::Requirement
|
44
56
|
requirements:
|
45
57
|
- - "~>"
|
46
58
|
- !ruby/object:Gem::Version
|
47
|
-
version:
|
59
|
+
version: '2.2'
|
48
60
|
type: :development
|
49
61
|
prerelease: false
|
50
62
|
version_requirements: !ruby/object:Gem::Requirement
|
51
63
|
requirements:
|
52
64
|
- - "~>"
|
53
65
|
- !ruby/object:Gem::Version
|
54
|
-
version:
|
66
|
+
version: '2.2'
|
67
|
+
- !ruby/object:Gem::Dependency
|
68
|
+
name: sqlite3
|
69
|
+
requirement: !ruby/object:Gem::Requirement
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: '1.3'
|
74
|
+
- - "<"
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '2.0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '1.3'
|
84
|
+
- - "<"
|
85
|
+
- !ruby/object:Gem::Version
|
86
|
+
version: '2.0'
|
55
87
|
- !ruby/object:Gem::Dependency
|
56
88
|
name: pg
|
89
|
+
requirement: !ruby/object:Gem::Requirement
|
90
|
+
requirements:
|
91
|
+
- - ">="
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '1.2'
|
94
|
+
- - "<"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '2.0'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - ">="
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '1.2'
|
104
|
+
- - "<"
|
105
|
+
- !ruby/object:Gem::Version
|
106
|
+
version: '2.0'
|
107
|
+
- !ruby/object:Gem::Dependency
|
108
|
+
name: mysql2
|
57
109
|
requirement: !ruby/object:Gem::Requirement
|
58
110
|
requirements:
|
59
111
|
- - "~>"
|
60
112
|
- !ruby/object:Gem::Version
|
61
|
-
version: 0.
|
113
|
+
version: '0.5'
|
62
114
|
type: :development
|
63
115
|
prerelease: false
|
64
116
|
version_requirements: !ruby/object:Gem::Requirement
|
65
117
|
requirements:
|
66
118
|
- - "~>"
|
67
119
|
- !ruby/object:Gem::Version
|
68
|
-
version: 0.
|
120
|
+
version: '0.5'
|
69
121
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
122
|
+
name: rspec
|
71
123
|
requirement: !ruby/object:Gem::Requirement
|
72
124
|
requirements:
|
73
125
|
- - "~>"
|
74
126
|
- !ruby/object:Gem::Version
|
75
|
-
version:
|
127
|
+
version: '3.12'
|
76
128
|
type: :development
|
77
129
|
prerelease: false
|
78
130
|
version_requirements: !ruby/object:Gem::Requirement
|
79
131
|
requirements:
|
80
132
|
- - "~>"
|
81
133
|
- !ruby/object:Gem::Version
|
82
|
-
version:
|
134
|
+
version: '3.12'
|
83
135
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
136
|
+
name: rake
|
85
137
|
requirement: !ruby/object:Gem::Requirement
|
86
138
|
requirements:
|
87
139
|
- - "~>"
|
88
140
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0
|
141
|
+
version: '13.0'
|
90
142
|
type: :development
|
91
143
|
prerelease: false
|
92
144
|
version_requirements: !ruby/object:Gem::Requirement
|
93
145
|
requirements:
|
94
146
|
- - "~>"
|
95
147
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0
|
148
|
+
version: '13.0'
|
97
149
|
- !ruby/object:Gem::Dependency
|
98
|
-
name:
|
150
|
+
name: simplecov
|
99
151
|
requirement: !ruby/object:Gem::Requirement
|
100
152
|
requirements:
|
101
|
-
- - "
|
153
|
+
- - "~>"
|
102
154
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
155
|
+
version: '0.17'
|
104
156
|
type: :development
|
105
157
|
prerelease: false
|
106
158
|
version_requirements: !ruby/object:Gem::Requirement
|
107
159
|
requirements:
|
108
|
-
- - "
|
160
|
+
- - "~>"
|
109
161
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
162
|
+
version: '0.17'
|
111
163
|
- !ruby/object:Gem::Dependency
|
112
|
-
name:
|
164
|
+
name: standard
|
113
165
|
requirement: !ruby/object:Gem::Requirement
|
114
166
|
requirements:
|
115
167
|
- - ">="
|
116
168
|
- !ruby/object:Gem::Version
|
117
|
-
version:
|
169
|
+
version: 0.0.1
|
170
|
+
- - "<"
|
171
|
+
- !ruby/object:Gem::Version
|
172
|
+
version: '2.0'
|
118
173
|
type: :development
|
119
174
|
prerelease: false
|
120
175
|
version_requirements: !ruby/object:Gem::Requirement
|
121
176
|
requirements:
|
122
177
|
- - ">="
|
123
178
|
- !ruby/object:Gem::Version
|
124
|
-
version:
|
179
|
+
version: 0.0.1
|
180
|
+
- - "<"
|
181
|
+
- !ruby/object:Gem::Version
|
182
|
+
version: '2.0'
|
125
183
|
description:
|
126
184
|
email: john@pignata.com
|
127
185
|
executables: []
|
@@ -129,8 +187,11 @@ extensions: []
|
|
129
187
|
extra_rdoc_files: []
|
130
188
|
files:
|
131
189
|
- lib/temping.rb
|
190
|
+
- lib/temping/model_factory.rb
|
191
|
+
- lib/temping/namespace_factory.rb
|
132
192
|
homepage: http://github.com/jpignata/temping
|
133
|
-
licenses:
|
193
|
+
licenses:
|
194
|
+
- MIT
|
134
195
|
metadata: {}
|
135
196
|
post_install_message:
|
136
197
|
rdoc_options: []
|
@@ -140,15 +201,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
140
201
|
requirements:
|
141
202
|
- - ">="
|
142
203
|
- !ruby/object:Gem::Version
|
143
|
-
version:
|
204
|
+
version: 2.2.2
|
144
205
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
145
206
|
requirements:
|
146
207
|
- - ">="
|
147
208
|
- !ruby/object:Gem::Version
|
148
209
|
version: '0'
|
149
210
|
requirements: []
|
150
|
-
|
151
|
-
rubygems_version: 2.6.10
|
211
|
+
rubygems_version: 3.0.3
|
152
212
|
signing_key:
|
153
213
|
specification_version: 4
|
154
214
|
summary: Create temporary table-backed ActiveRecord models for use in tests
|