distributable 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 12c155b9647754f6a436ba5ce5a74c28b4062513
4
+ data.tar.gz: e1c8881c0786a9e363b5e9056a607e378613def5
5
+ SHA512:
6
+ metadata.gz: 33fbf2d244ed7ff582880a430778c08f405ed92e07bde809e849193a279281aeaea09f1bc0d798b7d1f1cb2fd3533be6797749f11718f959630131c49021d408
7
+ data.tar.gz: 4335887220ce9d2f5fb576dc6f7867642f9cd308eda0866e646eb6a869fb88169d1c0ffa03e42fe40ded9ac6379b874d1f79cdab1ef65185ccd42ee2343ed9fd
@@ -0,0 +1,20 @@
1
+ Copyright 2018 Chris Liaw
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.
@@ -0,0 +1,66 @@
1
+ # What is Distributable?
2
+
3
+ Distributable gem is to assist in creating the framework for the data to be distributed across multiple nodes.
4
+
5
+ What it does at this version is to:
6
+ * Add 'identifier' field to the table structure. This field shall be populated with SecureRandom UUID during record creation and duplication is checked before commit. If you have already has the field defined then the field shall be used.
7
+ * Add change_logs table to keep track of which distributable table has changed (added/edited/deleted). These changed record is the target to be synced across to other database later.
8
+
9
+ ### Motivation
10
+ This comes when a distributed issue tracking system is being formulated to match the benefit of distributed issue tracker such as Git. Git has numerous value to developer in mobile, such as myself that could allow the source code to be distributed even without a central server. If the Git does not strictly need a central server, we are opted that issue tracker, which tied to the source of the code changed that is translated into the Git commit, should also be distributed to ensure the pace of the issue resolution is in tandem with the Git check ins.
11
+
12
+ ### Why not just used database that has distributed feature?
13
+ There are some RDBMS or NoSQL database do already have high grade distribution feature included such as PostgreSQL, Hadoop, CouchBase etc. However not all database has it. This mainly is to provide consistant distributable feature to the system that is being build without depending on database selection.
14
+
15
+ Also using this approach allow more fine grind control on data such that in a database, not all data would be suitable to be synced. One example would be local use access table. Local use access is meant to control which user has what access when the user is connecting to the system. That has no meaning or even harmful to other node as the access rights of a particular user in my node (Imagine you can push your changes to my laptop and your access rights does not mean the same with other team member who is a junior developer that consistantly breaks things).
16
+
17
+
18
+ ## Usage
19
+ This gem is designed and tested under Rails environment.
20
+
21
+ For a particular table that you wish to add to the distributable scope (here the sample table is DistributedRecord):
22
+ ```ruby
23
+ class DistributedRecord < AppicationRecord
24
+ distributable
25
+ end
26
+ ```
27
+ and you are done!
28
+
29
+ Upon record creation, a random SecureRandom UUID shall be inserted into the field identifier and changes would be recorded into the change_logs table created via command
30
+ ```bash
31
+ $ rake distributable:setup
32
+ ```
33
+
34
+ You can check if a model is marked as distributable by checking
35
+ ```ruby
36
+ @model.distributed?
37
+ ```
38
+
39
+ ## Installation
40
+ Add this line to your application's Gemfile:
41
+
42
+ ```ruby
43
+ gem 'distributable'
44
+ ```
45
+
46
+ And then execute:
47
+ ```bash
48
+ $ bundle
49
+ $ rake distributable:setup
50
+ ```
51
+ The rake command only required to execute once per project.
52
+
53
+ Or install it yourself as:
54
+ ```bash
55
+ $ gem install distributable
56
+ ```
57
+ If you install yourself, after included into the Gemfile, you still required to execute
58
+ ```bash
59
+ $ rake distributable:setup
60
+ ```
61
+
62
+ ## Contributing
63
+ You can contribute by forking the project. Just drop me a message so that I know who's
64
+
65
+ ## License
66
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,37 @@
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 = 'Distributable'
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("../test/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ require 'rake/testtask'
28
+
29
+ Rake::TestTask.new(:test) do |t|
30
+ t.libs << 'lib'
31
+ t.libs << 'test'
32
+ t.pattern = 'test/**/*_test.rb'
33
+ t.verbose = false
34
+ end
35
+
36
+
37
+ task default: :test
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/distributable .js
2
+ //= link_directory ../stylesheets/distributable .css
@@ -0,0 +1,13 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ //= require_tree .
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,5 @@
1
+ module Distributable
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,4 @@
1
+ module Distributable
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Distributable
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Distributable
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,5 @@
1
+ module Distributable
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Distributable</title>
5
+ <%= stylesheet_link_tag "distributable/application", media: "all" %>
6
+ <%= javascript_include_tag "distributable/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,2 @@
1
+ Distributable::Engine.routes.draw do
2
+ end
@@ -0,0 +1,105 @@
1
+ require "distributable/engine"
2
+ require 'securerandom'
3
+
4
+ module Distributable
5
+ module DistributedNode
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ end
10
+
11
+ module ClassMethods
12
+ #
13
+ # this install the logic and kick started the operation
14
+ #
15
+ def distributable(opts = {})
16
+ options = {
17
+ dist_key: :identifier,
18
+ skipped: %w(created_at updated_at created_by updated_by id),
19
+ change_log_table: "change_logs",
20
+ sync_log_table: "sync_ogs",
21
+ log: Logger.new(STDOUT)
22
+ }
23
+
24
+ options.merge!(opts)
25
+
26
+ self.primary_key = options[:dist_key].to_sym
27
+
28
+ before_create :generate_identifier
29
+
30
+ after_create :log_new
31
+ after_update :log_update
32
+ after_destroy :log_destroy
33
+
34
+ #validates options[:dist_key].to_sym, uniqueness: true
35
+ @@options = options
36
+ end
37
+
38
+ def options
39
+ @@options
40
+ end
41
+
42
+ end
43
+
44
+ # instance methods
45
+ def distributed?
46
+ true
47
+ end
48
+
49
+ def generate_identifier
50
+ if self.has_attribute?(:identifier)
51
+ while(true)
52
+ self.identifier = SecureRandom.uuid
53
+ cnt = eval("#{self.class.name}.where([\"identifier = ?\",'#{self.identifier}']).count")
54
+ break if cnt == 0
55
+ end
56
+ else
57
+ STDERR.puts "Identifier field does not exist in schema"
58
+ end
59
+ end
60
+
61
+
62
+
63
+ def log_new
64
+ opts = self.class.options
65
+ if ActiveRecord::Base.connection.table_exists?(opts[:change_log_table].to_s)
66
+ log = eval("#{opts[:change_log_table].to_s.classify}.new")
67
+ log.table_name = self.class.table_name
68
+ log.key = self.send opts[:dist_key]
69
+ log.operation = 1
70
+ log.save
71
+ end
72
+ end
73
+
74
+ def log_update
75
+ opts = self.class.options
76
+ if ActiveRecord::Base.connection.table_exists?(opts[:change_log_table].to_s)
77
+ if self.changed.length > 0
78
+ changed = self.changed.delete_if { |c| opts[:skipped].include?(c) }
79
+ if changed.length > 0
80
+
81
+ log = eval("#{opts[:change_log_table].to_s.classify}.new")
82
+ log.table_name = self.class.table_name
83
+ log.key = self.send opts[:dist_key]
84
+ log.changed_fields = changed.to_yaml
85
+ log.operation = 2
86
+ log.save
87
+
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ def log_destroy
94
+ opts = self.class.options
95
+ if ActiveRecord::Base.connection.table_exists?(opts[:change_log_table].to_s)
96
+ log = eval("#{opts[:change_log_table].to_s.classify}.new")
97
+ log.table_name = self.class.table_name
98
+ log.key = self.send opts[:dist_key]
99
+ log.operation = 3
100
+ log.save
101
+ end
102
+ end
103
+
104
+ end
105
+ end
@@ -0,0 +1,11 @@
1
+ module Distributable
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Distributable
4
+
5
+ config.to_prepare do
6
+ # include into ApplicationController
7
+ #ActionController::Base.send :include, Canopus::Concerns::Controllers::Authenticator
8
+ ActiveRecord::Base.send :include, Distributable::DistributedNode
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ module Distributable
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,28 @@
1
+ # desc "Explaining what the task does"
2
+ # task :distributable do
3
+ # # Task goes here
4
+ # end
5
+
6
+ namespace "distributable" do
7
+ desc "Setup the environment for distributable to work"
8
+ task :setup do
9
+ #out = Thread.new do
10
+ # create change log table
11
+ STDOUT.puts `rails g model change_log table_name:string key:string operation:integer changed_fields:text`
12
+ STDOUT.flush
13
+ STDOUT.puts "Distributable based model generated..."
14
+ STDOUT.flush
15
+ STDOUT.puts `rails db:migrate`
16
+ STDOUT.flush
17
+ #end
18
+
19
+ #i = Thread.new do
20
+ # $stdin.each_line do |l|
21
+ # STDOUT.puts l
22
+ # end
23
+ #end
24
+
25
+ # createh sync log table
26
+ #sl = `rails g model sync_log node_id:string user_id:string`
27
+ end
28
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: distributable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Chris Liaw
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-05-22 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: 4.0.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: sqlite3
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: ''
42
+ email:
43
+ - chrisliaw@antrapol.com
44
+ executables: []
45
+ extensions: []
46
+ extra_rdoc_files: []
47
+ files:
48
+ - MIT-LICENSE
49
+ - README.md
50
+ - Rakefile
51
+ - app/assets/config/distributable_manifest.js
52
+ - app/assets/javascripts/distributable/application.js
53
+ - app/assets/stylesheets/distributable/application.css
54
+ - app/controllers/distributable/application_controller.rb
55
+ - app/helpers/distributable/application_helper.rb
56
+ - app/jobs/distributable/application_job.rb
57
+ - app/mailers/distributable/application_mailer.rb
58
+ - app/models/distributable/application_record.rb
59
+ - app/views/layouts/distributable/application.html.erb
60
+ - config/routes.rb
61
+ - lib/distributable.rb
62
+ - lib/distributable/engine.rb
63
+ - lib/distributable/version.rb
64
+ - lib/tasks/distributable_tasks.rake
65
+ homepage: ''
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.5.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: ''
89
+ test_files: []