mockley_crew 1.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 +7 -0
- data/MIT-LICENSE +20 -0
- data/README.md +28 -0
- data/Rakefile +22 -0
- data/app/controllers/mockley_crew/application_controller.rb +4 -0
- data/app/controllers/mockley_crew/database/data_controller.rb +26 -0
- data/app/controllers/mockley_crew/database_controller.rb +22 -0
- data/app/jobs/mockley_crew/application_job.rb +4 -0
- data/app/mailers/mockley_crew/application_mailer.rb +6 -0
- data/app/models/mockley_crew/application_record.rb +5 -0
- data/config/initializers/mockley_crew.rb +15 -0
- data/config/initializers/monkey_patch.rb +81 -0
- data/config/routes.rb +5 -0
- data/lib/mockley_crew.rb +52 -0
- data/lib/mockley_crew/configuration.rb +37 -0
- data/lib/mockley_crew/data.rb +45 -0
- data/lib/mockley_crew/database.rb +183 -0
- data/lib/mockley_crew/engine.rb +11 -0
- data/lib/mockley_crew/errors/connection_not_made.rb +6 -0
- data/lib/mockley_crew/errors/database_not_found.rb +6 -0
- data/lib/mockley_crew/errors/database_with_no_name.rb +6 -0
- data/lib/mockley_crew/errors/invalid_amount.rb +6 -0
- data/lib/mockley_crew/errors/invalid_data.rb +6 -0
- data/lib/mockley_crew/errors/invalid_factory.rb +6 -0
- data/lib/mockley_crew/factory_builder.rb +41 -0
- data/lib/mockley_crew/mockley_crew_handled.rb +40 -0
- data/lib/mockley_crew/version.rb +3 -0
- data/lib/tasks/mockley_crew.rake +21 -0
- data/lib/tasks/mockley_crew_tasks.rake +21 -0
- metadata +158 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 993536215b5d2e4fe3a215e7b791fe4f7d270d88d4cabe27503ce96523baba24
|
4
|
+
data.tar.gz: 3374d188f2a337e4346885e8a1e842ee5d7ef765aa9d03b27331f038bf0f2be4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f7d9014166b658fa57d7b32385f88bd064d709a58b0844d3886e76910727fd0966e3890ed61c096b514cd9222cb25cf5f97dd2f773e26f729c015b6158fcca53
|
7
|
+
data.tar.gz: ebbb9d82dd74ace41203af0c67a8c81cae734beffa5e6d7d1488a900276852184cfeb65fecd2eff1b819607c7ec7aff29594bf01f65a87d2a3e20b9ab568d9dd
|
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2018 Ariel Schvartz
|
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,28 @@
|
|
1
|
+
# MockleyCrew
|
2
|
+
Short description and motivation.
|
3
|
+
|
4
|
+
## Usage
|
5
|
+
How to use my plugin.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'mockley_crew'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
```bash
|
16
|
+
$ bundle
|
17
|
+
```
|
18
|
+
|
19
|
+
Or install it yourself as:
|
20
|
+
```bash
|
21
|
+
$ gem install mockley_crew
|
22
|
+
```
|
23
|
+
|
24
|
+
## Contributing
|
25
|
+
Contribution directions go here.
|
26
|
+
|
27
|
+
## License
|
28
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
begin
|
2
|
+
require 'bundler/setup'
|
3
|
+
rescue LoadError
|
4
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'rdoc/task'
|
8
|
+
|
9
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
10
|
+
rdoc.rdoc_dir = 'rdoc'
|
11
|
+
rdoc.title = 'MockleyCrew'
|
12
|
+
rdoc.options << '--line-numbers'
|
13
|
+
rdoc.rdoc_files.include('README.md')
|
14
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
15
|
+
end
|
16
|
+
|
17
|
+
APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
|
18
|
+
load 'rails/tasks/engine.rake'
|
19
|
+
|
20
|
+
load 'rails/tasks/statistics.rake'
|
21
|
+
|
22
|
+
require 'bundler/gem_tasks'
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class Database::DataController < ApplicationController
|
3
|
+
rescue_from MockleyCrew::Errors::InvalidDataError do |exception|
|
4
|
+
render json: { success: false, message: "Invalid body structure" }, status: 400
|
5
|
+
end
|
6
|
+
|
7
|
+
rescue_from MockleyCrew::Errors::InvalidFactoryError do |exception|
|
8
|
+
render json: { success: false, message: "Invalid Factory", detailed_error: exception }, status: 400
|
9
|
+
end
|
10
|
+
|
11
|
+
def create
|
12
|
+
data = MockleyCrew::Data.new(data_params.to_h["_json"])
|
13
|
+
if data.save
|
14
|
+
render json: { success: true }, status: 201
|
15
|
+
else
|
16
|
+
render json: { success: false, request: data.as_json }, status: 422
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def data_params
|
23
|
+
params.permit!
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class DatabaseController < ApplicationController
|
3
|
+
skip_before_action :activate_database
|
4
|
+
skip_after_action :restore_database
|
5
|
+
skip_after_action :set_response_header
|
6
|
+
after_action :set_response_header, only: [:create]
|
7
|
+
|
8
|
+
def create
|
9
|
+
@database = MockleyCrew::Database.create
|
10
|
+
render json: { success: true, database: { name: @database.filename } }, status: 201
|
11
|
+
end
|
12
|
+
|
13
|
+
def destroy
|
14
|
+
if @database
|
15
|
+
@database.destroy
|
16
|
+
render json: { success: true }, status: 200
|
17
|
+
else
|
18
|
+
invalid_badge
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
if defined?(ActionController::Base)
|
2
|
+
ActionController::Base.include(MockleyCrew::MockleyCrewHandled)
|
3
|
+
end
|
4
|
+
|
5
|
+
if defined?(ActionController::API)
|
6
|
+
ActionController::API.include(MockleyCrew::MockleyCrewHandled)
|
7
|
+
end
|
8
|
+
|
9
|
+
if defined?(DeviseController)
|
10
|
+
DeviseController.include(MockleyCrew::MockleyCrewHandled)
|
11
|
+
end
|
12
|
+
|
13
|
+
if defined?(DeviseTokenAuth)
|
14
|
+
DeviseTokenAuth::ApplicationController.include(MockleyCrew::MockleyCrewHandled)
|
15
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module ConnectionAdapters
|
3
|
+
class ConnectionHandler
|
4
|
+
def establish_connection(config, opts = {})
|
5
|
+
resolver = ConnectionSpecification::Resolver.new(Base.configurations)
|
6
|
+
spec = resolver.spec(config)
|
7
|
+
|
8
|
+
remove_connection(spec.name)
|
9
|
+
|
10
|
+
message_bus = ActiveSupport::Notifications.instrumenter
|
11
|
+
payload = {
|
12
|
+
connection_id: object_id
|
13
|
+
}
|
14
|
+
if spec
|
15
|
+
payload[:spec_name] = spec.name
|
16
|
+
payload[:config] = spec.config
|
17
|
+
end
|
18
|
+
|
19
|
+
message_bus.instrument("!connection.active_record", payload) do
|
20
|
+
owner_to_pool[spec.name] = ConnectionAdapters::ConnectionPool.new(spec, opts)
|
21
|
+
end
|
22
|
+
|
23
|
+
owner_to_pool[spec.name]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class ConnectionPool
|
28
|
+
def initialize(spec, opts = {})
|
29
|
+
super()
|
30
|
+
|
31
|
+
@spec = spec
|
32
|
+
|
33
|
+
@checkout_timeout = (spec.config[:checkout_timeout] && spec.config[:checkout_timeout].to_f) || 5
|
34
|
+
if @idle_timeout = spec.config.fetch(:idle_timeout, 300)
|
35
|
+
@idle_timeout = @idle_timeout.to_f
|
36
|
+
@idle_timeout = nil if @idle_timeout <= 0
|
37
|
+
end
|
38
|
+
|
39
|
+
# default max pool size to 5
|
40
|
+
@size = (spec.config[:pool] && spec.config[:pool].to_i) || 5
|
41
|
+
|
42
|
+
# This variable tracks the cache of threads mapped to reserved connections, with the
|
43
|
+
# sole purpose of speeding up the +connection+ method. It is not the authoritative
|
44
|
+
# registry of which thread owns which connection. Connection ownership is tracked by
|
45
|
+
# the +connection.owner+ attr on each +connection+ instance.
|
46
|
+
# The invariant works like this: if there is mapping of <tt>thread => conn</tt>,
|
47
|
+
# then that +thread+ does indeed own that +conn+. However, an absence of a such
|
48
|
+
# mapping does not mean that the +thread+ doesn't own the said connection. In
|
49
|
+
# that case +conn.owner+ attr should be consulted.
|
50
|
+
# Access and modification of <tt>@thread_cached_conns</tt> does not require
|
51
|
+
# synchronization.
|
52
|
+
@thread_cached_conns = Concurrent::Map.new(initial_capacity: @size)
|
53
|
+
|
54
|
+
@connections = []
|
55
|
+
@automatic_reconnect = true
|
56
|
+
|
57
|
+
# Connection pool allows for concurrent (outside the main +synchronize+ section)
|
58
|
+
# establishment of new connections. This variable tracks the number of threads
|
59
|
+
# currently in the process of independently establishing connections to the DB.
|
60
|
+
@now_connecting = 0
|
61
|
+
|
62
|
+
@threads_blocking_new_connections = 0
|
63
|
+
|
64
|
+
@available = ConnectionLeasingQueue.new self
|
65
|
+
|
66
|
+
@lock_thread = false
|
67
|
+
|
68
|
+
unless opts[:no_reaper] == true
|
69
|
+
# +reaping_frequency+ is configurable mostly for historical reasons, but it could
|
70
|
+
# also be useful if someone wants a very low +idle_timeout+.
|
71
|
+
reaping_frequency = spec.config.fetch(:reaping_frequency, 60)
|
72
|
+
@reaper = Reaper.new(self, reaping_frequency && reaping_frequency.to_f)
|
73
|
+
t = @reaper.run
|
74
|
+
if opts[:thread_name].present?
|
75
|
+
t["thread_name"] = opts[:thread_name]
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/config/routes.rb
ADDED
data/lib/mockley_crew.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require "factory_bot"
|
2
|
+
|
3
|
+
require "mockley_crew/engine"
|
4
|
+
require "mockley_crew/configuration"
|
5
|
+
|
6
|
+
require "mockley_crew/errors/connection_not_made"
|
7
|
+
require "mockley_crew/errors/database_not_found"
|
8
|
+
require "mockley_crew/errors/database_with_no_name"
|
9
|
+
require "mockley_crew/errors/invalid_data"
|
10
|
+
require "mockley_crew/errors/invalid_factory"
|
11
|
+
require "mockley_crew/errors/invalid_amount"
|
12
|
+
|
13
|
+
require "mockley_crew/database"
|
14
|
+
require "mockley_crew/factory_builder"
|
15
|
+
require "mockley_crew/data"
|
16
|
+
|
17
|
+
require "mockley_crew/mockley_crew_handled"
|
18
|
+
|
19
|
+
module MockleyCrew
|
20
|
+
class << self
|
21
|
+
attr_writer :configuration
|
22
|
+
attr_accessor :sqlite3_loaded
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.configuration
|
26
|
+
@configuration ||= Configuration.new
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.reset_configuration
|
30
|
+
@configuration = Configuration.new
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.configure
|
34
|
+
yield(configuration)
|
35
|
+
if configuration.heroku?
|
36
|
+
set_sqlite3
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.set_sqlite3
|
41
|
+
return if self.sqlite3_loaded == true
|
42
|
+
|
43
|
+
$: << "#{root}/vendor/gems/sqlite3/gems/sqlite3-1.3.13/lib/"
|
44
|
+
require 'sqlite3'
|
45
|
+
require 'active_record/connection_adapters/sqlite3_adapter'
|
46
|
+
self.sqlite3_loaded = true
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.root
|
50
|
+
File.expand_path '../..', __FILE__
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class Configuration
|
3
|
+
attr_accessor :crew_folder, :factories, :crew_header, :heroku
|
4
|
+
|
5
|
+
def initialize args = {}
|
6
|
+
@crew_header = args["crew_header"] || "crew-man-badge"
|
7
|
+
@crew_folder = args["crew_folder"] || "#{Rails.root}/db/crew"
|
8
|
+
@heroku = args["heroku"] || false
|
9
|
+
end
|
10
|
+
|
11
|
+
def default_database_path
|
12
|
+
"#{@crew_folder}/default_database.db"
|
13
|
+
end
|
14
|
+
|
15
|
+
def database_files_path
|
16
|
+
"#{@crew_folder}/databases/"
|
17
|
+
end
|
18
|
+
|
19
|
+
def database_files
|
20
|
+
Dir["#{database_files_path}*.db"]
|
21
|
+
end
|
22
|
+
|
23
|
+
def database_codes
|
24
|
+
database_files.map do |filename|
|
25
|
+
File.basename(filename, ".db").split("_").last
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def registered_factory? factory_name
|
30
|
+
FactoryBot.factories.registered?(factory_name)
|
31
|
+
end
|
32
|
+
|
33
|
+
def heroku?
|
34
|
+
@heroku == true
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class Data
|
3
|
+
attr_accessor :builders
|
4
|
+
|
5
|
+
def initialize params = []
|
6
|
+
raise Errors::InvalidDataError unless params.is_a? Array
|
7
|
+
|
8
|
+
params.each do |p|
|
9
|
+
raise Errors::InvalidDataError unless p.is_a? Hash
|
10
|
+
|
11
|
+
builders.push FactoryBuilder.new(p)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def builders
|
16
|
+
@builders ||= []
|
17
|
+
end
|
18
|
+
|
19
|
+
def save
|
20
|
+
success = true
|
21
|
+
ActiveRecord::Base.transaction do
|
22
|
+
@builders.each do |b|
|
23
|
+
unless b.save
|
24
|
+
success = false
|
25
|
+
end
|
26
|
+
end
|
27
|
+
raise ActiveRecord::Rollback unless success
|
28
|
+
end
|
29
|
+
return success
|
30
|
+
end
|
31
|
+
|
32
|
+
def as_json
|
33
|
+
@builders.map do |b|
|
34
|
+
h = {
|
35
|
+
"factory" => b.factory,
|
36
|
+
"options" => b.options
|
37
|
+
}
|
38
|
+
unless b.errors.blank?
|
39
|
+
h["errors"] = b.errors
|
40
|
+
end
|
41
|
+
h
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,183 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class Database
|
3
|
+
class << self
|
4
|
+
def connect args = {}
|
5
|
+
if args["filename"].present?
|
6
|
+
args["database"] = "#{MockleyCrew.configuration.database_files_path}#{args["filename"]}"
|
7
|
+
end
|
8
|
+
|
9
|
+
self.commit_transactions
|
10
|
+
|
11
|
+
ActiveRecord::Base.connection_handler.establish_connection({
|
12
|
+
adapter: "sqlite3",
|
13
|
+
database: args["database"],
|
14
|
+
host: args["host"] || File.basename(args["database"], ".*"),
|
15
|
+
username: "crew_master",
|
16
|
+
password: "crew_man"
|
17
|
+
}, {
|
18
|
+
thread_name: args["thread_name"] || "mockley_crew_default"
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
def migrate
|
23
|
+
ActiveRecord::Migration.verbose = false
|
24
|
+
ActiveRecord::Base.connection.migration_context.migrate
|
25
|
+
ActiveRecord::Migration.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
def disconnect
|
29
|
+
self.commit_transactions
|
30
|
+
|
31
|
+
if ActiveRecord::Base.connected?
|
32
|
+
ActiveRecord::Base.connection.close
|
33
|
+
ActiveRecord::Base.connection.disconnect!
|
34
|
+
|
35
|
+
self.restore_default_connection
|
36
|
+
else
|
37
|
+
raise "Cannot disconnect. You are already disconnected from the database."
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def restore_default_connection
|
42
|
+
ActiveRecord::Base.connection_handler.establish_connection(
|
43
|
+
ActiveRecord::Base.configurations[Rails.env], no_reaper: true
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
def commit_transactions
|
48
|
+
if ActiveRecord::Base.connected?
|
49
|
+
if ActiveRecord::Base.connection.transaction_open?
|
50
|
+
ActiveRecord::Base.connection.commit_transaction
|
51
|
+
end
|
52
|
+
else
|
53
|
+
raise "Cannot commit transactions. You are disconnected from the database."
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def terminate_thread filename
|
58
|
+
thread = Thread.list.select { |t| t["thread_name"] == filename }.first
|
59
|
+
thread.kill if thread
|
60
|
+
end
|
61
|
+
|
62
|
+
def create_default_database
|
63
|
+
unless File.exists?(MockleyCrew.configuration.default_database_path)
|
64
|
+
Database.connect("database" => MockleyCrew.configuration.default_database_path)
|
65
|
+
Database.migrate
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def delete_default_databse
|
70
|
+
if File.exists?(MockleyCrew.configuration.default_database_path)
|
71
|
+
File.delete(MockleyCrew.configuration.default_database_path)
|
72
|
+
terminate_thread "mockley_crew_default"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def reset_default_database
|
77
|
+
delete_default_databse
|
78
|
+
create_default_database
|
79
|
+
end
|
80
|
+
|
81
|
+
def remove_file_by_full_filename filename
|
82
|
+
File.delete(filename)
|
83
|
+
end
|
84
|
+
|
85
|
+
def remove_file_by_filename filename
|
86
|
+
self.remove_file_by_full_filename(MockleyCrew.configuration.database_files_path + filename)
|
87
|
+
end
|
88
|
+
|
89
|
+
def clean_database_files seconds = 60
|
90
|
+
MockleyCrew.configuration.database_files.each do |filename|
|
91
|
+
filename_parts = File.basename(filename, ".db").split("_");
|
92
|
+
if Time.zone.now.to_i - filename_parts.first.to_i >= seconds or filename_parts.length < 2
|
93
|
+
self.remove_file_by_full_filename(filename)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
def next_name
|
99
|
+
"#{Time.zone.now.to_i}_#{generate_unique_code}.db"
|
100
|
+
end
|
101
|
+
|
102
|
+
def generate_unique_code
|
103
|
+
loop do
|
104
|
+
code = SecureRandom.hex(10)
|
105
|
+
break code unless MockleyCrew.configuration.database_codes.include?(code)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def find_by_filename filename
|
110
|
+
db = self.new(filename: filename)
|
111
|
+
if db.exists?
|
112
|
+
return db
|
113
|
+
else
|
114
|
+
return nil
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def create
|
119
|
+
db = self.new
|
120
|
+
db.save
|
121
|
+
db
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
attr_accessor :filename
|
126
|
+
|
127
|
+
def initialize args = {}
|
128
|
+
args.each do |k, v|
|
129
|
+
self.send("#{k}=".to_sym, v)
|
130
|
+
end
|
131
|
+
|
132
|
+
unless self.filename.present?
|
133
|
+
self.filename = self.class.next_name
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def exists?
|
138
|
+
File.exists?(full_file_path)
|
139
|
+
end
|
140
|
+
|
141
|
+
def get_name
|
142
|
+
File.basename(@filename, ".*").split("_").last
|
143
|
+
end
|
144
|
+
|
145
|
+
def get_created_at
|
146
|
+
Time.at(File.basename(@filename, ".*").split("_").first.to_i)
|
147
|
+
end
|
148
|
+
|
149
|
+
def save
|
150
|
+
self.create_database_file unless File.exists?(full_file_path)
|
151
|
+
end
|
152
|
+
|
153
|
+
def full_file_path
|
154
|
+
"#{MockleyCrew.configuration.database_files_path}#{@filename}"
|
155
|
+
end
|
156
|
+
|
157
|
+
def create_database_file
|
158
|
+
self.class.create_default_database unless File.exists?(MockleyCrew.configuration.default_database_path)
|
159
|
+
|
160
|
+
FileUtils.mkdir_p(File.dirname(full_file_path))
|
161
|
+
FileUtils.cp MockleyCrew.configuration.default_database_path, full_file_path
|
162
|
+
end
|
163
|
+
|
164
|
+
def connect
|
165
|
+
self.class.connect(
|
166
|
+
"thread_name" => @filename,
|
167
|
+
"filename" => @filename
|
168
|
+
)
|
169
|
+
end
|
170
|
+
alias_method :on, :connect
|
171
|
+
|
172
|
+
def disconnect
|
173
|
+
self.class.disconnect
|
174
|
+
end
|
175
|
+
alias_method :off, :disconnect
|
176
|
+
|
177
|
+
def destroy
|
178
|
+
self.disconnect
|
179
|
+
self.class.remove_file_by_filename(self.filename)
|
180
|
+
self.class.terminate_thread(self.filename)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class Engine < ::Rails::Engine
|
3
|
+
isolate_namespace MockleyCrew
|
4
|
+
# config.generators.api_only = true
|
5
|
+
|
6
|
+
config.generators do |g|
|
7
|
+
g.test_framework :rspec
|
8
|
+
# g.fixture_replacement :factory_girl, dir: 'spec/factories'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module MockleyCrew
|
2
|
+
class FactoryBuilder
|
3
|
+
attr_accessor :factory, :options, :errors
|
4
|
+
|
5
|
+
def initialize params = {}
|
6
|
+
raise Errors::InvalidDataError unless params.is_a? Hash
|
7
|
+
raise Errors::InvalidDataError unless params.keys.include?("factory")
|
8
|
+
|
9
|
+
@factory = params["factory"]
|
10
|
+
unless MockleyCrew.configuration.registered_factory?(@factory.to_sym)
|
11
|
+
@factory = @factory.singularize
|
12
|
+
unless MockleyCrew.configuration.registered_factory?(@factory.to_sym)
|
13
|
+
raise Errors::InvalidFactoryError
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
params["options"] ||= {}
|
18
|
+
params["options"].reverse_merge!(
|
19
|
+
"amount" => 1,
|
20
|
+
"attributes" => {}
|
21
|
+
)
|
22
|
+
@options = params["options"]
|
23
|
+
|
24
|
+
raise Errors::InvalidAmountError unless @options["amount"].to_i > 0
|
25
|
+
|
26
|
+
@errors = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def save
|
30
|
+
begin
|
31
|
+
FactoryBot.create_list(@factory.to_sym, @options["amount"].to_i, @options["attributes"])
|
32
|
+
return true
|
33
|
+
rescue NoMethodError => e
|
34
|
+
self.errors[:attributes] = { message: "Invalid Attributes", detailed_error: e }
|
35
|
+
rescue ActiveRecord::RecordInvalid => e
|
36
|
+
self.errors[:attributes] = { message: "Model Validation Error", detailed_error: e }
|
37
|
+
end
|
38
|
+
return false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module MockleyCrew::MockleyCrewHandled
|
2
|
+
extend ActiveSupport::Concern
|
3
|
+
|
4
|
+
included do
|
5
|
+
prepend_before_action :activate_database, if: :mockley_crew_header_present?
|
6
|
+
prepend_before_action :set_database, if: :mockley_crew_header_present?
|
7
|
+
append_after_action :restore_database, if: :mockley_crew_header_present?
|
8
|
+
append_after_action :set_response_header, if: :mockley_crew_header_present?
|
9
|
+
|
10
|
+
rescue_from MockleyCrew::Errors::DatabaseNotFoundError, with: :invalid_badge
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def mockley_crew_header_present?
|
16
|
+
request.headers[MockleyCrew.configuration.crew_header].present?
|
17
|
+
end
|
18
|
+
|
19
|
+
def activate_database
|
20
|
+
@database.on
|
21
|
+
end
|
22
|
+
|
23
|
+
def restore_database
|
24
|
+
@database.off
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_database
|
28
|
+
@database_name = request.headers[MockleyCrew.configuration.crew_header]
|
29
|
+
@database = MockleyCrew::Database.find_by_filename(@database_name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def set_response_header
|
33
|
+
return unless @database
|
34
|
+
response.set_header(MockleyCrew.configuration.crew_header, @database.filename)
|
35
|
+
end
|
36
|
+
|
37
|
+
def invalid_badge
|
38
|
+
render json: { success: false, error: "Invalid badge" }, status: 403
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
namespace :mockley_crew do
|
2
|
+
desc "Clear the mockley crew created database files"
|
3
|
+
task :clear do
|
4
|
+
MockleyCrew::Database.clean_database_files(0)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# require 'thor'
|
9
|
+
# require 'mockley_crew'
|
10
|
+
|
11
|
+
# module MockleyCrew
|
12
|
+
# class CLI < Thor
|
13
|
+
|
14
|
+
# desc "clear", "Clear the mockley crew SQLite3 created database files"
|
15
|
+
# def clear
|
16
|
+
# MockleyCrew.
|
17
|
+
# puts "Clearing"
|
18
|
+
# end
|
19
|
+
|
20
|
+
# end
|
21
|
+
# end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
namespace :mockley_crew do
|
2
|
+
desc "Clear the mockley crew created database files"
|
3
|
+
task :clear do
|
4
|
+
puts "Clearing"
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# require 'thor'
|
9
|
+
# require 'mockley_crew'
|
10
|
+
|
11
|
+
# module MockleyCrew
|
12
|
+
# class CLI < Thor
|
13
|
+
|
14
|
+
# desc "clear", "Clear the mockley crew SQLite3 created database files"
|
15
|
+
# def clear
|
16
|
+
# MockleyCrew.
|
17
|
+
# puts "Clearing"
|
18
|
+
# end
|
19
|
+
|
20
|
+
# end
|
21
|
+
# end
|
metadata
ADDED
@@ -0,0 +1,158 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mockley_crew
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ariel Schvartz
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-25 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rails
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 5.2.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 5.2.0
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: factory_bot_rails
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: sqlite3
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry-rails
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: timecop
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rspec-rails
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '3.2'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '3.2'
|
97
|
+
description: With Mockley Crew, you can create fake databases to use from your API
|
98
|
+
consumer. This way, you can isolate the consumer tests by having multiple data scenarios
|
99
|
+
built on your API.
|
100
|
+
email:
|
101
|
+
- ari.shh@gmail.com
|
102
|
+
executables: []
|
103
|
+
extensions: []
|
104
|
+
extra_rdoc_files: []
|
105
|
+
files:
|
106
|
+
- MIT-LICENSE
|
107
|
+
- README.md
|
108
|
+
- Rakefile
|
109
|
+
- app/controllers/mockley_crew/application_controller.rb
|
110
|
+
- app/controllers/mockley_crew/database/data_controller.rb
|
111
|
+
- app/controllers/mockley_crew/database_controller.rb
|
112
|
+
- app/jobs/mockley_crew/application_job.rb
|
113
|
+
- app/mailers/mockley_crew/application_mailer.rb
|
114
|
+
- app/models/mockley_crew/application_record.rb
|
115
|
+
- config/initializers/mockley_crew.rb
|
116
|
+
- config/initializers/monkey_patch.rb
|
117
|
+
- config/routes.rb
|
118
|
+
- lib/mockley_crew.rb
|
119
|
+
- lib/mockley_crew/configuration.rb
|
120
|
+
- lib/mockley_crew/data.rb
|
121
|
+
- lib/mockley_crew/database.rb
|
122
|
+
- lib/mockley_crew/engine.rb
|
123
|
+
- lib/mockley_crew/errors/connection_not_made.rb
|
124
|
+
- lib/mockley_crew/errors/database_not_found.rb
|
125
|
+
- lib/mockley_crew/errors/database_with_no_name.rb
|
126
|
+
- lib/mockley_crew/errors/invalid_amount.rb
|
127
|
+
- lib/mockley_crew/errors/invalid_data.rb
|
128
|
+
- lib/mockley_crew/errors/invalid_factory.rb
|
129
|
+
- lib/mockley_crew/factory_builder.rb
|
130
|
+
- lib/mockley_crew/mockley_crew_handled.rb
|
131
|
+
- lib/mockley_crew/version.rb
|
132
|
+
- lib/tasks/mockley_crew.rake
|
133
|
+
- lib/tasks/mockley_crew_tasks.rake
|
134
|
+
homepage: https://github.com/arielschvartz/mockley_crew
|
135
|
+
licenses:
|
136
|
+
- MIT
|
137
|
+
metadata: {}
|
138
|
+
post_install_message:
|
139
|
+
rdoc_options: []
|
140
|
+
require_paths:
|
141
|
+
- lib
|
142
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
143
|
+
requirements:
|
144
|
+
- - ">="
|
145
|
+
- !ruby/object:Gem::Version
|
146
|
+
version: '0'
|
147
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
requirements: []
|
153
|
+
rubyforge_project:
|
154
|
+
rubygems_version: 2.7.7
|
155
|
+
signing_key:
|
156
|
+
specification_version: 4
|
157
|
+
summary: Rock your API integration tests by mocking data into multiple SQLite3 instances
|
158
|
+
test_files: []
|