create_update_destroy_async 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2bab654a7b0e40f7cff90fcd7d988f9d56c9b2368f2a40cd7eed358746dce7a0
4
+ data.tar.gz: 96f07c4dc56362f8fd9214a0674315cd9794588d63534ec85ad5f1be20dc6e24
5
+ SHA512:
6
+ metadata.gz: efe9bfc96f4b39171484e19b678f79652837ba56a0ca08b0defe60bc2fce14fc8a1c7daa22cf55b082912037331e4fbf873dd869a5fa7b448ed151a8ea181e49
7
+ data.tar.gz: 6bf428544ebb7f0e0d760ded274450fc56409fd83b2c4a0adde210b425882495833a4ec139a40656f3c24336851d3543d4f4b477b5930765ef8ff1af697476a4
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2023 Igor Kasyanchuk
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,139 @@
1
+ # Create/Update/Destroy + Async
2
+
3
+ [![RailsJazz](https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/my_other.svg?raw=true)](https://www.railsjazz.com)
4
+ [![Listed on OpenSource-Heroes.com](https://opensource-heroes.com/badge-v1.svg)](https://opensource-heroes.com/o/railsjazz)
5
+
6
+ A simple benchmark to grab your attention. Performance difference is noticeable.
7
+ ![Performance](docs/benchmark_example.png)
8
+
9
+ A very simple solution for a very simple but annoying problem.
10
+ An example of problem that this gem solves.
11
+
12
+ ```ruby
13
+ #
14
+ # code that can be improved
15
+ #
16
+ class EventsAPIController < AppController
17
+ def show
18
+ @event = Event.find(params[:id])
19
+
20
+ # scroll down to see how we can improve this line
21
+ EventView.create(user: current_user)
22
+
23
+ render json: @event
24
+ end
25
+ end
26
+ ```
27
+
28
+ How code above can be improved (and by improved I mean faster)?
29
+ This API endpoint is responsible to return JSON info about Event.
30
+
31
+ What in this code can slowdown this API?
32
+ Probably this: `EventView.create(user: current_user)`? This is the only one write operation that we have.
33
+
34
+ Now, imagine that in our app we have background jobs. We can start a new background job to create event in async mode.
35
+ But what could be even simpler? Correct answer is to use this gem :)
36
+ With help of this gem you can rewrite your code to this:
37
+
38
+ ```ruby
39
+ #
40
+ # improved version, it will work 100% faster
41
+ #
42
+ class EventsAPIController < AppController
43
+ def show
44
+ @event = Event.find(params[:id])
45
+
46
+ EventView.create_async(user: current_user) # <--- here is a change
47
+
48
+ render json: @event
49
+ end
50
+ end
51
+ ```
52
+
53
+ When we call `.create_async`, it will start a new background job and create new record in the background.
54
+
55
+ ## Usage
56
+
57
+ You have methods like:
58
+
59
+ - `.create_async`
60
+ - `.save_async`
61
+ - `.update_async`
62
+ - `.destroy_async`
63
+
64
+ ## Examples
65
+
66
+ ```ruby
67
+
68
+ # create
69
+ User.create_async(first_name: "John", last_name: "Smith")
70
+
71
+ # update
72
+ @user.update_async(first_name: "New Name")
73
+
74
+ # save
75
+ @user.first_name = "New Name"
76
+ @user.save_async
77
+
78
+ # destroy
79
+ @user.destroy_async
80
+ ```
81
+
82
+ Ideal use case - when you just need to do an atomic action that won't require any logic with object.
83
+
84
+ ## Limitations:
85
+
86
+ - you need to have some background job service in your app (as far I remember Sidekiq can be started within your app)
87
+ - maybe it won't work with complex associations that are changed in the object (not tested)
88
+ - validations will happen in the background, so you cannot write code like:
89
+ ```ruby
90
+ if @user.save_async
91
+ ... # this won't work as expected
92
+ else
93
+ ... # sorry validation happens only in the background
94
+ end
95
+ ```
96
+ - you cannot refer variables in your code, because they will be created in the background. Example:
97
+ ```ruby
98
+ @user = User.create_async(attrs)
99
+ puts @user.full_name # this code won't work
100
+ ```
101
+
102
+ ## Installation
103
+
104
+ Add this line to your application's Gemfile:
105
+
106
+ ```ruby
107
+ gem "create_update_destroy_async"
108
+ ```
109
+
110
+ And then execute:
111
+ ```bash
112
+ $ bundle
113
+ ```
114
+
115
+ ## TODO
116
+
117
+ - how to set queue where it will be executed
118
+ - verify if it works on not with more complext objects, like accept nested attributes
119
+ - options for jobs, like discard_on, priority, etc
120
+
121
+ ## Benchmark
122
+
123
+ - `rails s -e production`
124
+ - `wrk -c100 http://127.0.0.1:3000/home/index1`
125
+ - stop server
126
+ - `rails s -e production`
127
+ - `wrk -c100 http://127.0.0.1:3000/home/index2`
128
+ - repeat few times to be 100% sure
129
+
130
+ ## Contributing
131
+
132
+ You are welcome to contribute.
133
+
134
+ ## License
135
+
136
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
137
+
138
+ [<img src="https://github.com/igorkasyanchuk/rails_time_travel/blob/main/docs/more_gems.png?raw=true"
139
+ />](https://www.railsjazz.com/?utm_source=github&utm_medium=bottom&utm_campaign=create_update_destroy_async)
data/Rakefile ADDED
@@ -0,0 +1,3 @@
1
+ require "bundler/setup"
2
+
3
+ require "bundler/gem_tasks"
@@ -0,0 +1,26 @@
1
+ module CreateUpdateDestroyAsync
2
+ module ActiveRecordExt
3
+ extend ActiveSupport::Concern
4
+
5
+ class_methods do
6
+ def create_async(attributes = {})
7
+ CreateUpdateDestroyAsync::Jobs::Create.perform_later(self.name, attributes)
8
+ end
9
+ end
10
+
11
+ def save_async
12
+ new_attributes = changes.each_with_object({}) do |(k, v), hash|
13
+ hash[k] = v[1]
14
+ end
15
+ CreateUpdateDestroyAsync::Jobs::Save.perform_later(self, new_attributes)
16
+ end
17
+
18
+ def update_async(new_attributes = {})
19
+ CreateUpdateDestroyAsync::Jobs::Update.perform_later(self, changed_attributes.merge(new_attributes))
20
+ end
21
+
22
+ def destroy_async
23
+ CreateUpdateDestroyAsync::Jobs::Destroy.perform_later(self)
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateUpdateDestroyAsync
4
+ module Jobs
5
+ class Create < ActiveJob::Base
6
+ def perform(klass, attributes = {})
7
+ model = klass.constantize
8
+ model.create(attributes)
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateUpdateDestroyAsync
4
+ module Jobs
5
+ class Destroy < ActiveJob::Base
6
+ def perform(record)
7
+ record.destroy
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateUpdateDestroyAsync
4
+ module Jobs
5
+ class Save < ActiveJob::Base
6
+ def perform(record, attributes = {})
7
+ record.assign_attributes(attributes)
8
+ record.save
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CreateUpdateDestroyAsync
4
+ module Jobs
5
+ class Update < ActiveJob::Base
6
+ def perform(record, attributes = {})
7
+ record.update(attributes)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,7 @@
1
+ module CreateUpdateDestroyAsync
2
+ class Railtie < ::Rails::Railtie
3
+ ActiveSupport.on_load(:active_record) do
4
+ ActiveRecord::Base.send(:include, CreateUpdateDestroyAsync::ActiveRecordExt)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module CreateUpdateDestroyAsync
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,10 @@
1
+ require "create_update_destroy_async/version"
2
+ require "create_update_destroy_async/jobs/create"
3
+ require "create_update_destroy_async/jobs/save"
4
+ require "create_update_destroy_async/jobs/update"
5
+ require "create_update_destroy_async/jobs/destroy"
6
+ require "create_update_destroy_async/active_record_ext"
7
+ require "create_update_destroy_async/railtie"
8
+
9
+ module CreateUpdateDestroyAsync
10
+ end
metadata ADDED
@@ -0,0 +1,114 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: create_update_destroy_async
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Igor Kasyanchuk
8
+ - Liubomyr Manastyretskyi
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2023-05-11 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rails
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ">="
19
+ - !ruby/object:Gem::Version
20
+ version: '0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: activejob
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: pry
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ">="
47
+ - !ruby/object:Gem::Version
48
+ version: '0'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ">="
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: sidekiq
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ">="
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ description: Execute create/update/destroy operations asynchronously in your Rails
71
+ app
72
+ email:
73
+ - igorkasyanchuk@gmail.com
74
+ - manastyretskyi@gmail.com
75
+ executables: []
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - MIT-LICENSE
80
+ - README.md
81
+ - Rakefile
82
+ - lib/create_update_destroy_async.rb
83
+ - lib/create_update_destroy_async/active_record_ext.rb
84
+ - lib/create_update_destroy_async/jobs/create.rb
85
+ - lib/create_update_destroy_async/jobs/destroy.rb
86
+ - lib/create_update_destroy_async/jobs/save.rb
87
+ - lib/create_update_destroy_async/jobs/update.rb
88
+ - lib/create_update_destroy_async/railtie.rb
89
+ - lib/create_update_destroy_async/version.rb
90
+ homepage: https://github.com/railsjazz/create_update_destroy_async
91
+ licenses:
92
+ - MIT
93
+ metadata:
94
+ homepage_uri: https://github.com/railsjazz/create_update_destroy_async
95
+ post_install_message:
96
+ rdoc_options: []
97
+ require_paths:
98
+ - lib
99
+ required_ruby_version: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ required_rubygems_version: !ruby/object:Gem::Requirement
105
+ requirements:
106
+ - - ">="
107
+ - !ruby/object:Gem::Version
108
+ version: '0'
109
+ requirements: []
110
+ rubygems_version: 3.3.7
111
+ signing_key:
112
+ specification_version: 4
113
+ summary: Execute create/update/destroy operations asynchronously in your Rails app
114
+ test_files: []