skadategems-dev 0.0.9

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 (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