fixpoints 0.1.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +2 -1
- data/README.md +117 -28
- data/lib/fixpoint.rb +11 -13
- data/lib/fixpoint_test_helpers.rb +62 -0
- data/lib/fixpoints.rb +1 -0
- data/lib/fixpoints/version.rb +1 -1
- data/lib/incremental_fixpoint.rb +3 -61
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: fad78acf149e5b3740844ca088969ddcf7e377a10d06f224be8b4f6c400a2b9c
|
4
|
+
data.tar.gz: 9f3cf9819a8e4d549b3e17b6556888070dbcb2d091bde9b8d27b104246c183f2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8f9618522b5e02bbcff8d06fc94df4da23f7dc35bf8ff0536c91483a66f06375f3a9e203b06e92707c1a1f0e2693a34b3a88c641eda650b3511d92539eadb985
|
7
|
+
data.tar.gz: 3a7294db258a4cae2e790e435ee0d2796914d77a877d24a6c354716a9ef87402622ada27734b9f360f7d5dae6e6d45571b9e3af44093ca391834c6732f6263a1
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -2,46 +2,138 @@
|
|
2
2
|
|
3
3
|
Fixpoints enables saving, restoring and comparing the database state before & after tests.
|
4
4
|
|
5
|
+
This gem came about during my time at [Netskin GmbH](https://www.netskin.com/en). Check it out, we do great (Rails) work there.
|
6
|
+
|
5
7
|
## Motivation
|
6
8
|
|
7
|
-
|
9
|
+
When running behavior tests, we seed the database with a defined snapshot called fixpoint.
|
10
|
+
We do run the behavior test and save the resulting database state as another fixpoint.
|
11
|
+
This method allows testing complex business processes in legacy applications without having to implement fixtures/factories upfront.
|
12
|
+
By building one fixpoint on top of another, we can ensure that the process chain works without any gaps.
|
13
|
+
Comparing each resulting database state at the end of a test with a previously recorded state ensures that refactoring did not have unintended side effects.
|
14
|
+
|
15
|
+
**Advantages**
|
16
|
+
|
17
|
+
- No need to write fixtures or factories
|
18
|
+
- discover which records were created/changed by the test’s actions by reading the fixpoint file (YAML)
|
19
|
+
- get notified about differences in database state (i.e. unintended side effects) after refactoring something
|
20
|
+
- allow version control to save the "ground truth" at the end of a test
|
21
|
+
|
22
|
+
Please check out the full article: [Behavior-Driven Test Data](https://tomrothe.de/posts/behaviour-driven-test-data.html).
|
8
23
|
|
9
|
-
|
24
|
+
## Installation
|
25
|
+
|
26
|
+
Add this line to your application's Gemfile: `gem 'fixpoints'` and make sure to add:
|
27
|
+
|
28
|
+
```ruby
|
29
|
+
# rails_helper.rb
|
30
|
+
RSpec.configure do |config|
|
31
|
+
# ...
|
32
|
+
config.include FixpointTestHelpers
|
33
|
+
end
|
34
|
+
```
|
10
35
|
|
11
36
|
## Usage
|
12
37
|
|
13
|
-
|
38
|
+
We save the fixpoint (database snapshot) after the test. Other tests can build on them.
|
14
39
|
|
15
|
-
|
40
|
+
A fixpoint is a snapshot of the database contents as YAML file.
|
41
|
+
It is saved to the `spec/fixpoints` folder.
|
42
|
+
The file contains a mapping of table names to a list if their records.
|
43
|
+
Empty tables are stripped from files.
|
16
44
|
|
45
|
+
**Order & Bootstrapping** We need to mind the order though.
|
46
|
+
When bootstrapping (when there is no fixpoints saved to the disk yet), we need to make sure that all tests that depend on a certain fixpoint run _after_ it was stored.
|
47
|
+
In a single RSpec file, you can use the order in which the tests are defined (`RSpec.describe 'MyFeature', order: :defined do`).
|
48
|
+
However, tests in groups might follow a slightly different order (see [RSpec Docs](https://relishapp.com/rspec/rspec-core/docs/configuration/overriding-global-ordering))
|
17
49
|
|
18
50
|
```ruby
|
19
|
-
|
20
|
-
it 'registers a user' do
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
51
|
+
RSpec.describe 'User Flow', order: :defined do # !!! mind the order here !!!
|
52
|
+
it 'registers a user' do
|
53
|
+
visit new_user_path
|
54
|
+
fill_in 'Name', with: 'Tom'
|
55
|
+
click_on 'Save'
|
56
|
+
|
57
|
+
store_fixpoint_unless_present :registered_user
|
58
|
+
# creates a YAML file containing all records (/spec/fixpoints/registred_user.yml)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'posts an item' do
|
62
|
+
restore_fixpoint :registered_user
|
63
|
+
|
64
|
+
user = User.find_by(name: 'Hans')
|
65
|
+
visit new_item_path(user)
|
66
|
+
fill_in 'Item', with: '...'
|
67
|
+
click_on 'Post'
|
68
|
+
|
69
|
+
compare_fixpoint(:item_posted, store_fixpoint_and_fail: true)
|
70
|
+
# compares the database state with the previously saved fixpoint and
|
71
|
+
# raises if there is a difference. when there is no previous fixpoint,
|
72
|
+
# it writes the fixpoint and fails the test (so it can be re-run)
|
73
|
+
end
|
27
74
|
end
|
75
|
+
```
|
76
|
+
|
77
|
+
**Changes** If you did a lot of changes to a test, you can remove a fixpoint file from its directory.
|
78
|
+
It will be recreated when the test producing it runs again.
|
79
|
+
Don't forget re-running the tests based on it because their fixpoints might have to change too.
|
80
|
+
Example: You need to add something to the database's `seeds.rb`. All subsequent fixpoints are missing the required entry.
|
81
|
+
To update all fixpoints, just remove the whole `spec/fixpoints` folder and re-run all tests. Now all fixpoints should be updated.
|
82
|
+
Be careful though, don't just remove the fixpoints if you are not sure what is going on.
|
83
|
+
A change in a fixpoint might point to an unintended change in code.
|
28
84
|
|
29
|
-
|
85
|
+
We need to be be careful to use `let` and `let!` with factories.
|
86
|
+
Records might be created twice when using create in there (once by the fixpoint and once by the factory).
|
87
|
+
|
88
|
+
**Ignoring columns** Often you might want to add more columns to ignore (e.g. login time stamps):
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
let(:ignored_fixpoint_columns) { [:updated_at, :created_at, users: [:last_login_at] }
|
92
|
+
# ignores timestamps for all tables, and last_login_at for the users table
|
93
|
+
|
94
|
+
it 'logs in' do
|
30
95
|
restore_fixpoint :registered_user
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
fill_in 'Item', with: '...'
|
35
|
-
click_on 'Post'
|
36
|
-
|
37
|
-
compare_fixpoint(:posted_item, ignore_columns: [:release_date], store_fixpoint_and_fail: true)
|
38
|
-
# compares the database state with the previously saved fixpoint and
|
39
|
-
# raises if there is a difference. when there is no previous fixpoint,
|
40
|
-
# it writes it and fails the test (so it can be re-run)
|
96
|
+
# ...
|
97
|
+
compare_fixpoint(:registered_user, ignored_fixpoint_columns)
|
98
|
+
# asserts that there is no change
|
41
99
|
end
|
42
100
|
```
|
43
101
|
|
44
|
-
|
102
|
+
**Incremental** By the default the `FixpointTestHelpers` use the `IncrementalFixpoint` instead of the more verbose `Fixpoint` version.
|
103
|
+
This means that only changes are saved to the YAML file.
|
104
|
+
In order to achieve this, we must make sure that we let the store function know who daddy is.
|
105
|
+
|
106
|
+
```ruby
|
107
|
+
it 'posts an item' do
|
108
|
+
restore_fixpoint :registered_user
|
109
|
+
# ...
|
110
|
+
compare_fixpoint(fixname, store_fixpoint_and_fail: true, parent_fixname: :registered_user)
|
111
|
+
# now only changes to compared to the previous fixpoint are stored
|
112
|
+
# instead of using the name of the last restored fixpoint, you can also use `:last_restored`
|
113
|
+
end
|
114
|
+
```
|
115
|
+
|
116
|
+
**Multiple Databases** If an application uses multiple databases, you can use the optional `connection` parameter
|
117
|
+
to specify the database connection to use.
|
118
|
+
|
119
|
+
```ruby
|
120
|
+
it 'posts an item' do
|
121
|
+
restore_fixpoint :registered_user, connection: ActiveRecord::Base.connection
|
122
|
+
# ...
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
## Limitations & Known issues
|
127
|
+
|
128
|
+
- The records in tables are ordered by their id.
|
129
|
+
If there is no id for a table, we use database's order (what the SELECT query returns).
|
130
|
+
This order may be instable.
|
131
|
+
- We do not clean the database after each test, depending on your cleaning strategy (e.g. transaction), we might leak primary key sequence counters from one test to another.
|
132
|
+
If you have problems try running `Fixpoint.reset_pk_sequences!` and create am issue, so we can investigate.
|
133
|
+
- Under certain conditions you may get `duplicate key value violates unique constraint` because the primary key sequences are not updated correctly.
|
134
|
+
If this happens, just add a `Fixpoint.reset_pk_sequences!` at the beginning of your test. We need to dig a little deeper here at some point...
|
135
|
+
|
136
|
+
# Development
|
45
137
|
|
46
138
|
```bash
|
47
139
|
docker run --rm -ti -v (pwd):/app -w /app ruby:2.7 bash
|
@@ -53,12 +145,9 @@ gem build
|
|
53
145
|
gem install fixpoints-0.1.0.gem
|
54
146
|
pry -r fixpoints
|
55
147
|
gem uninstall fixpoints
|
56
|
-
gem push
|
148
|
+
gem push fixpoints
|
57
149
|
```
|
58
150
|
|
59
|
-
|
60
|
-
## Development
|
61
|
-
|
62
151
|
## Contributing
|
63
152
|
|
64
153
|
Bug reports and pull requests are welcome on GitHub at https://github.com/motine/fixpoints.
|
data/lib/fixpoint.rb
CHANGED
@@ -45,8 +45,8 @@ class Fixpoint
|
|
45
45
|
end
|
46
46
|
|
47
47
|
# Creates a Fixpoint from the database contents. Empty tables are skipped.
|
48
|
-
def from_database
|
49
|
-
new(read_database_records)
|
48
|
+
def from_database(conn)
|
49
|
+
new(read_database_records(conn))
|
50
50
|
end
|
51
51
|
|
52
52
|
def remove(fixname)
|
@@ -56,7 +56,7 @@ class Fixpoint
|
|
56
56
|
# reset primary key sequences for all tables
|
57
57
|
# useful when tests sometimes run before the storing the first fixpoint.
|
58
58
|
# these test might have incremented the id sequence already, so the ids in the fixpoints chance (which leads to differences).
|
59
|
-
def reset_pk_sequences!
|
59
|
+
def reset_pk_sequences!(conn)
|
60
60
|
return unless conn.respond_to?(:reset_pk_sequence!)
|
61
61
|
conn.tables.each { |table_name| conn.reset_pk_sequence!(table_name) }
|
62
62
|
end
|
@@ -69,10 +69,6 @@ class Fixpoint
|
|
69
69
|
File.join(fspath, "#{fixname}.yml")
|
70
70
|
end
|
71
71
|
|
72
|
-
def conn
|
73
|
-
ActiveRecord::Base.connection
|
74
|
-
end
|
75
|
-
|
76
72
|
protected
|
77
73
|
|
78
74
|
def fixpoints_path
|
@@ -85,17 +81,21 @@ class Fixpoint
|
|
85
81
|
File.join(spec_path, FIXPOINT_FOLDER)
|
86
82
|
end
|
87
83
|
|
88
|
-
def read_database_records
|
84
|
+
def read_database_records(conn)
|
89
85
|
# adapted from: https://yizeng.me/2017/07/16/generate-rails-test-fixtures-yaml-from-database-dump/
|
90
86
|
tables = conn.tables
|
91
87
|
tables.reject! { |table_name| TABLES_TO_SKIP.include?(table_name) }
|
92
88
|
|
93
89
|
tables.each_with_object({}) do |table_name, acc|
|
94
|
-
result = conn.select_all("SELECT * FROM #{table_name}")
|
90
|
+
result = conn.select_all("SELECT * FROM #{conn.quote_table_name(table_name)}")
|
95
91
|
next if result.count.zero?
|
96
92
|
|
97
93
|
rows = result.to_a
|
98
94
|
rows.sort_by! { |row| row['id'] } if result.columns.include?('id') # let's make the order of items stable
|
95
|
+
# fix jsonb columns by re-parsing them, so they are not saved as string to the yaml file
|
96
|
+
jsonb_columns = result.column_types.select { |_, col_type| col_type.type == :jsonb }.collect { |col_name, _| col_name }
|
97
|
+
rows.collect! { |row| jsonb_columns.each {|jcol| row[jcol] = JSON.parse(row[jcol] || 'null') }; row }
|
98
|
+
|
99
99
|
acc[table_name] = rows
|
100
100
|
end
|
101
101
|
end
|
@@ -107,7 +107,7 @@ class Fixpoint
|
|
107
107
|
@records_in_tables = records_in_tables
|
108
108
|
end
|
109
109
|
|
110
|
-
def load_into_database
|
110
|
+
def load_into_database(conn)
|
111
111
|
# Here some more pointers on implementation details of fixtures:
|
112
112
|
# - https://github.com/rails/rails/blob/2998672fc22f0d5e1a79a29ccb60d0d0e627a430/activerecord/lib/active_record/fixtures.rb#L612
|
113
113
|
# - http://api.rubyonrails.org/v5.2.4/classes/ActiveRecord/FixtureSet.html#method-c-create_fixtures
|
@@ -123,7 +123,7 @@ class Fixpoint
|
|
123
123
|
|
124
124
|
# actually insert
|
125
125
|
conn.insert_fixtures_set(@records_in_tables)
|
126
|
-
self.class.reset_pk_sequences!
|
126
|
+
self.class.reset_pk_sequences!(conn)
|
127
127
|
end
|
128
128
|
|
129
129
|
def save_to_file(fixname)
|
@@ -146,8 +146,6 @@ class Fixpoint
|
|
146
146
|
|
147
147
|
protected
|
148
148
|
|
149
|
-
delegate :conn, to: :class
|
150
|
-
|
151
149
|
def contents_for_file
|
152
150
|
YAML.dump(@records_in_tables)
|
153
151
|
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# Helper methods to be included into RSpec
|
2
|
+
module FixpointTestHelpers
|
3
|
+
def restore_fixpoint(fixname, connection: default_connection)
|
4
|
+
@last_restored = fixname
|
5
|
+
IncrementalFixpoint.from_file(fixname).load_into_database(connection)
|
6
|
+
end
|
7
|
+
|
8
|
+
# Compares the fixpoint with the records in the database.
|
9
|
+
# If there is no such fixpoint yet, it will write a new one to the file system.
|
10
|
+
# The latter is useful if the fixpoint was deleted to accommodate changes to it (see example in class description).
|
11
|
+
#
|
12
|
+
# +tables_to_compare+ can either be +:all+ or a list of table names (e.g. ['users', 'posts'])
|
13
|
+
# +ignored_columns+ see Fixnum#records_for_table
|
14
|
+
# +store_fixpoint_and_fail+ when given and the fixpoint does not already exist, a new fixpoint is created an the test will be marked pending/failed
|
15
|
+
# +parent_fixname+ when storing a new fixpoint, use this as parent fixpoint (you can specify `:last_restored` then the last given to restore_fixpoint is used; not thread safe)
|
16
|
+
# ---
|
17
|
+
# If we refactor this to a gem, we should rely on rspec (e.g. use minitest or move comparison logic to Fixpoint class).
|
18
|
+
# Anyhow, we keep it like this for now, because the expectations give much nicer output than the minitest assertions.
|
19
|
+
def compare_fixpoint(fixname, ignored_columns=[:updated_at, :created_at], tables_to_compare: :all, store_fixpoint_and_fail: false, parent_fixname: nil, connection: default_connection)
|
20
|
+
if !IncrementalFixpoint.exists?(fixname)
|
21
|
+
if store_fixpoint_and_fail
|
22
|
+
store_fixpoint(fixname, parent_fixname, connection: connection)
|
23
|
+
pending("Fixpoint \"#{fixname}\" did not exist yet. Skipping comparison, but created fixpoint from database. Try re-running the test.")
|
24
|
+
fail
|
25
|
+
else
|
26
|
+
raise Fixpoint::Error, "Fixpoint #{fixname} does not exist"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
database_fp = IncrementalFixpoint.from_database(nil, connection)
|
31
|
+
fixpoint_fp = IncrementalFixpoint.from_file(fixname)
|
32
|
+
|
33
|
+
tables_to_compare = (database_fp.table_names + fixpoint_fp.table_names).uniq if tables_to_compare == :all
|
34
|
+
tables_to_compare.each do |table_name|
|
35
|
+
db_records = database_fp.records_for_table(table_name, ignored_columns)
|
36
|
+
fp_records = fixpoint_fp.records_for_table(table_name, ignored_columns)
|
37
|
+
|
38
|
+
# if a table is present in a fixpoint, there must be records in it because empty tables are stripped from fixpoints
|
39
|
+
expect(db_records).not_to be_empty, "#{table_name} not in database, but in fixpoint"
|
40
|
+
expect(fp_records).not_to be_empty, "#{table_name} not in fixpoint, but in database"
|
41
|
+
# we assume that the order of records returned by SELECT is stable (so we do not do any sorting)
|
42
|
+
expect(db_records).to eq(fp_records), "Database records for table \"#{table_name}\" did not match fixpoint \"#{fixname}\". Consider removing the fixpoint and re-running the test if the change is intended."
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# it is not a good idea to overwrite the fixpoint each time because timestamps may change (which then shows up in version control).
|
47
|
+
# Hence we only provide a method to write to it if it does not exist.
|
48
|
+
def store_fixpoint_unless_present(fixname, parent_fixname = nil, connection: default_connection)
|
49
|
+
store_fixpoint(fixname, parent_fixname, connection: connection) unless IncrementalFixpoint.exists?(fixname)
|
50
|
+
end
|
51
|
+
|
52
|
+
# +parent_fixname+ when given, only the (incremental) changes to the parent are saved
|
53
|
+
# please see store_fixpoint_unless_present for note on why not to use this method
|
54
|
+
def store_fixpoint(fixname, parent_fixname = nil, connection: default_connection)
|
55
|
+
parent_fixname = @last_restored if parent_fixname == :last_restored
|
56
|
+
IncrementalFixpoint.from_database(parent_fixname, connection).save_to_file(fixname)
|
57
|
+
end
|
58
|
+
|
59
|
+
private def default_connection
|
60
|
+
ActiveRecord::Base.connection
|
61
|
+
end
|
62
|
+
end
|
data/lib/fixpoints.rb
CHANGED
data/lib/fixpoints/version.rb
CHANGED
data/lib/incremental_fixpoint.rb
CHANGED
@@ -33,11 +33,11 @@ class IncrementalFixpoint < Fixpoint
|
|
33
33
|
end
|
34
34
|
|
35
35
|
# Creates a Fixpoint from the database contents. Empty tables are skipped.
|
36
|
-
def self.from_database(parent_fixname=nil)
|
37
|
-
return super() if parent_fixname.nil?
|
36
|
+
def self.from_database(parent_fixname=nil, conn)
|
37
|
+
return super(conn) if parent_fixname.nil?
|
38
38
|
|
39
39
|
parent = from_file(parent_fixname)
|
40
|
-
changes_in_tables = FixpointDiff.extract_changes(parent.records_in_tables, read_database_records)
|
40
|
+
changes_in_tables = FixpointDiff.extract_changes(parent.records_in_tables, read_database_records(conn))
|
41
41
|
new(changes_in_tables, parent_fixname)
|
42
42
|
end
|
43
43
|
|
@@ -49,61 +49,3 @@ class IncrementalFixpoint < Fixpoint
|
|
49
49
|
return YAML.dump(file_contents)
|
50
50
|
end
|
51
51
|
end
|
52
|
-
|
53
|
-
# Helper methods to be included into RSpec
|
54
|
-
module FixpointTestHelpers
|
55
|
-
def restore_fixpoint(fixname)
|
56
|
-
IncrementalFixpoint.from_file(fixname).load_into_database
|
57
|
-
end
|
58
|
-
|
59
|
-
# Compares the fixpoint with the records in the database.
|
60
|
-
# If there is no such fixpoint yet, it will write a new one to the file system.
|
61
|
-
# The latter is useful if the fixpoint was deleted to accommodate changes to it (see example in class description).
|
62
|
-
#
|
63
|
-
# +tables_to_compare+ can either be +:all+ or a list of table names (e.g. ['users', 'posts'])
|
64
|
-
# +ignored_columns+ see Fixnum#records_for_table
|
65
|
-
# +not_exists_handler+ when given and the fixpoint does not exists, it will be called with the fixname as argument
|
66
|
-
#
|
67
|
-
# ---
|
68
|
-
# If we refactor this to a gem, we should rely on rspec (e.g. use minitest or move comparison logic to Fixpoint class).
|
69
|
-
# Anyhow, we keep it like this for now, because the expectations give much nicer output than the minitest assertions.
|
70
|
-
def compare_fixpoint(fixname, ignored_columns=[:updated_at, :created_at], tables_to_compare=:all, ¬_exists_handler)
|
71
|
-
if !IncrementalFixpoint.exists?(fixname)
|
72
|
-
not_exists_handler.call(fixname) if not_exists_handler
|
73
|
-
return
|
74
|
-
end
|
75
|
-
|
76
|
-
database_fp = IncrementalFixpoint.from_database
|
77
|
-
fixpoint_fp = IncrementalFixpoint.from_file(fixname)
|
78
|
-
|
79
|
-
tables_to_compare = (database_fp.table_names + fixpoint_fp.table_names).uniq if tables_to_compare == :all
|
80
|
-
tables_to_compare.each do |table_name|
|
81
|
-
db_records = database_fp.records_for_table(table_name, ignored_columns)
|
82
|
-
fp_records = fixpoint_fp.records_for_table(table_name, ignored_columns)
|
83
|
-
|
84
|
-
# if a table is present in a fixpoint, there must be records in it because empty tables are stripped from fixpoints
|
85
|
-
expect(db_records).not_to be_empty, "#{table_name} not in database, but in fixpoint"
|
86
|
-
expect(fp_records).not_to be_empty, "#{table_name} not in fixpoint, but in database"
|
87
|
-
# we assume that the order of records returned by SELECT is stable (so we do not do any sorting)
|
88
|
-
expect(db_records).to eq(fp_records), "Database records for table \"#{table_name}\" did not match fixpoint \"#{fixname}\". Consider removing the fixpoint and re-running the test if the change is intended."
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
def store_fixpoint_and_fail(fixname, parent_fixname = nil)
|
93
|
-
store_fixpoint(fixname, parent_fixname)
|
94
|
-
pending("Fixpoint \"#{fixname}\" did not exist yet. Skipping comparison, but created fixpoint from database")
|
95
|
-
fail
|
96
|
-
end
|
97
|
-
|
98
|
-
# it is not a good idea to overwrite the fixpoint each time because timestamps may change (which then shows up in version control).
|
99
|
-
# Hence we only provide a method to write to it if it does not exist.
|
100
|
-
def store_fixpoint_unless_present(fixname, parent_fixname = nil)
|
101
|
-
store_fixpoint(fixname, parent_fixname) unless IncrementalFixpoint.exists?(fixname)
|
102
|
-
end
|
103
|
-
|
104
|
-
# +parent_fixname+ when given, only the (incremental) changes to the parent are saved
|
105
|
-
# please see store_fixpoint_unless_present for note on why not to use this method
|
106
|
-
def store_fixpoint(fixname, parent_fixname = nil)
|
107
|
-
IncrementalFixpoint.from_database(parent_fixname).save_to_file(fixname)
|
108
|
-
end
|
109
|
-
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fixpoints
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Rothe
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-10-07 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -55,6 +55,7 @@ files:
|
|
55
55
|
- fixpoints.gemspec
|
56
56
|
- lib/fixpoint.rb
|
57
57
|
- lib/fixpoint_diff.rb
|
58
|
+
- lib/fixpoint_test_helpers.rb
|
58
59
|
- lib/fixpoints.rb
|
59
60
|
- lib/fixpoints/version.rb
|
60
61
|
- lib/incremental_fixpoint.rb
|