unitylock 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.
Files changed (63) hide show
  1. checksums.yaml +7 -0
  2. data/.babelrc +8 -0
  3. data/.gitignore +11 -0
  4. data/.node-version +1 -0
  5. data/.rspec +2 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +5 -0
  8. data/CODE_OF_CONDUCT.md +49 -0
  9. data/Gemfile +8 -0
  10. data/Gemfile.lock +53 -0
  11. data/LICENSE.txt +21 -0
  12. data/Procfile +1 -0
  13. data/README.md +32 -0
  14. data/Rakefile +10 -0
  15. data/app.json +7 -0
  16. data/app/action_creators/Lock.js +22 -0
  17. data/app/action_creators/Login.js +10 -0
  18. data/app/action_creators/LoginDialogAction.js +9 -0
  19. data/app/action_creators/Search.js +13 -0
  20. data/app/action_creators/SnackMessage.js +6 -0
  21. data/app/action_creators/Unlock.js +22 -0
  22. data/app/client.js +1 -0
  23. data/app/components/AppSnackBarComponent.js +24 -0
  24. data/app/components/FooterComponent.js +40 -0
  25. data/app/components/LockAppBarComponent.js +60 -0
  26. data/app/components/LockTableComponent.js +59 -0
  27. data/app/components/LoginDialogComponent.js +69 -0
  28. data/app/containers/App.js +18 -0
  29. data/app/containers/AppSnackBar.js +19 -0
  30. data/app/containers/LockAppBar.js +30 -0
  31. data/app/containers/LockTable.js +44 -0
  32. data/app/containers/LoginDialog.js +32 -0
  33. data/app/index.html +20 -0
  34. data/app/index.js +44 -0
  35. data/app/reducers/index.js +33 -0
  36. data/app/server.js +2 -0
  37. data/app/src/client.js +35 -0
  38. data/app/src/helper.js +85 -0
  39. data/app/src/row.js +60 -0
  40. data/app/src/socket.js +47 -0
  41. data/app/test/helper.spec.js +47 -0
  42. data/app/utils/FileUtils.js +11 -0
  43. data/art/unitylock-logo.png +0 -0
  44. data/bin/console +14 -0
  45. data/bin/setup +8 -0
  46. data/config.ru +4 -0
  47. data/config/puma.rb +9 -0
  48. data/exe/unitylock +44 -0
  49. data/lib/unitylock/client.rb +2 -0
  50. data/lib/unitylock/client/main.rb +90 -0
  51. data/lib/unitylock/server.rb +2 -0
  52. data/lib/unitylock/server/entity/unityfile.rb +38 -0
  53. data/lib/unitylock/server/model.rb +59 -0
  54. data/lib/unitylock/server/router.rb +71 -0
  55. data/lib/unitylock/server/service.rb +41 -0
  56. data/lib/unitylock/version.rb +3 -0
  57. data/package.json +52 -0
  58. data/public/favicon.ico +0 -0
  59. data/public/static/bundle.js +98 -0
  60. data/public/static/main.css +82 -0
  61. data/unitylock.gemspec +25 -0
  62. data/webpack.config.js +40 -0
  63. metadata +148 -0
@@ -0,0 +1,60 @@
1
+ class Row {
2
+ constructor(path, helper) {
3
+ this.path = path
4
+ this.helper = helper
5
+ }
6
+
7
+ // TODO: sync
8
+ lock (user) {
9
+ return new Promise((resolve, reject) => {
10
+ helper.fetch_by_path(this.path)
11
+ .then(row => {
12
+ if (row === undefined) {
13
+ reject("Not found lock object.")
14
+ return
15
+ }
16
+
17
+ if (row.owner != null) {
18
+ reject("Owner already exist.")
19
+ return
20
+ }
21
+
22
+ this.owner = user
23
+ helper
24
+ .create_or_update_rows([this])
25
+ .then(row => resolve(this))
26
+ .catch(err => reject(err))
27
+ })
28
+ })
29
+ }
30
+
31
+ // TODO: sync
32
+ unlock (user) {
33
+ var row = helper.fetch_by_path(this.path)
34
+ if (row === undefined) {
35
+ return false
36
+ }
37
+
38
+ if (row.owner != user) {
39
+ return false
40
+ }
41
+
42
+ this.owner = null
43
+ helper.create_or_update_rows([this])
44
+ return true
45
+ }
46
+
47
+ delete () {
48
+ helper.delete_by_path(this.path)
49
+ }
50
+
51
+ static touches(helper, pathes) {
52
+ helper.create_or_update_rows(pathes)
53
+ }
54
+
55
+ static list(helper) {
56
+ helper.search()
57
+ }
58
+ }
59
+
60
+ export default Row
@@ -0,0 +1,47 @@
1
+ var port = process.env.WEBSOCKET_PORT || 8080;
2
+ var WebSocketServer = require('websocket').server;
3
+ var http = require('http');
4
+ var server = http.createServer(function(request, response) {
5
+ console.log((new Date()) + ' Received request for ' + request.url);
6
+ response.writeHead(404);
7
+ response.end();
8
+ });
9
+
10
+ server.listen(port, function() {
11
+ console.log((new Date()) + ' WebSocket port is ' + port);
12
+ });
13
+
14
+ var wsServer = new WebSocketServer({
15
+ httpServer: server,
16
+ autoAcceptConnections: false
17
+ });
18
+
19
+ function originIsAllowed(origin) {
20
+ return true;
21
+ }
22
+
23
+ wsServer.on('request', function(request) {
24
+ if (!originIsAllowed(request.origin)) {
25
+ // Make sure we only accept requests from an allowed origin
26
+ request.reject();
27
+ console.log((new Date()) + ' Connection from origin ' + request.origin + ' rejected.');
28
+ return;
29
+ }
30
+
31
+ var connection = request.accept('echo-protocol', request.origin);
32
+ console.log((new Date()) + ' Connection accepted.');
33
+ connection.on('message', function(message) {
34
+ if (message.type === 'utf8') {
35
+ console.log('Received Message: ' + message.utf8Data);
36
+ connection.sendUTF(message.utf8Data);
37
+ }
38
+ else if (message.type === 'binary') {
39
+ console.log('Received Binary Message of ' + message.binaryData.length + ' bytes');
40
+ connection.sendBytes(message.binaryData);
41
+ }
42
+ });
43
+ connection.on('close', function(reasonCode, description) {
44
+ console.log((new Date()) + ' Peer ' + connection.remoteAddress + ' disconnected.');
45
+ });
46
+ });
47
+
@@ -0,0 +1,47 @@
1
+ import assert from 'assert'
2
+ import fs from 'fs'
3
+ import Helper from '../src/helper.js'
4
+ import Row from '../src/row.js'
5
+ let test_file = 'test.lock'
6
+
7
+ describe('Helper', () => {
8
+ beforeEach((done) => {
9
+ fs.unlink(test_file, (err) => {
10
+ done()
11
+ })
12
+ })
13
+
14
+ it('can instantiate', () => {
15
+ assert.ok(new Helper(test_file) != null, "Helper is not null")
16
+ })
17
+
18
+ it('can search when initialized', (done) => {
19
+ new Helper(test_file)
20
+ .search()
21
+ .then(rows => {
22
+ assert.ok(rows.length == 0, "length should be zero")
23
+ done()
24
+ })
25
+ .catch(err => {
26
+ assert.ok(err == null, "should not raise error")
27
+ done()
28
+ })
29
+ })
30
+
31
+ it('can create_or_update_by_pathes', (done) => {
32
+ new Helper(test_file)
33
+ .create_or_update_by_pathes(["key1", "key2"])
34
+ .then(rows => {
35
+ assert.equal(2, rows.length, "length should be two")
36
+ assert.equal("key1", rows[0].path, "path should not be null")
37
+ assert.ok(null != rows[0].updated_at, "updated_at should not be null")
38
+ assert.equal("key2", rows[1].path, "path should not be null")
39
+ assert.ok(null != rows[1].updated_at, "updated_at should not be null")
40
+ done()
41
+ })
42
+ .catch(err => {
43
+ assert.ok(err == null, "should not raise error")
44
+ done()
45
+ })
46
+ })
47
+ })
@@ -0,0 +1,11 @@
1
+ function basename(path) {
2
+ var base = new String(path).substring(path.lastIndexOf('/') + 1)
3
+
4
+ if (base.lastIndexOf() != -1) {
5
+ base = base.substring(0, base.lastIndexOf('.'))
6
+ }
7
+
8
+ return base
9
+ }
10
+
11
+ export { basename }
Binary file
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "unitylock/server"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,4 @@
1
+ require 'sinatra/base'
2
+ require 'unitylock/server'
3
+
4
+ run Unitylock::Server::Main
@@ -0,0 +1,9 @@
1
+ workers Integer(ENV['WEB_CONCURRENCY'] || 2)
2
+ threads_count = Integer(ENV['MAX_THREADS'] || 5)
3
+ threads threads_count, threads_count
4
+
5
+ preload_app!
6
+
7
+ rackup DefaultRackup
8
+ port ENV['PORT'] || 3000
9
+ environment ENV['RACK_ENV'] || 'development'
@@ -0,0 +1,44 @@
1
+ #!/usr/bin/env ruby
2
+ require 'unitylock/client'
3
+ require 'optparse'
4
+
5
+ PROJECT_CONFIG_FILE = '.unitylock'
6
+
7
+ params = ARGV.getopts('', 'user:', 'force')
8
+ command = ARGV.shift
9
+ project_config = File.exists?(PROJECT_CONFIG_FILE) ? JSON.parse(File.read(PROJECT_CONFIG_FILE), {symbolize_names: true}) : {}
10
+ url = project_config[:url] || 'http://localhost:3000'
11
+ client = Unitylock::Client::Main.new(url)
12
+
13
+ case command
14
+ when "search" then
15
+ puts client.search.body
16
+ when "touches" then
17
+ puts client.touches(ARGV).body
18
+ when "deletes" then
19
+ puts client.deletes(ARGV).body
20
+ when "delete_all" then
21
+ puts client.delete_all.body
22
+ when "lock" then
23
+ user = params['user']
24
+ raise "requires parameter 'file'" unless ARGV.size > 0
25
+ raise "requires parameter 'user'" unless user != nil
26
+ puts client.locks(user: user, files: ARGV)
27
+ when "unlock" then
28
+ user = params['user']
29
+ raise "requires parameter 'file'" unless ARGV.size > 0
30
+ raise "requires parameter 'user'" unless user != nil
31
+ puts client.unlocks(user: user, files: ARGV)
32
+ when 'init' then
33
+ print 'url (http://localhost:3000): '
34
+ url = gets.chomp.sub(/^$/, 'http://localhost:3000')
35
+ File.write(PROJECT_CONFIG_FILE, JSON.pretty_generate({url: url}))
36
+ puts 'Created .unitylock'
37
+ when 'sync' then
38
+ force = params['force'] || false
39
+ client.sync(dir: '.', force: force)
40
+ else
41
+ puts "undefined command"
42
+ exit 1
43
+ end
44
+
@@ -0,0 +1,2 @@
1
+ require 'unitylock/version'
2
+ require 'unitylock/client/main'
@@ -0,0 +1,90 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'find'
4
+
5
+ module Unitylock
6
+ module Client
7
+ class Main
8
+ def initialize(urlbase)
9
+ @conn = Faraday.new(:url => urlbase) do |faraday|
10
+ faraday.request :url_encoded
11
+ # faraday.response :logger
12
+ faraday.adapter Faraday.default_adapter
13
+ end
14
+ end
15
+
16
+ def search
17
+ @conn.get '/files'
18
+ end
19
+
20
+ def touches(files)
21
+ @conn.post do |req|
22
+ req.url '/files'
23
+ req.headers['Content-Type'] = 'application/json'
24
+ req.body = JSON.generate(files)
25
+ end
26
+ end
27
+
28
+ def deletes(files)
29
+ @conn.delete do |req|
30
+ req.url '/files'
31
+ req.headers['Content-Type'] = 'application/json'
32
+ req.body = JSON.generate(files)
33
+ end
34
+ end
35
+
36
+ def delete_all
37
+ files = JSON.parse(search.body).collect {|it| it['file']}
38
+ deletes(files)
39
+ end
40
+
41
+ def locks(user:, files:)
42
+ # TODO: requests at once
43
+ results = []
44
+ files.each do |file|
45
+ results << @conn.put do |req|
46
+ req.url "/user/#{user}/lock"
47
+ req.headers['Content-Type'] = 'application/json'
48
+ req.body = JSON.generate({file: file})
49
+ end.body
50
+ end
51
+ results
52
+ end
53
+
54
+ def unlocks(user:, files:)
55
+ # TODO: requests at once
56
+ results = []
57
+ files.each do |file|
58
+ results << @conn.put do |req|
59
+ req.url "/user/#{user}/unlock"
60
+ req.headers['Content-Type'] = 'application/json'
61
+ req.body = JSON.generate({file: file})
62
+ end.body
63
+ end
64
+ results
65
+ end
66
+
67
+ def sync(dir: '.', force: false, includes: /\.(unity|prefab)$/, excludes: /^$/)
68
+ files = Find.find(dir).select{|it| it =~ includes}.select{|it| it !~ excludes}
69
+
70
+ puts "touched:"
71
+ puts files
72
+
73
+ touches(files)
74
+ touched_files = JSON.parse(search.body).collect {|it| it['file']}
75
+
76
+ if force
77
+ remove_files = touched_files.select{|it| !files.include?(it) }
78
+
79
+ puts "removed:"
80
+ puts remove_files
81
+
82
+ deletes(remove_files)
83
+ return (touched_files - remove_files)
84
+ end
85
+
86
+ return touched_files
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,2 @@
1
+ require 'unitylock/version'
2
+ require 'unitylock/server/router'
@@ -0,0 +1,38 @@
1
+ require 'json/add/core'
2
+
3
+ module Unitylock
4
+ module Server
5
+ module Entity
6
+ class UnityFile < Struct.new(:user,:file,:updated_at)
7
+ def update
8
+ self.updated_at = Time.now
9
+ self
10
+ end
11
+
12
+ def to_hash
13
+ {
14
+ user: self.user,
15
+ file: self.file,
16
+ updated_at: self.updated_at.strftime("%Y-%m-%d %H:%M:%S")
17
+ }
18
+ end
19
+
20
+ class << self
21
+ def json_create(o)
22
+ new(*o['v'])
23
+ end
24
+
25
+ def create(hash)
26
+ unityfile = UnityFile.new
27
+ default_values = { updated_at: Time.now }
28
+ default_values.merge(hash).each do |key,value|
29
+ unityfile[key] = value
30
+ end
31
+
32
+ unityfile
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,59 @@
1
+ require 'ostruct'
2
+ require 'json'
3
+ require 'unitylock/server/entity/unityfile'
4
+
5
+ include Unitylock::Server::Entity
6
+ module Unitylock
7
+ module Server
8
+ class Model
9
+ attr_accessor :data, :file
10
+
11
+ def initialize(file)
12
+ @file = file
13
+ File.write(@file, '{}') unless File.exists? @file
14
+ load
15
+ end
16
+
17
+ def load
18
+ @data = JSON.parse(File.read(@file), create_additions: true)
19
+ end
20
+
21
+ def save
22
+ File.write(@file, JSON.generate(@data))
23
+ end
24
+
25
+ def delete(file:)
26
+ @data.delete(file)
27
+ save
28
+ end
29
+
30
+ def lock(file:, user:)
31
+ raise "lock failure" unless @data.has_key?(file) && @data[file].user == nil
32
+ @data[file] = UnityFile.create(file: file, user: user)
33
+ save
34
+ @data[file]
35
+ end
36
+
37
+ def unlock(file:, user:)
38
+ raise "unlock failure" unless @data.has_key?(file) && @data[file].user == user
39
+ @data[file] = UnityFile.create(file: file, user: nil)
40
+ save
41
+ @data[file]
42
+ end
43
+
44
+ def create_or_update(file:)
45
+ @data[file] ||= UnityFile.create(file: file)
46
+ @data[file].update
47
+ save
48
+ end
49
+
50
+ def search
51
+ @data.map {|key,value| value}
52
+ end
53
+
54
+ def fetch(file:)
55
+ @data[file]
56
+ end
57
+ end
58
+ end
59
+ end