create_update_destroy_async 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []