unitylock 0.1.0

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