skadategems-dev 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +16 -0
  3. data/.travis.yml +12 -0
  4. data/Gemfile +13 -0
  5. data/Guardfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +74 -0
  8. data/Rakefile +6 -0
  9. data/bin/skadate +9 -0
  10. data/bin/skadategems-dev +18 -0
  11. data/integration/README.md +97 -0
  12. data/integration/cli/skadate_init_spec.rb +86 -0
  13. data/integration/cli/skadate_spec.rb +50 -0
  14. data/integration/cli/spec_helper.rb +23 -0
  15. data/integration/lib/remote/download_file_spec.rb +136 -0
  16. data/integration/spec_helper.rb +101 -0
  17. data/lib/skadategems/dev/bundle.rb +115 -0
  18. data/lib/skadategems/dev/cli/remote.rb +364 -0
  19. data/lib/skadategems/dev/cli/skadate.rb +184 -0
  20. data/lib/skadategems/dev/remote.rb +170 -0
  21. data/lib/skadategems/dev/remote/clone_logger.rb +118 -0
  22. data/lib/skadategems/dev/remote/configs.rb +97 -0
  23. data/lib/skadategems/dev/remote/directory.rb +88 -0
  24. data/lib/skadategems/dev/remote/file.rb +157 -0
  25. data/lib/skadategems/dev/remote/includes/mysql_connect.php +8 -0
  26. data/lib/skadategems/dev/skadate.rb +30 -0
  27. data/lib/skadategems/dev/software_version.rb +66 -0
  28. data/lib/skadategems/dev/source_controller.rb +101 -0
  29. data/lib/skadategems/dev/version.rb +5 -0
  30. data/skadategems-dev.gemspec +33 -0
  31. data/spec/skadategems/dev/bundle_spec.rb +268 -0
  32. data/spec/skadategems/dev/cli/remote_spec.rb +623 -0
  33. data/spec/skadategems/dev/cli/skadate_spec.rb +182 -0
  34. data/spec/skadategems/dev/remote/clone_logger_spec.rb +324 -0
  35. data/spec/skadategems/dev/remote/directory_spec.rb +117 -0
  36. data/spec/skadategems/dev/remote/file_spec.rb +74 -0
  37. data/spec/skadategems/dev/remote_spec.rb +95 -0
  38. data/spec/skadategems/dev/skadate_spec.rb +62 -0
  39. data/spec/skadategems/dev/software_version_spec.rb +92 -0
  40. data/spec/skadategems/dev/source_controller_spec.rb +243 -0
  41. data/spec/spec_helper.rb +32 -0
  42. metadata +153 -0
@@ -0,0 +1,97 @@
1
+ require 'json'
2
+
3
+ module SkadateGems
4
+ module Dev
5
+
6
+ class Remote
7
+
8
+ # Represents remote application configurations accessor.
9
+ class Configs
10
+
11
+ # @param remote [Remote] skadate remote app
12
+ def initialize(remote)
13
+ @remote = remote
14
+ end
15
+
16
+ # Configuration value accessor method.
17
+ # @param section_path_or_id [Fixnum|String] config section path or `config_id`
18
+ def [](section_path_or_id)
19
+ case section_path_or_id.class.to_s
20
+ when 'String'
21
+ raise 'Getting remote configs by path is currently not implemented'
22
+ when 'Fixnum'
23
+ get_by_id(section_path_or_id)
24
+ else
25
+ raise ArgumentError, "unexpected `section_path_or_id` type #{section_path_or_id.class}"
26
+ end
27
+ end
28
+
29
+ # @param config_id [Fixnum] configuration id
30
+ def get_by_id(config_id)
31
+ response = @remote.exec include: 'mysql_connect' do |script|
32
+ script << <<-PHPSCRIPT
33
+ $result = $mysql->query("SELECT `name`, `value` FROM `".TBL_CONFIG."` WHERE `config_id`=#{config_id}");
34
+ $config = $result->fetch_object();
35
+ echo '{ ';
36
+ echo '"name": "'.$config->name.'", ';
37
+ echo '"value": '.$config->value;
38
+ echo ' }';
39
+ PHPSCRIPT
40
+ end
41
+
42
+ result = JSON.parse(response.body)
43
+
44
+ Config.new(@remote, config_id, result['name'], result['value'])
45
+ end
46
+
47
+ # Represents remote application config entry.
48
+ class Config
49
+
50
+ # @return [Fixnum] config entry id
51
+ attr_reader :id
52
+
53
+ # @return [String] config entry name
54
+ attr_reader :name
55
+
56
+ # @return [mixed] config entry value
57
+ attr_reader :value
58
+
59
+ # @return [nil | :updated | :not_modified] config value update status
60
+ attr_reader :update_status
61
+
62
+ # @param remote [Remote] remote skadate app
63
+ # @param id [Fixnum] config id
64
+ # @param name [String] config name
65
+ # @param value [mixed] config value
66
+ def initialize(remote, id, name, value)
67
+ @remote, @id, @name, @value = remote, id, name, value
68
+ end
69
+
70
+ # Perform remote application config value update.
71
+ # Changes the #update_status value.
72
+ # @param value [#to_json] json serializable value
73
+ def value=(value)
74
+ if value == @value
75
+ @update_status = :not_modified
76
+ return
77
+ end
78
+
79
+ response = @remote.exec(include: 'mysql_connect') do |script|
80
+ script << <<-PHPSCRIPT
81
+ $value = $mysql->real_escape_string('#{value.to_json.gsub(/['\\]/, '\\\\\0')}');
82
+ $mysql->query("UPDATE `".TBL_CONFIG."` SET `value`='$value' WHERE `config_id`=#{@id}");
83
+ echo $mysql->affected_rows;
84
+ PHPSCRIPT
85
+ end
86
+
87
+ @update_status = (response.body == '1') ? :updated : :not_modified
88
+ end
89
+
90
+ end
91
+
92
+ end
93
+
94
+ end
95
+
96
+ end
97
+ end
@@ -0,0 +1,88 @@
1
+ require 'json'
2
+ require 'skadategems/dev/remote/file'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ class Remote
8
+
9
+ # Represents a remote directory.
10
+ class Directory < File
11
+
12
+ # @attr list_hidden [boolean]
13
+ attr_accessor :list_hidden
14
+
15
+ # @return [boolean] always true
16
+ def directory?
17
+ true
18
+ end
19
+
20
+ # @return [String] always '/'
21
+ def extname
22
+ '/'
23
+ end
24
+
25
+ # List remote directory files.
26
+ # @return [Array < Directory | File >] where each entry
27
+ # may be either a `Remote::Directory` or a `Remote::File` accessor object.
28
+ def list
29
+ path = self.path.dup
30
+ path[0] = '' if path[0] == '/'
31
+ path[-1] = '' if path[-1] == '/'
32
+
33
+ response = @remote.exec do |batch|
34
+ batch << <<-PHPSCRIPT
35
+ $dir = dir(DIR_SITE_ROOT . '#{path.gsub(/['\\]/, '\\\\\0')}');
36
+
37
+ $list = array();
38
+ while (false !== ($file = $dir->read())) {
39
+ if (#{if list_hidden
40
+ '$file === \'.\' || $file === \'..\''
41
+ else
42
+ '$file{0} === \'.\' && $file !== \'.htaccess\''
43
+ end}) {
44
+ continue;
45
+ }
46
+
47
+ $path = "$dir->path/$file";
48
+
49
+ if ($path === $_SERVER['SCRIPT_FILENAME']) {
50
+ continue;
51
+ }
52
+
53
+ if (is_file($path)) {
54
+ $type = 'f';
55
+ } elseif (is_dir($path)) {
56
+ $type = 'd';
57
+ } else {
58
+ continue;
59
+ }
60
+
61
+ $list[] = array($file, $type, filesize($path));
62
+ }
63
+
64
+ echo json_encode($list);
65
+ PHPSCRIPT
66
+ end
67
+
68
+ result = []
69
+
70
+ JSON.parse(response.body).each do |entry|
71
+ name, type, size = entry
72
+ _path = ::File.join(self.path, name)
73
+
74
+ result << if type == 'd'
75
+ Directory.new(@remote, _path, size)
76
+ else
77
+ File.new(@remote, _path, size)
78
+ end
79
+ end
80
+
81
+ result.sort! { |a, b| a.basename <=> b.basename }
82
+ end
83
+ end
84
+
85
+ end
86
+
87
+ end
88
+ end
@@ -0,0 +1,157 @@
1
+ require 'fileutils'
2
+ require 'digest/md5'
3
+ require 'tempfile'
4
+
5
+ module SkadateGems
6
+ module Dev
7
+
8
+ class Remote
9
+
10
+ # Remote file accessor.
11
+ class File
12
+
13
+ # @return [String] relative path to a file
14
+ attr_reader :path
15
+
16
+ # @return [Fixnum] remote file size in bytes
17
+ attr_reader :size
18
+
19
+ def initialize(remote, path, size = nil)
20
+ @remote, @path, @size = remote, path, size
21
+ end
22
+
23
+ def directory?
24
+ false
25
+ end
26
+
27
+ # @return [String] remote file basename
28
+ def basename
29
+ ::File.basename(@path)
30
+ end
31
+
32
+ # @return [String] remote file extension
33
+ def extname
34
+ ::File.extname(@path)
35
+ end
36
+
37
+ # @return [String] human readable file size
38
+ def friendly_size
39
+ units = %W(B KiB MiB GiB TiB)
40
+
41
+ size, unit = units.reduce(@size.to_f) do |(fsize, _), type|
42
+ fsize > 512 ? [fsize / 1024, type] : (break [fsize, type])
43
+ end
44
+
45
+ "#{size > 9 || size.modulo(1) < 0.1 ? '%d' : '%.1f'} %s" % [size, unit]
46
+ end
47
+
48
+ # Request remote file to read it's contents.
49
+ # @yield [response] passes a http response to the block
50
+ def request(md5_checksum: nil, &block)
51
+ path = self.path.dup
52
+ path[0] = '' if path[0] == '/'
53
+
54
+ batch = ScriptBatch.new
55
+
56
+ batch << <<-PHPSCRIPT
57
+ $filename = DIR_SITE_ROOT . '#{path.gsub(/['\\]/, '\\\\\0')}';
58
+
59
+ if (!file_exists($filename)) {
60
+ header('HTTP/1.1 404 Not Found');
61
+ exit;
62
+ }
63
+
64
+ $md5_checksum = md5_file($filename);
65
+ PHPSCRIPT
66
+
67
+ if md5_checksum
68
+ raise ':md5_checksum must be a String' unless md5_checksum.is_a? String
69
+
70
+ batch << <<-PHPSCRIPT
71
+ if ($md5_checksum === '#{md5_checksum.gsub(/['\\]/, '\\\\\0')}') {
72
+ header('HTTP/1.1 304 Not Modified');
73
+ exit;
74
+ }
75
+ PHPSCRIPT
76
+ end
77
+
78
+ batch << <<-PHPSCRIPT
79
+ header('Content-Description: File Transfer');
80
+ header('Content-Type: application/octet-stream');
81
+ header('Content-Disposition: attachment; filename=' . basename($filename));
82
+ header('Content-Length: ' . filesize($filename));
83
+ header('X-Content-MD5-Checksum: ' . $md5_checksum);
84
+ ob_clean();
85
+ flush();
86
+ readfile($filename);
87
+ exit;
88
+ PHPSCRIPT
89
+
90
+ @remote.accessor.exec(batch) do |response|
91
+ if response.is_a?(Net::HTTPNotModified)
92
+ block.call(response)
93
+ elsif response.is_a?(Net::HTTPOK)
94
+ if response['content-disposition'] == "attachment; filename=#{basename}" &&
95
+ response['x-content-md5-checksum']
96
+ block.call(response)
97
+ else
98
+ raise 'unexpected response headers'
99
+ end
100
+ else
101
+ raise "[#{response.code} \"#{response.message}\"]"
102
+ end
103
+ end
104
+ end
105
+
106
+ # Download and save a remote file as a given filename.
107
+ # @param filename [String] filename to save file as
108
+ # @yield [chunk_size] on each segment receive
109
+ # @yieldparam chunk_size [Fixnum] received segment size
110
+ def save_as(filename)
111
+ current_checksum = if ::File.exists?(filename)
112
+ Digest::MD5.file(filename).hexdigest
113
+ end
114
+
115
+ request(md5_checksum: current_checksum) do |response|
116
+ if response.is_a?(Net::HTTPNotModified)
117
+ return false
118
+ elsif response.is_a?(Net::HTTPOK)
119
+ @size = response['content-length'].to_i if @size.nil?
120
+ new_checksum = response['x-content-md5-checksum']
121
+ md5 = Digest::MD5.new
122
+
123
+ begin
124
+ tempfile = Tempfile.create([basename, extname])
125
+ response.read_body do |chunk|
126
+ tempfile << chunk
127
+ md5 << chunk
128
+ yield chunk.size if block_given?
129
+ end
130
+ tempfile.close
131
+
132
+ if md5.hexdigest != new_checksum
133
+ raise "md5 checksum mismatch:\n [#{md5.hexdigest}]\n [#{new_checksum}]"
134
+ end
135
+
136
+ parent_dir = ::File.dirname(filename)
137
+ FileUtils.mkdir_p(parent_dir) unless ::File.exists?(parent_dir)
138
+
139
+ FileUtils.mv(tempfile.path, filename)
140
+ FileUtils.chmod(0644, filename)
141
+
142
+ return true
143
+ ensure
144
+ tempfile.close unless tempfile.closed?
145
+ end
146
+ else
147
+ raise "unexpected response #{response}"
148
+ end
149
+ end
150
+ end
151
+
152
+ end
153
+
154
+ end
155
+
156
+ end
157
+ end
@@ -0,0 +1,8 @@
1
+ <?php
2
+
3
+ $mysql = new mysqli(DB_HOST, DB_USER, DB_PASS, DB_NAME) or die("Error " . mysqli_error($link));
4
+ if ($mysql->connect_error) {
5
+ die('Connect Error (' . $mysql->connect_errno . ') ' . $mysql->connect_error);
6
+ }
7
+
8
+ require_once DIR_INTERNALS . 'db_tbl.const.php';
@@ -0,0 +1,30 @@
1
+ require 'skadategems/dev/source_controller'
2
+ require 'skadategems/dev/remote'
3
+
4
+ module SkadateGems
5
+ module Dev
6
+
7
+ # Represents Skadate development project that is located in a specified `:root_dir`.
8
+ class Skadate
9
+
10
+ attr_reader :root_dir
11
+
12
+ # @param root_dir [String] path to skadate source root directory
13
+ def initialize(root_dir)
14
+ @root_dir = root_dir
15
+ end
16
+
17
+ # @return [SourceController] skadate source controller object
18
+ def source
19
+ @source ||= SourceController.new(@root_dir)
20
+ end
21
+
22
+ # @return [Remote] remote application accessor
23
+ def remote
24
+ @remote ||= Remote.new(source.filename '.dev/remote-server.json')
25
+ end
26
+
27
+ end
28
+
29
+ end
30
+ end
@@ -0,0 +1,66 @@
1
+ require 'date'
2
+
3
+ module SkadateGems
4
+ module Dev
5
+
6
+ # Represents Skadate software version
7
+ class SoftwareVersion
8
+ LIST, MAP = [], {}
9
+
10
+ attr_reader :major
11
+ attr_reader :minor
12
+ attr_reader :build
13
+ attr_reader :release_date
14
+ attr_reader :codename
15
+
16
+ # @param version [String] string version representation
17
+ # @param release_date [Date] version release date
18
+ # @param codename [String] software version codename
19
+ def initialize(version, release_date = nil, codename = nil)
20
+ @major, @minor, @build = version.split('.', 3).map { |n| n.to_i }
21
+ @release_date = release_date
22
+ @codename = codename
23
+ end
24
+
25
+ # @return [String] version string representation
26
+ def to_s
27
+ "#{@major}.#{@minor}.#{@build}"
28
+ end
29
+
30
+ # Versions equality operator
31
+ def == (other)
32
+ return other == to_s if other.instance_of?(String)
33
+
34
+ return false unless other.instance_of?(SoftwareVersion)
35
+
36
+ @major == other.major &&
37
+ @minor == other.minor &&
38
+ @build == other.build
39
+ end
40
+
41
+ # @return [boolean] true if current version is supported by `skadategems-dev`
42
+ def supported?
43
+ MAP.key?(to_s)
44
+ end
45
+
46
+ # Raised in case of software version compatibility error.
47
+ # @see Dev::SourceController#version!
48
+ class CompatibilityError < StandardError; end
49
+ end
50
+
51
+ # Skadate software supported versions.
52
+ [ ['9.0.2500', Date.new(2012, 1, 30)],
53
+ ['9.0.2555', Date.new(2012, 5, 23)],
54
+ ['9.2.2657', Date.new(2012, 10, 12)],
55
+ ['9.2.2960', Date.new(2013, 4, 16), 'Master'],
56
+ ['9.3.3144', Date.new(2013, 10, 8), 'Master II']
57
+ ].each do |params|
58
+ version = SoftwareVersion.new(*params)
59
+ SoftwareVersion::LIST << version
60
+ SoftwareVersion::MAP[params[0]] = version
61
+ end
62
+
63
+ SoftwareVersion::LIST.freeze
64
+ SoftwareVersion::MAP.freeze
65
+ end
66
+ end