scheme_parser 0.1.99

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d1c253509ba415213a2964fca1d0e8b1ffd15013
4
+ data.tar.gz: a7245eba36867d0676dc1c718a201b93369d5904
5
+ SHA512:
6
+ metadata.gz: '086e8be2e128d97afb198d9a1483c8e66ae4f0346519849523ae7125c3f11baec56c2c0b9140954e2f0b33f08bbfffccc6e8e3dccb7d8cac477f7c0d965440d2'
7
+ data.tar.gz: 36814471e1926dac2a2bcf79c8dde1e0497332c30124a0484902365e9bb1071e7fbf3e87a7887965de8d2e7aeda8ce337981163b1aa79d5339ef43c5c2622042
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ *~
12
+ *.sw*
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.rvmrc ADDED
@@ -0,0 +1,53 @@
1
+ #!/bin/bash -l
2
+
3
+ set -x
4
+ gemruby=$(cat Gemfile|grep ^ruby)
5
+ if [[ "$gemruby" == *engine* ]]; then
6
+ ruby_type=$(echo "$gemruby"|sed "s/.*engine[: ][^\"']\+.\([^\"']\+\).*/\1/")
7
+ ruby_version=$(echo "$gemruby"|sed "s/.*engine_version[: ][^\"']\+.\([^\"']\+\).*/\1/")
8
+ fi
9
+
10
+ if [ -z "$ruby_version" -o -z "$ruby_type" ]; then
11
+ set -
12
+ info=$(rvm info)
13
+ set -x
14
+ ruby_type=$(echo "$info"|grep "^ ruby:" -A 6|grep " interpreter"|sed "s/[^\"]*\"\([^\"]*\)\"/\1/")
15
+
16
+ ruby_version=$(echo "$info"|grep "^ ruby:" -A 6|grep " version"|sed "s/[^\"]*\"\([^\"]*\)\"/\1/")
17
+ if [ -z "$ruby_version" ]; then
18
+ ruby_path=$(echo "$info"|grep "^ homes:" -A 2|grep " ruby:"|sed "s/[^\"]*\"\([^\"]*\)\"/\1/")
19
+ ruby_type=$(basename "$ruby_path"|sed "s/^\([^\[]\+\)-.*/\1/")
20
+ ruby_version=$(basename "$ruby_path"|sed "s/.*-\([^\[]\+\)/\1/")
21
+ fi
22
+ fi
23
+
24
+ ruby_gemset=$(basename $(pwd))
25
+ if [ "$ruby_version" = 'not set' ]; then
26
+ # selecting first ruby
27
+ set -
28
+ list=$(rvm list)
29
+ set -x
30
+ ruby_type=$(echo "$list"|grep rubies -A 2|grep "\[.*\]"|sed "s/.* \([^\[]\+\)-.*/\1/")
31
+ ruby_version=$(echo "$list"|grep rubies -A 2|grep "\[.*\]"|sed "s/.*-\([^\[]\+\)\[.*/\1/")
32
+ fi
33
+
34
+ if [ -z "$(cat ~/.rvm/config/known_strings|grep ${ruby_type}-$ruby_version)" ]; then
35
+ echo ${ruby_type}-$ruby_version >> ~/.rvm/config/known_strings
36
+ fi
37
+ echo will use ${ruby_type}-$ruby_version@$ruby_gemset with create flag
38
+ set -
39
+ rvm use ${ruby_type}-$ruby_version@$ruby_gemset --create
40
+
41
+ set -x
42
+ if [ -z "$(echo "$PATH"|grep "$(pwd)/.bundle/bin")" ]; then
43
+ if [ -z "$_ORIGINAL_GEM_PATH" ]; then
44
+ export _ORIGINAL_GEM_PATH=$GEM_PATH
45
+ fi
46
+ export PATH=$(pwd)/bin:$(pwd)/.bundle/bin:$PATH
47
+ export GEM_HOME=$(pwd)/.bundle
48
+ export GEM_PATH=$GEM_HOME:$GEM_PATH
49
+ bundler=$(gem which bundler)
50
+ export RUBYLIB=$(dirname $bundler)
51
+ fi
52
+ set -
53
+
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.2.2
5
+ before_install: gem install bundler -v 1.12.5
@@ -0,0 +1,49 @@
1
+ # Contributor Code of Conduct
2
+
3
+ As contributors and maintainers of this project, and in the interest of
4
+ fostering an open and welcoming community, we pledge to respect all people who
5
+ contribute through reporting issues, posting feature requests, updating
6
+ documentation, submitting pull requests or patches, and other activities.
7
+
8
+ We are committed to making participation in this project a harassment-free
9
+ experience for everyone, regardless of level of experience, gender, gender
10
+ identity and expression, sexual orientation, disability, personal appearance,
11
+ body size, race, ethnicity, age, religion, or nationality.
12
+
13
+ Examples of unacceptable behavior by participants include:
14
+
15
+ * The use of sexualized language or imagery
16
+ * Personal attacks
17
+ * Trolling or insulting/derogatory comments
18
+ * Public or private harassment
19
+ * Publishing other's private information, such as physical or electronic
20
+ addresses, without explicit permission
21
+ * Other unethical or unprofessional conduct
22
+
23
+ Project maintainers have the right and responsibility to remove, edit, or
24
+ reject comments, commits, code, wiki edits, issues, and other contributions
25
+ that are not aligned to this Code of Conduct, or to ban temporarily or
26
+ permanently any contributor for other behaviors that they deem inappropriate,
27
+ threatening, offensive, or harmful.
28
+
29
+ By adopting this Code of Conduct, project maintainers commit themselves to
30
+ fairly and consistently applying these principles to every aspect of managing
31
+ this project. Project maintainers who do not follow or enforce the Code of
32
+ Conduct may be permanently removed from the project team.
33
+
34
+ This code of conduct applies both within project spaces and in public spaces
35
+ when an individual is representing the project or its community.
36
+
37
+ Instances of abusive, harassing, or otherwise unacceptable behavior may be
38
+ reported by contacting a project maintainer at majioa@yandex.ru. All
39
+ complaints will be reviewed and investigated and will result in a response that
40
+ is deemed necessary and appropriate to the circumstances. Maintainers are
41
+ obligated to maintain confidentiality with regard to the reporter of an
42
+ incident.
43
+
44
+ This Code of Conduct is adapted from the [Contributor Covenant][homepage],
45
+ version 1.3.0, available at
46
+ [http://contributor-covenant.org/version/1/3/0/][version]
47
+
48
+ [homepage]: http://contributor-covenant.org
49
+ [version]: http://contributor-covenant.org/version/1/3/0/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in xparser.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Malo Skrylevo
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.
@@ -0,0 +1,41 @@
1
+ # XParser
2
+
3
+ Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/x_parser`. To experiment with that code, run `bin/console` for an interactive prompt.
4
+
5
+ TODO: Delete this and the text above, and describe your gem
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'x_parser'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install x_parser
22
+
23
+ ## Usage
24
+
25
+ TODO: Write usage instructions here
26
+
27
+ ## Development
28
+
29
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
30
+
31
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
32
+
33
+ ## Contributing
34
+
35
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/x_parser. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct.
36
+
37
+
38
+ ## License
39
+
40
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
41
+
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "xparser"
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,13 @@
1
+ require "scheme/parser/version"
2
+ require "scheme/parser/methods"
3
+ require "scheme/parser/base"
4
+
5
+ module Scheme::Parser
6
+ class << self
7
+ def included kls
8
+ kls.extend Scheme::Parser::Base
9
+ kls.extend Scheme::Parser::Methods
10
+ end
11
+ end
12
+ end
13
+
@@ -0,0 +1,445 @@
1
+ require 'scheme/parser/version'
2
+
3
+ require 'nokogiri'
4
+
5
+ module Scheme::Parser::Base
6
+ class ParameterError < StandardError;end
7
+
8
+ attr_reader :errors, :this
9
+
10
+ Struct.new('Value', :text)
11
+
12
+ def import xml
13
+ model_attrs = parse(xml)
14
+ #name = File.basename(attrs.keys.first, '_attributes')
15
+ #model = name.singularize.camelize.constantize
16
+ #model_attrs = attrs.values.first
17
+
18
+ model = self
19
+ key = current_options[:key].to_s || 'id'
20
+
21
+ if model_attrs[key]
22
+ if instance = model.where(key => model_attrs[key]).first
23
+ instance.update!(model_attrs)
24
+ else
25
+ instance = model.new(model_attrs)
26
+ instance.save!
27
+ end
28
+ # binding.pry
29
+ # model_attrs.each do |name, value|
30
+ # if value.is_a?(Array)
31
+ # _update_array(name, value)
32
+ # end
33
+ # end
34
+
35
+ # binding.pry
36
+ end
37
+ # rescue => e
38
+ # err_text = "Failed to import record #{name} with messages '#{e.message}'"
39
+ # if instance && !(errs = show_errors(instance.errors)).empty?
40
+ # err_text += " and #{errs}"
41
+ # end
42
+ # err_text += " at #{e.backtrace.first}"
43
+ #
44
+ # Rails.logger.error(err_text)
45
+ # error "", err_text
46
+ end
47
+
48
+ def parse xml
49
+ clean_errors
50
+ filtered = xml.is_a?(String) && xml.gsub(/[\r\u{feff}]/,"") || xml
51
+ doc = filtered.kind_of?(Nokogiri::XML::Element) && filtered || Nokogiri::XML.parse(filtered)
52
+ @this = {}
53
+ attrs = each_field_for(self.schemes[nil], doc)
54
+ #binding.pry
55
+ raise ParameterError if errors.present?
56
+ attrs
57
+ end
58
+
59
+ # def _update_array name, array
60
+ # array.each do |value|
61
+ # if value['id']
62
+ # name = File.basename(name, '_attributes')
63
+ # m = name.singularize.camelize.constantize
64
+ # i = m.where(id: value['id']).first
65
+ # binding.pry
66
+ # i.update!(value)
67
+ # end
68
+ # end
69
+ #
70
+ # array.delete_if { |value | value['id'] }
71
+ # end
72
+ #
73
+ def import_attrs attrs
74
+ end
75
+
76
+ protected
77
+
78
+ def clean_errors
79
+ @errors = {}
80
+ end
81
+
82
+ def show_errors errors
83
+ errors.messages.map do |field, msgs|
84
+ msgs.map do |msg|
85
+ "#{field} #{msg}"
86
+ end
87
+ end.flatten.join(", ")
88
+ end
89
+
90
+ def touch_file file
91
+ if file =~ /\.zip$/i
92
+ Dir.mktmpdir do |dir|
93
+ unzip(dir, file).each do |xml|
94
+ Rails.logger.info("Xml file to proceed #{xml}")
95
+
96
+ yield(IO.read(xml))
97
+ end
98
+ end
99
+ else
100
+ yield(IO.read(file))
101
+ end
102
+ end
103
+
104
+ def unzip dir, file
105
+ if defined?(Zip)
106
+ Zip::File.open(file) do |zip_file|
107
+ zip_file.map do |entry|
108
+ tname = File.join(dir, file, entry.name)
109
+ FileUtils.mkdir_p(File.dirname(tname))
110
+ entry.extract(tname)
111
+ tname
112
+ end
113
+ end
114
+ end
115
+ end
116
+
117
+ def each_field_for schema, xml_context
118
+ schema.map do |name, options|
119
+ next unless name # skip settings
120
+
121
+ v = type_value(name, options, xml_context)
122
+
123
+ real_name = options[:multiple] && name.pluralize || name
124
+ is_assign = [Array, Hash].any? { |x| x === v }
125
+ key = is_assign && "#{real_name}_attributes" || real_name
126
+
127
+ if handler = handler_for(options[:if])
128
+ v = handler[v]
129
+ end
130
+
131
+ @this[name] ||= []
132
+ @this[name] << v
133
+
134
+ [ key, v ]
135
+ end.compact.select {|_, v| !v.nil? }.to_h
136
+ end
137
+
138
+ def type_value name, options, xml_context
139
+ value = case options[:type]
140
+ when :field
141
+ v = select_value(name, xml_context, options[:contexts], options).first
142
+ v = ! v.respond_to?(:text) && v || v.text
143
+ v = v.respond_to?(:strip) && v.strip || v
144
+ v.present? && v || nil
145
+
146
+ when :scheme
147
+ scheme_value(name, options, xml_context)
148
+
149
+ when :reference
150
+ reference_value(name, options, xml_context)
151
+ end
152
+
153
+ if options[:on_complete]
154
+ value = handled_value(options[:on_complete], value, name, xml_context, options).text
155
+ end
156
+
157
+ if options[:required] && value.nil?
158
+ error(name, "field is required")
159
+ end
160
+
161
+ ### apply method defined by :map to the value
162
+ if value && options[:map]
163
+ value = value.send(options[:map].to_s)
164
+ end
165
+
166
+ value
167
+ end
168
+
169
+ def is_update_required(name, xml_context, options, object)
170
+ base = object.send(options[:update_field])
171
+ inx, inc = find_new_contexts(name, xml_context, options)
172
+ o = select_value(options[:update], inx, this_contexts(inc), options).first
173
+
174
+ base < Time.parse(o.text)
175
+ end
176
+
177
+ def attributes_update name, options, xml_context
178
+ if options[:update] && options[:update_field] &&
179
+ ref = reference_value(name, options, xml_context)
180
+ if is_update_required(name, xml_context, options, ref)
181
+ { "id" => ref.id }
182
+ else
183
+ nil
184
+ end
185
+ elsif ref = reference_value(name, options, xml_context)
186
+ # binding.pry if name =~ /lot/
187
+ if ref.is_a?(Array)
188
+ ref.map { |r| { "id" => r.id } }
189
+ else
190
+ { "id" => ref.id }
191
+ end
192
+ else
193
+ {}
194
+ end
195
+ end
196
+
197
+ def scheme_value name, options, xml_context
198
+ if attrs = attributes_update(name, options, xml_context)
199
+ # binding.pry if name.to_s =~ /lot_apps/
200
+
201
+ value, i = select_value(name, xml_context, options[:contexts], options)
202
+ # binding.pry if name =~ /lot/
203
+ if value.text.present?
204
+ as = options[:contexts][i][:scheme] || options[:as] || name
205
+ # binding.pry if name.to_s =~ /lot_item/
206
+ scheme_name = self.scheme_name(as)
207
+ vvv =
208
+ if options[:multiple]
209
+
210
+ if attrs.is_a?(Array)
211
+ values = [ value, attrs ].transpose
212
+ values.map do |(v, attr)|
213
+ # binding.pry
214
+ each_field_for(self.schemes[scheme_name], v).merge(attr)
215
+ end
216
+ else
217
+ value.map { |v| each_field_for(self.schemes[scheme_name], v) }
218
+ end
219
+ else
220
+ each_field_for(self.schemes[scheme_name], value).merge(attrs)
221
+ end
222
+ # binding.pry if name =~ /lots?/
223
+ vvv
224
+ end
225
+ end
226
+ end
227
+
228
+ def find_new_contexts name, xml_context, options
229
+ new_contexts = self.filter_hashes(options[:contexts],
230
+ Scheme::Parser::Methods::PURE_CONTEXT_KEYS)
231
+ inx, index = select_value(name, xml_context, new_contexts, options)
232
+
233
+ if inx.text
234
+ inc = options[:contexts][index]
235
+ end
236
+
237
+ [inx, inc]
238
+ end
239
+
240
+ def reference_value name, options, xml_context
241
+ inx, inc = find_new_contexts(name, xml_context, options)
242
+
243
+ if inc && inc[:by]
244
+ begin
245
+ value, = select_value(inc[:by], inx, this_contexts(inc), options)
246
+ rescue NoMethodError
247
+ error(name, "field error for '#{inc[:by]}' in context: #{inx.inspect}")
248
+ end
249
+
250
+ model = model_for(name, options)
251
+
252
+ if field = inc[:field] || inc[:by]
253
+ # binding.pry
254
+ if options[:multiple] && value.respond_to?(:size)
255
+ rela = model.where(field => value.map(&:text))
256
+ else
257
+ rela = model.where(field => value.text)
258
+ end
259
+ end
260
+
261
+ # binding.pry if name.to_s =~ /placing_method/
262
+ if rela.empty? && field = inc[:re_field]
263
+ begin
264
+ rela = model.where("? ~* #{field}", value.text).order(code: :desc)
265
+ rescue Java::JavaLang::NoSuchMethodError
266
+ error(name, "reference is unavailable to find out via referenced field" +
267
+ " '#{field}' with data: #{value.text}")
268
+ []
269
+ end
270
+ end
271
+
272
+ # binding.pry if name =~ /lot/
273
+ if options[:multiple]
274
+ rela_a = rela.empty? && [nil] || rela
275
+ value =
276
+ rela_a.map do |rela_v|
277
+ if inc[:on_found]
278
+ handled_value(inc[:on_found], rela_v, name, xml_context, options).text
279
+ else
280
+ rela_v
281
+ end
282
+ end
283
+ else
284
+ value = rela.first
285
+ if inc[:on_found]
286
+ value = handled_value(inc[:on_found], value, name, xml_context, options).text
287
+ end
288
+ end
289
+
290
+ this['relations'] ||= {}
291
+ this['relations'][name] ||= []
292
+ this['relations'][name] << value
293
+
294
+ # binding.pry if name =~ /lot/
295
+ value
296
+ end
297
+ end
298
+
299
+ def error name, error
300
+ # binding.pry
301
+ @errors ||= {}
302
+ @errors[name] ||= []
303
+ @errors[name] << error
304
+
305
+ nil
306
+ end
307
+
308
+ def this_contexts in_context = {}
309
+ [ in_context.merge({context: [""], from: nil}) ]
310
+ end
311
+
312
+ def model_for name, options
313
+ (options[:as] || name).to_s.singularize.camelize.constantize
314
+ end
315
+
316
+ def search_in xml_context, path, options
317
+ # TODO since nokogiri 1.8.2 dones not understand namespaces in attribute names
318
+ # we shold crop it from path, and traverse manually
319
+ res = xml_context.xpath(path)
320
+
321
+ options[:multiple] && res || res.first
322
+ rescue Nokogiri::XML::XPath::SyntaxError => e
323
+ err_text = "Failed to navigate path with messages '#{e.message}'"
324
+ error path, err_text
325
+ Rails.logger.error(err_text)
326
+ nil
327
+ end
328
+
329
+ # +select_value+ выборка общего обработчика контекста для заданного атрибута.
330
+ # Осуществляет выбор между обработчиками встроенным и заданным пользователем в
331
+ # подробностях (options).
332
+ #
333
+ def select_value name, xml_context, contexts, options
334
+ contexts.map.with_index do |c, i|
335
+ [ c, i ]
336
+ end.reduce([nil, nil]) do |(value, val_index), (context, index)|
337
+ next [value, val_index] if value && value.text.present?
338
+
339
+ new =
340
+ if context[:on_proceed]
341
+ midvalue = value(name, xml_context, context, options)
342
+ handled_value(context[:on_proceed], midvalue, name, xml_context, options)
343
+ else
344
+ value(name, xml_context, context, options)
345
+ end
346
+
347
+ # binding.pry if name =~ /lot/
348
+ [ new, index ]
349
+ end
350
+ end
351
+
352
+ def handler_for handler
353
+ case handler
354
+ when Proc, Method
355
+ handler
356
+ when NilClass
357
+ nil
358
+ else
359
+ self.methods.include?(handler) && self.method(handler)
360
+ end
361
+ end
362
+
363
+ def handled_value handler, value, name, xml_context, options
364
+ method = handler_for(handler)
365
+
366
+ # new_value =
367
+ # if context[:by]
368
+ # c = [ this_contexts(context).first.merge(handler: nil) ]
369
+ # select_value(context[:by], value, c, options).first
370
+ # else
371
+ # value
372
+ # end
373
+ #
374
+ if method
375
+ new_value =
376
+ if value.is_a?(Nokogiri::XML::NodeSet)
377
+ new = Nokogiri::XML::NodeSet.new(value.document)
378
+ new_value = value.map do |subvalue|
379
+ args = [ subvalue, name, xml_context, options ]
380
+ method[*args[0...method.arity]]
381
+ # if subvalue.nil? || subvalue.is_a?(Nokogiri::XML::Searchable)
382
+ # subvalue
383
+ # else
384
+ # binding.pry
385
+ # Struct::Value.new(method[*args[0...method.arity]])
386
+ # end
387
+ end.compact.reduce(new) { |new, x| new << x }
388
+ new_value
389
+ else
390
+ args = [ value, name, xml_context, options ]
391
+ Struct::Value.new(method[*args[0...method.arity]])
392
+ end
393
+ else
394
+ value
395
+ end
396
+ end
397
+
398
+ def value name, xml_context, context, options
399
+ paths = paths(name, context)
400
+ new_context = paths.reduce(nil) do |new_context, path|
401
+ new_context.blank? && search_in(xml_context, path, options) || new_context
402
+ end
403
+
404
+ if new_context.respond_to?(:text)
405
+ new_context
406
+ else
407
+ Struct::Value.new(new_context)
408
+ end
409
+ end
410
+
411
+ # [1,2].map { |x| [3,4].map { |y| [x, y] } }.flatten(1)
412
+ #=> [[1, 3], [1, 4], [2, 3], [2, 4]]
413
+ def integrate ary1, ary2
414
+ ary1.map { |x| ary2.map { |y| [x, y] } }.flatten(1)
415
+ end
416
+
417
+ # +join_context+ объединяет переданные строки контекста в единую строку в
418
+ # формате xpath.
419
+ #
420
+ # Пример 1:
421
+ # args: "ns2:documentationDelivery", "deliveryEndDateTime"
422
+ # out: ".//ns2:documentationDelivery//:deliveryEndDateTime"]
423
+ #
424
+ # Пример 2:
425
+ # args: "", "ns2:contact"
426
+ # out: ".//ns2:contact"
427
+ #
428
+ # Пример 3:
429
+ # args: nil, "contact", nil
430
+ # out: ".//:contact"
431
+ #
432
+ def paths name, context
433
+ froms = [ context[:from] || name ].flatten.compact
434
+ ctxs = [ context[:reset_context] || context[:context] ].flatten.compact
435
+ prefix = context[:reset_context] && "" || "."
436
+
437
+ integrate(ctxs, froms).map do |from|
438
+ from.map do |a|
439
+ a.split('/')
440
+ end.flatten.map do |a|
441
+ /(:|\A..\z)/ =~ a && a || "#{a}"
442
+ end.unshift(prefix).join("//")
443
+ end
444
+ end
445
+ end
@@ -0,0 +1,98 @@
1
+ module Scheme::Parser::Methods
2
+ CONTEXT_KEYS = [ :by, :re, :from, :context, :field, :reset_context, :on_proceed, :scheme, :on_found ]
3
+ FIELD_KEYS = [ :required, :as, :if, :update, :update_field, :on_complete, :map ]
4
+ PURE_CONTEXT_KEYS = [ :from, :context, :reset_context ]
5
+
6
+ def scheme name, &block
7
+ current_scheme_path << name.to_s
8
+ yield
9
+ current_scheme_path.pop
10
+ end
11
+
12
+ def context name, &block
13
+ current_context << name.to_s
14
+ yield
15
+ current_context.pop
16
+ end
17
+
18
+ def scheme_name name
19
+ name.to_s.singularize
20
+ end
21
+
22
+ def use_default_key name
23
+ current_options[:key] = name
24
+ end
25
+
26
+ def has_field name, *args
27
+ current_scheme[scheme_name(name)] = make_options(:field, args)
28
+ end
29
+
30
+ def has_scheme name, *args
31
+ current_scheme[scheme_name(name)] = make_options(:scheme, args)
32
+ end
33
+
34
+ def has_schemes name, *args
35
+ current_scheme[scheme_name(name)] = make_options(:scheme, args, true)
36
+ end
37
+
38
+ def has_reference name, *args
39
+ current_scheme[scheme_name(name)] = make_options(:reference, args)
40
+ end
41
+
42
+ # has_reference :lot, [
43
+ # { by: 'guid', from: 'ns2:lot', reset_context: 'ns2:protocolLotApplications' },
44
+ # { by: 'ns2:guid', from: 'ns2:protocolLotApplications', reset_context: 'ns2:lotApplicationsList' },
45
+ # ], required: true
46
+ #
47
+ # has_field :rate, { required: true },
48
+ # { from: [ 'ns2:winnerIndication' ], handler: proc { |value| ! (value !~ /W/) } },
49
+ # { from: [ 'ns2:applicationPlace' ], handler: proc { |value| ! (value !~ /F/) } },
50
+ # { from: [ 'ns2:applicationRate' ], handler: proc { |value| ! (value !~ /1/) } }
51
+
52
+ # TODO move to protected
53
+ #
54
+ def make_options type, args, multiple = false
55
+ contexts = make_contexts(filter_hashes(args, CONTEXT_KEYS))
56
+ local = filter_hashes(args, FIELD_KEYS).reduce({}) { |r, x| r.merge(x) }
57
+ { type: type, multiple: multiple, contexts: contexts }.merge(local)
58
+ end
59
+
60
+ def filter_hashes hashes, by
61
+ hashes.flatten.map do |hash|
62
+ (hash.keys & by).map { |x| [ x, hash[x] ] }.to_h
63
+ end.select do |hash|
64
+ hash.any?
65
+ end
66
+ end
67
+
68
+ def make_contexts contexts
69
+ (contexts.empty? && [{}] || contexts).map do |options|
70
+ ctxs = [ options[:context] || "" ].flatten
71
+ options[:context] = ctxs.map do |ctx|
72
+ [ current_context, ctx ].flatten.compact.join('/')
73
+ end
74
+
75
+ options
76
+ end
77
+ end
78
+
79
+ def current_context
80
+ @current_context ||= []
81
+ end
82
+
83
+ def current_scheme_path
84
+ @current_scheme_path ||= []
85
+ end
86
+
87
+ def schemes
88
+ @schemes ||= {}
89
+ end
90
+
91
+ def current_options
92
+ current_scheme[nil] ||= {}
93
+ end
94
+
95
+ def current_scheme
96
+ schemes[current_scheme_path.last] ||= {}
97
+ end
98
+ end
@@ -0,0 +1,5 @@
1
+ module Scheme
2
+ module Parser
3
+ VERSION = "0.1.99"
4
+ end
5
+ end
@@ -0,0 +1,38 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'scheme/parser/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "scheme_parser"
8
+ spec.version = Scheme::Parser::VERSION
9
+ spec.authors = ["Malo Skrylevo"]
10
+ spec.email = ["majioa@yandex.ru"]
11
+
12
+ spec.summary = %q{Scheme Parser parse an XML/HTML document to hashly-structured format}
13
+ spec.description = %q{Scheme Parser parse an XML/HTML document to hashly-structured format,
14
+ prepared to import to a DB, or just serialize it and save it as is}
15
+ spec.homepage = "https://github.com/majioa/scheme_parser"
16
+ spec.license = "MIT"
17
+
18
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
19
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
20
+ if spec.respond_to?(:metadata)
21
+ spec.metadata["allowed_push_host"] = "https://rubygems.org"
22
+ else
23
+ raise "RubyGems 2.0 or newer is required to protect against public gem pushes."
24
+ end
25
+
26
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
+ spec.bindir = "exe"
28
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
29
+ spec.require_paths = ["lib"]
30
+
31
+ spec.add_runtime_dependency "nokogiri", "~> 1.8.2", '>= 1.8.2'
32
+ # spec.add_dependency "ox", platform: ruby
33
+
34
+ spec.add_development_dependency "bundler", "~> 1.12"
35
+ spec.add_development_dependency "rake", "~> 10.0"
36
+ spec.add_development_dependency "pry", "~> 0.10"
37
+ spec.add_development_dependency "rspec", "~> 3.0"
38
+ end
metadata ADDED
@@ -0,0 +1,139 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: scheme_parser
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.99
5
+ platform: ruby
6
+ authors:
7
+ - Malo Skrylevo
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-02-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.2
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 1.8.2
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: 1.8.2
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 1.8.2
33
+ - !ruby/object:Gem::Dependency
34
+ name: bundler
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '1.12'
40
+ type: :development
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.12'
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '10.0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: pry
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '0.10'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '0.10'
75
+ - !ruby/object:Gem::Dependency
76
+ name: rspec
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '3.0'
89
+ description: |-
90
+ Scheme Parser parse an XML/HTML document to hashly-structured format,
91
+ prepared to import to a DB, or just serialize it and save it as is
92
+ email:
93
+ - majioa@yandex.ru
94
+ executables: []
95
+ extensions: []
96
+ extra_rdoc_files: []
97
+ files:
98
+ - ".gitignore"
99
+ - ".rspec"
100
+ - ".rvmrc"
101
+ - ".travis.yml"
102
+ - CODE_OF_CONDUCT.md
103
+ - Gemfile
104
+ - LICENSE.txt
105
+ - README.md
106
+ - Rakefile
107
+ - bin/console
108
+ - bin/setup
109
+ - lib/scheme/parser.rb
110
+ - lib/scheme/parser/base.rb
111
+ - lib/scheme/parser/methods.rb
112
+ - lib/scheme/parser/version.rb
113
+ - scheme_parser.gemspec
114
+ homepage: https://github.com/majioa/scheme_parser
115
+ licenses:
116
+ - MIT
117
+ metadata:
118
+ allowed_push_host: https://rubygems.org
119
+ post_install_message:
120
+ rdoc_options: []
121
+ require_paths:
122
+ - lib
123
+ required_ruby_version: !ruby/object:Gem::Requirement
124
+ requirements:
125
+ - - ">="
126
+ - !ruby/object:Gem::Version
127
+ version: '0'
128
+ required_rubygems_version: !ruby/object:Gem::Requirement
129
+ requirements:
130
+ - - ">="
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ requirements: []
134
+ rubyforge_project:
135
+ rubygems_version: 2.6.12
136
+ signing_key:
137
+ specification_version: 4
138
+ summary: Scheme Parser parse an XML/HTML document to hashly-structured format
139
+ test_files: []