https://github.com/sammyhenningsson/file_transactions
Commands that can be undone and combined in transactions
https://github.com/sammyhenningsson/file_transactions
Last synced: 7 months ago
JSON representation
Commands that can be undone and combined in transactions
- Host: GitHub
- URL: https://github.com/sammyhenningsson/file_transactions
- Owner: sammyhenningsson
- License: mit
- Created: 2020-06-16T20:03:03.000Z (over 5 years ago)
- Default Branch: master
- Last Pushed: 2021-09-30T06:33:19.000Z (about 4 years ago)
- Last Synced: 2025-02-28T21:06:45.937Z (8 months ago)
- Language: Ruby
- Size: 43.9 KB
- Stars: 17
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# FileTransactions
This gem makes it easy to wrap code in classes using the command pattern. This means that a specific operation/task can be executed and after that, it can be undone. Multiple commands can be grouped in a (non-database) transaction, that will undo all executed commands if an exception is raised.
This gem includes a few commands for simple file operations. (Note: in spite of the name and the included commands, this gem is not limited to only file operations)## Installation
Add this line to your application's Gemfile:
```ruby
gem 'file_transactions'
```And then execute:
$ bundle install
Or install it yourself as:
$ gem install file_transactions
## Usage
FileTransactions provides a few basic commands for file operations:
- `CreateFileCommand`
- `CreateDirectoryCommand`
- `ChangeFileCommand`
- `DeleteFileCommand`
- `MoveFileCommand`All commands inherit from `FileTransactions::BaseCommand`, which requires subclasses to implement `#execute!`, `#undo!` and `#initialize`
They may also implement `#before` and `#after` which will be executed before or after `#execute` gets called.
If the `#before` or `#after` methods includes any commands, those sub commands will be undone when the command gets undone.
Multiple commands can be grouped together in a transaction, which will make sure all commands will get executed or all of them will be undone if a rollback happens.## Examples
Create a new file:
```rubycmd = FileTransactions::CreateFileCommand.new('my_file') do
"some content that will be written to this new file"
endcmd.execute
File.exist? 'my_file' # true
cmd.undo
File.exist? 'my_file' # false
```If the block passed to `CreateFileCommand.new` returns a `String` then the file will be created with that string as the file content.
If you rather create the file yourself (for example to set some specific file permissions etc), then simply return anything but a `String` instance.
Just make sure to create a file with the same name (Note: the filename also gets passed to the block). So instead we can do:
```rubycmd = FileTransactions::CreateFileCommand.new('my_file') do |name|
File.open(name, 'w') do |f|
f.write 'some content that will be written to this new file'
end
end
```There is also a short version to create and call a command at once:
```rubycmd = FileTransactions::CreateFileCommand.execute('my_file') { 'some file content' }
```If you want to save some key strokes, you can require 'ft' instead of 'file_transactions' which makes `FT` and alias for `FileTransactions`:
```ruby
require 'ft'# Then simply type
cmd = FT::CreateFileCommad.new('foo') { 'bar }# Note FileTransactions will still be available
FT == FileTransactions # => true
```A transaction is simply a call to `FileTransactions.transaction` passing a block that contains commands:
```ruby
FT.transaction do
CreateDirectoryCommand.execute('some_directory')
MoveFileCommand.execute('some_file', 'some_directory/some_file')
end
```Commands are not limited to file operations and can be used for anything with side effects. By inheriting `FileTransactions::BaseCommand` commands get the required methods to be executed, undone and registered withing other commands and transactions. However you must yourself keep track of the side effects and what needs to be undone.
For example, say that you use an api for managing tasks, then some simple commands like this could be used:
```ruby
require 'file_transactions'class ClaimTask < FileTransactions::BaseCommand
attr_reader :taskdef initialize(task)
@task = task
endprivate
def execute!
response = task.post('claim-task')
raise unless (200..299).cover? response.http_status
enddef undo!
response = task.delete('assignee')
raise unless (200..299).cover? response.http_status
end
endclass UpdateTaskEstimate < FileTransactions::BaseCommand
attr_reader :task, :estimatedef initialize(task, estimate)
@task = task
@estimate = estimate
endprivate
def execute!
@previous_estimate = task.attribute(:estimated_time)
update(estimate)
enddef undo!
update(@previous_estimate)
enddef update(estimated_time)
form = task.get(:edit_form)
form[:estimated_time] = estimated_time
task = form.submit
raise unless (200..299).cover? task.http_status
end
end
```
Then this commands can now be put together in a transaction, that ensure that a task is claimed and given an estimate. If an exception is raised then the transaction will be rolled back and the task will be unassigned again.
```ruby
tasks = ShafClient.new('https://some-task-api.com/', user: 'john', password: 'doe')
.get_root
.get(:unassigned_tasks)task = select_suitable_task(tasks)
FT.transaction do
ClaimTask.execute(task)
estimated_time = rand(1..5) * 8
raise 'Too much work' if estimated_time > 30
UpdateTaskEstimate.execute(task, estimated_time)
end
```## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/sammyhenningsson/file_transactions.
## License
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).