php-composer 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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.rspec +2 -0
  4. data/.rubocop.yml +1006 -0
  5. data/Gemfile +15 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +35 -0
  8. data/Rakefile +1 -0
  9. data/lib/composer.rb +52 -0
  10. data/lib/composer/error.rb +8 -0
  11. data/lib/composer/json/json_file.rb +270 -0
  12. data/lib/composer/json/json_formatter.rb +159 -0
  13. data/lib/composer/json/json_validaton_error.rb +29 -0
  14. data/lib/composer/manager.rb +79 -0
  15. data/lib/composer/package/alias_package.rb +273 -0
  16. data/lib/composer/package/base_package.rb +130 -0
  17. data/lib/composer/package/complete_package.rb +55 -0
  18. data/lib/composer/package/dumper/hash_dumper.rb +169 -0
  19. data/lib/composer/package/link.rb +51 -0
  20. data/lib/composer/package/link_constraint/base_constraint.rb +36 -0
  21. data/lib/composer/package/link_constraint/empty_constraint.rb +35 -0
  22. data/lib/composer/package/link_constraint/multi_constraint.rb +67 -0
  23. data/lib/composer/package/link_constraint/specific_constraint.rb +41 -0
  24. data/lib/composer/package/link_constraint/version_constraint.rb +221 -0
  25. data/lib/composer/package/loader/hash_loader.rb +316 -0
  26. data/lib/composer/package/loader/json_loader.rb +47 -0
  27. data/lib/composer/package/loader/project_attributes_loader.rb +71 -0
  28. data/lib/composer/package/loader/project_root_package_loader.rb +28 -0
  29. data/lib/composer/package/package.rb +118 -0
  30. data/lib/composer/package/root_alias_package.rb +37 -0
  31. data/lib/composer/package/root_package.rb +37 -0
  32. data/lib/composer/package/version/version_parser.rb +583 -0
  33. data/lib/composer/package/version/version_selector.rb +106 -0
  34. data/lib/composer/provider.rb +94 -0
  35. data/lib/composer/repository/array_repository.rb +195 -0
  36. data/lib/composer/repository/filesystem_repository.rb +86 -0
  37. data/lib/composer/repository/writeable_array_repository.rb +60 -0
  38. data/lib/composer/version.rb +3 -0
  39. data/php-composer.gemspec +31 -0
  40. data/resources/composer-schema.json +421 -0
  41. metadata +188 -0
data/Gemfile ADDED
@@ -0,0 +1,15 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in composer.gemspec
4
+ gemspec
5
+
6
+ gem 'json', '>= 1.7', :platforms => [:mri_18, :mri_19]
7
+ gem 'json-schema', '>= 2.5.0'
8
+ # gem 'version_compare', '~> 0.0.2'
9
+ gem 'digest-crc'
10
+
11
+ group :development, :test do
12
+ gem 'rubocop', '>= 0.28.0', require: false
13
+ gem 'rspec'
14
+ gem 'simplecov', '>= 0.9.2'
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015, Ioannis Kappas <ikappas@devworks.gr>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ # PHP Composer Ruby Gem
2
+
3
+ A ruby gem library for consistent interactions with php composer dependency manager.
4
+
5
+ This is a partial port of the [Composer - Dependency Management for PHP](https://github.com/composer/composer) project as a library that enables consistent interactions with php composer.
6
+
7
+ See [https://getcomposer.org/](https://getcomposer.org/) for more information.
8
+
9
+ ## Installation / Usage
10
+ Add this line to your application's Gemfile:
11
+
12
+ ```ruby
13
+ gem 'php-composer'
14
+ ```
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ Or install it yourself as:
21
+
22
+ $ gem install php-composer
23
+
24
+ ## Authors
25
+ Ioannis Kappas - <ikappas@devworks.gr>
26
+
27
+ ## License
28
+ PHP Composer Ruby Gem is licensed under the MIT License - see the LICENSE file for details
29
+
30
+ ## Contributing
31
+ 1. Fork it ( https://github.com/ikappas/php-composer/fork )
32
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
33
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
34
+ 4. Push to the branch (`git push origin my-new-feature`)
35
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require 'bundler/gem_tasks'
data/lib/composer.rb ADDED
@@ -0,0 +1,52 @@
1
+ # external
2
+ require 'json'
3
+ require 'json-schema'
4
+
5
+ # /
6
+ require 'composer/version'
7
+ require 'composer/error'
8
+
9
+ # /json
10
+ require 'composer/json/json_validaton_error'
11
+ require 'composer/json/json_file'
12
+ require 'composer/json/json_formatter'
13
+
14
+ # /package
15
+ require 'composer/package/base_package'
16
+ require 'composer/package/package'
17
+ require 'composer/package/complete_package'
18
+ require 'composer/package/alias_package'
19
+ require 'composer/package/root_alias_package'
20
+ require 'composer/package/root_package'
21
+ require 'composer/package/link'
22
+
23
+ # /package/dumper
24
+ require 'composer/package/dumper/hash_dumper'
25
+
26
+ # /package/link_constraint
27
+ require 'composer/package/link_constraint/base_constraint'
28
+ require 'composer/package/link_constraint/empty_constraint'
29
+ require 'composer/package/link_constraint/specific_constraint'
30
+ require 'composer/package/link_constraint/version_constraint'
31
+ require 'composer/package/link_constraint/multi_constraint'
32
+
33
+ # /package/loader
34
+ require 'composer/package/loader/hash_loader'
35
+ require 'composer/package/loader/json_loader'
36
+
37
+ # /package/version
38
+ require 'composer/package/version/version_parser'
39
+ require 'composer/package/version/version_selector'
40
+
41
+ # Dir[File.join(File.dirname(__FILE__), "composer/package/dumper/*.rb")].each {|file| require file }
42
+ # Dir[File.join(File.dirname(__FILE__), "composer/package/link_constraint/*.rb")].each {|file| require file }
43
+ # Dir[File.join(File.dirname(__FILE__), "composer/package/loader/*.rb")].each {|file| require file }
44
+ # Dir[File.join(File.dirname(__FILE__), "composer/package/version/*.rb")].each {|file| require file }
45
+
46
+ # /repository
47
+ require 'composer/repository/array_repository'
48
+ require 'composer/repository/writeable_array_repository'
49
+ require 'composer/repository/filesystem_repository'
50
+
51
+ module Composer
52
+ end
@@ -0,0 +1,8 @@
1
+ module Composer
2
+ class Error < ::StandardError; end
3
+ class ArgumentError < Error; end
4
+ class TypeError < Error; end
5
+ class UnexpectedValueError < Error; end
6
+ class LogicError < Error; end
7
+ class InvalidRepositoryError < Error; end
8
+ end
@@ -0,0 +1,270 @@
1
+ #
2
+ # This file was ported to ruby from Composer php source code file.
3
+ # Original Source: Composer\Json\JsonFile.php
4
+ #
5
+ # (c) Nils Adermann <naderman@naderman.de>
6
+ # Jordi Boggiano <j.boggiano@seld.be>
7
+ #
8
+ # For the full copyright and license information, please view the LICENSE
9
+ # file that was distributed with this source code.
10
+ #
11
+
12
+ module Composer
13
+ module Json
14
+ # Reads/writes json files.
15
+ #
16
+ # PHP Authors:
17
+ # Konstantin Kudryashiv <ever.zet@gmail.com>
18
+ # Jordi Boggiano <j.boggiano@seld.be>
19
+ #
20
+ # Ruby Authors:
21
+ # Ioannis Kappas <ikappas@devworks.gr>
22
+ class JsonFile
23
+ attr_reader :path
24
+
25
+ LAX_SCHEMA = 1
26
+ STRICT_SCHEMA = 2
27
+
28
+ JSON_ERROR_NONE = 0
29
+ JSON_ERROR_DEPTH = 1
30
+ JSON_ERROR_STATE_MISMATCH = 2
31
+ JSON_ERROR_CTRL_CHAR = 3
32
+ JSON_ERROR_SYNTAX = 4
33
+ JSON_ERROR_UTF8 = 5
34
+ JSON_ERROR_RECURSION = 6
35
+ JSON_ERROR_INF_OR_NAN = 7
36
+ JSON_ERROR_UNSUPPORTED_TYPE = 8
37
+
38
+ # Initializes json file reader/parser.
39
+ # @param [String] path path to a json file
40
+ # @param [RemoteFileSystem] rfs The remote filesystem to use for http/https json files
41
+ # @raise [ArgumentError]
42
+ def initialize(path, rfs = nil)
43
+ @path = path
44
+ if rfs === nil && /^https?:\/\//i.match(path)
45
+ raise ArgumentError,
46
+ 'http urls require a RemoteFilesystem instance to be passed'
47
+ end
48
+ @rfs = rfs
49
+ end
50
+
51
+ # Checks whether this json file exists.
52
+ #
53
+ # Returns:
54
+ # true if this json file exists; otherwise false.
55
+ def exist?
56
+ File.exist?(path)
57
+ end
58
+
59
+ # Reads the json file.
60
+ #
61
+ # Raises:
62
+ # RuntimeError
63
+ #
64
+ # Returns:
65
+ # mixed
66
+ def read
67
+ if @rfs
68
+ json = @rfs.get_contents(@path, @path, false)
69
+ else
70
+ json = File.open(@path, 'r') { |f| f.read }
71
+ end
72
+
73
+ parse_json(json, @path)
74
+
75
+ rescue Exception => e
76
+ raise e
77
+ end
78
+
79
+ def write(hash, options = 448)
80
+ dir = File.dirname(@path)
81
+
82
+ unless File.directory?(storage_path)
83
+ if File.exist?(dir)
84
+ raise UnexpectedValueError,
85
+ "#{dir} exists and is not a directory."
86
+ end
87
+ FileUtils.mkdir_p(dir, 0777)
88
+ end
89
+
90
+ retries = 3
91
+ while retries >= 0
92
+ begin
93
+ file_ending = options & JSON_PRETTY_PRINT ? "\n" : ''
94
+ File.open(path, 'w') do |f|
95
+ content = encode(hash, options) + file_ending
96
+ f.write(content)
97
+ end
98
+ break
99
+
100
+ rescue Exception => e
101
+ if retries
102
+ retries -= 1
103
+ sleep 0.5
104
+ else
105
+ raise e
106
+ end
107
+ end
108
+ end
109
+
110
+ end
111
+
112
+ # Validates the schema of the current json file according
113
+ # to composer-schema.json rules
114
+ #
115
+ # @param schema int a JsonFile::*_SCHEMA constant
116
+ # @return bool true if schema is valid; Otherwise false.
117
+ # @throw Composer::Json::JsonValidationError
118
+ def validate_schema(schema = STRICT_SCHEMA)
119
+ content = File.open(@path, 'r') { |f| f.read }
120
+ data = JSON.parse(content)
121
+
122
+ if data == nil && content != 'null'
123
+ self::validate_syntax(content, @path)
124
+ end
125
+
126
+ schema_file = File.join(
127
+ File.dirname(__FILE__),
128
+ '../../../resources/composer-schema.json'
129
+ )
130
+
131
+ schema_data = JSON.parse(
132
+ File.open(schema_file, 'r') { |f| f.read }
133
+ )
134
+
135
+ if schema === LAX_SCHEMA
136
+ schema_data['additionalProperties'] = true
137
+ schema_data['properties']['name']['required'] = false
138
+ schema_data['properties']['description']['required'] = false
139
+ end
140
+
141
+ errors = JSON::Validator.fully_validate(
142
+ schema_data,
143
+ data,
144
+ {:errors_as_objects => true}
145
+ )
146
+
147
+ unless errors.empty?
148
+ processed_errors = []
149
+ errors.each do |error|
150
+ prefix = error[:fragment] ? "#{error[:fragment]} : " : ''
151
+ processed_errors.push( prefix + error[:message])
152
+ end
153
+ raise Composer::Json::JsonValidationError.new(processed_errors),
154
+ "\"#{@path}\" does not match the expected JSON schema"
155
+ end
156
+
157
+ true
158
+ end
159
+
160
+ class << self
161
+
162
+ # Encodes an hash into (optionally pretty-printed) JSON
163
+ #
164
+ # @param data mixed Data to encode into a formatted JSON string
165
+ # @param options int json_encode options (defaults to JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)
166
+ # @return string Encoded json
167
+ def encode(data, options = 448)
168
+
169
+ # if (version_compare(PHP_VERSION, '5.4', '>=')) {
170
+ # $json = json_encode(data, options);
171
+
172
+ # # compact brackets to follow recent php versions
173
+ # if (PHP_VERSION_ID < 50428 || (PHP_VERSION_ID >= 50500 && PHP_VERSION_ID < 50512) || (defined('JSON_C_VERSION') && version_compare(phpversion('json'), '1.3.6', '<'))) {
174
+ # $json = preg_replace('/\[\s+\]/', '[]', $json);
175
+ # $json = preg_replace('/\{\s+\}/', '{}', $json);
176
+ # }
177
+
178
+ # return $json;
179
+ # }
180
+
181
+ # * *indent*: a string used to indent levels (default: ''),
182
+ # * *space*: a string that is put after, a : or , delimiter (default: ''),
183
+ # * *space_before*: a string that is put before a : pair delimiter (default: ''),
184
+ # * *object_nl*: a string that is put at the end of a JSON object (default: ''),
185
+ # * *array_nl*: a string that is put at the end of a JSON array (default: ''),
186
+ # * *allow_nan*: true if NaN, Infinity, and -Infinity should be
187
+ # generated, otherwise an exception is thrown if these values are
188
+ # encountered. This options defaults to false.
189
+ # * *max_nesting*: The maximum depth of nesting allowed in the data
190
+ # structures from which JSON is to be generated. Disable depth checking
191
+ # with :max_nesting => false, it defaults to 100.
192
+
193
+ if data.nil?
194
+ return 'null'
195
+ elsif data.is_a?(TrueClass)
196
+ return 'true'
197
+ elsif data.is_a?(FalseClass)
198
+ return 'false'
199
+ elsif data.is_a?(Integer)
200
+ return Integer(data)
201
+ elsif data.is_a?(Float)
202
+ return Float(data)
203
+ else
204
+ begin
205
+ json = JSON.generate(data, { quirks_mode: false })
206
+ rescue JSON::GeneratorError => e
207
+ if e.message === 'only generation of JSON objects or arrays allowed'
208
+ #trick into parsing scalar values by wrapping them in an array
209
+ scalar = data.gsub("\\\\", "\\\\\\")
210
+ if json = JSON::generate([scalar])
211
+ json = json[1..(json.length - 2)]
212
+ end
213
+ end
214
+ end
215
+ end
216
+
217
+ return json unless options
218
+
219
+ result = Composer::Json::JsonFormatter::format(
220
+ json,
221
+ options
222
+ )
223
+
224
+ result
225
+ end
226
+
227
+ # Parses json string and returns hash.
228
+ #
229
+ # Params:
230
+ # +json+ string The json string to parse
231
+ # +file+ string The json file
232
+ #
233
+ # Returns:
234
+ # mixed
235
+ def parse_json(json, file = nil)
236
+ last_error = JSON_ERROR_NONE
237
+
238
+ begin
239
+ data = JSON.parse(json)
240
+ rescue Exception => e
241
+ last_error = e
242
+ end
243
+
244
+ if data.nil? && last_error != JSON_ERROR_NONE
245
+ validate_syntax(json, file)
246
+ raise JSON::ParserError,
247
+ "\"#{file}\" does not contain valid JSON\n
248
+ #{last_error.message}"
249
+ end
250
+
251
+ data
252
+ end
253
+
254
+ def validate_syntax(json, file)
255
+ # JSON::
256
+ # parser = Composer::Json::JsonParser.new
257
+ # if (result = parser.lint(json))
258
+ # raise ParsingError,
259
+ # "\"#{file}\" does not contain valid JSON\n
260
+ # #{result.message}"
261
+ # end
262
+ # if (defined('JSON_ERROR_UTF8') && JSON_ERROR_UTF8 === json_last_error()) {
263
+ # throw new \UnexpectedValueException('"'.$file.'" is not UTF-8, could not parse as JSON');
264
+ # }
265
+ true
266
+ end
267
+ end
268
+ end
269
+ end
270
+ end
@@ -0,0 +1,159 @@
1
+ #
2
+ # This file was ported to ruby from Composer php source code file.
3
+ # Original Source: Composer\Json\JsonFormatter.php
4
+ #
5
+ # (c) Nils Adermann <naderman@naderman.de>
6
+ # Jordi Boggiano <j.boggiano@seld.be>
7
+ #
8
+ # For the full copyright and license information, please view the LICENSE
9
+ # file that was distributed with this source code.
10
+ #
11
+
12
+ # JSON_HEX_TAG => 1
13
+ # JSON_HEX_AMP => 2
14
+ # JSON_HEX_APOS => 4
15
+ # JSON_HEX_QUOT => 8
16
+ # JSON_FORCE_OBJECT => 16
17
+ # JSON_NUMERIC_CHECK => 32
18
+ # JSON_UNESCAPED_SLASHES => 64
19
+ # JSON_PRETTY_PRINT => 128
20
+ # JSON_UNESCAPED_UNICODE => 256
21
+
22
+ module Composer
23
+ module Json
24
+ # * Formats json strings used for php < 5.4 because the json_encode doesn't
25
+ # * supports the flags JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE
26
+ # * in these versions
27
+ #
28
+ # PHP Authors:
29
+ # Konstantin Kudryashiv <ever.zet@gmail.com>
30
+ # Jordi Boggiano <j.boggiano@seld.be>
31
+ #
32
+ # Ruby Authors:
33
+ # Ioannis Kappas <ikappas@devworks.gr>
34
+ class JsonFormatter
35
+
36
+ JSON_HEX_TAG = 1
37
+ JSON_HEX_AMP = 2
38
+ JSON_HEX_APOS = 4
39
+ JSON_HEX_QUOT = 8
40
+ JSON_FORCE_OBJECT = 16
41
+ JSON_NUMERIC_CHECK = 32
42
+ JSON_UNESCAPED_SLASHES = 64
43
+ JSON_PRETTY_PRINT = 128
44
+ JSON_UNESCAPED_UNICODE = 256
45
+
46
+ class << self
47
+ # This code is based on the function found at:
48
+ # http://recursive-design.com/blog/2008/03/11/format-json-with-php/
49
+ #
50
+ # Originally licensed under MIT by Dave Perrett <mail@recursive-design.com>
51
+ #
52
+ # @param json string
53
+ # @param unescape_unicode bool Un escape unicode
54
+ # @param unescape_slashes bool Un escape slashes
55
+ # @return string
56
+ def format(json, options)
57
+
58
+ result = ''
59
+ pos = 0
60
+ str_len = json.length
61
+ indent_str = ' '
62
+ new_line = "\n"
63
+ out_of_quotes = true
64
+ buffer = ''
65
+ no_escape = true
66
+
67
+ for i in 0..(str_len - 1)
68
+
69
+ # Grab the next character in the string
70
+ char = json[i]
71
+
72
+ # Are we inside a quoted string?
73
+ if '"' === char && no_escape
74
+ out_of_quotes = !out_of_quotes
75
+ end
76
+
77
+ if !out_of_quotes
78
+ buffer << char
79
+ no_escape = '\\' === char ? !no_escape : true
80
+ next
81
+ elsif buffer != ''
82
+ if options & JSON_HEX_TAG === JSON_HEX_TAG
83
+ buffer.gsub!('<', '\\u003C')
84
+ buffer.gsub!('>', '\\u003E')
85
+ end
86
+ if options & JSON_HEX_AMP === JSON_HEX_AMP
87
+ buffer.gsub!('&', '\\u0026')
88
+ end
89
+ if options & JSON_HEX_APOS === JSON_HEX_APOS
90
+ buffer.gsub!('\'', '\\u0027')
91
+ end
92
+ if options & JSON_HEX_QUOT === JSON_HEX_QUOT
93
+ buffer.gsub!('\"', '\\u0022')
94
+ end
95
+ if options & JSON_UNESCAPED_SLASHES === JSON_UNESCAPED_SLASHES
96
+ buffer.gsub!('\\/', '/')
97
+ end
98
+ if options & JSON_UNESCAPED_UNICODE === JSON_UNESCAPED_UNICODE
99
+ buffer.gsub!(/\\u([\da-fA-F]{4})/) {|m| [$1].pack('H*').unpack('n*').pack('U*')}
100
+ end
101
+
102
+ result << buffer + char
103
+ buffer = ''
104
+ next
105
+ end
106
+
107
+ if options & JSON_PRETTY_PRINT === JSON_PRETTY_PRINT
108
+ if char === ':'
109
+ # Add a space after the : character
110
+ char << ' '
111
+ elsif char === '}' || char === ']'
112
+ pos -= 1
113
+ prev_char = json[i - 1] #substr(json, i - 1, 1)
114
+
115
+ if prev_char != '{' && prev_char != '['
116
+ # If this character is the end of an element,
117
+ # output a new line and indent the next line
118
+ result << new_line
119
+
120
+ for j in 0..(pos - 1)
121
+ result << indent_str
122
+ end
123
+ else
124
+ # Collapse empty {} and []
125
+ result.rstrip!
126
+ end
127
+ end
128
+ end
129
+
130
+ result << char
131
+
132
+ if options & JSON_PRETTY_PRINT === JSON_PRETTY_PRINT
133
+ # If the last character was the beginning of an element,
134
+ # output a new line and indent the next line
135
+ if char === ',' || char === '{' || char === '['
136
+ result << new_line
137
+ pos += 1 if char === '{' || char === '['
138
+ for j in 0..(pos - 1)
139
+ result << indent_str
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ result
146
+ end
147
+
148
+ def unescape_slashes(s)
149
+ s.gsub('\\/', '/')
150
+ end
151
+
152
+ def unescape_unicode(s)
153
+ s.gsub(/\\u([\da-fA-F]{4})/) {|m| [$1].pack('H*').unpack('n*').pack('U*')}
154
+ end
155
+
156
+ end
157
+ end
158
+ end
159
+ end