expressir 2.3.0 → 2.3.2

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop_todo.yml +480 -49
  3. data/benchmark/srl_benchmark.rb +47 -34
  4. data/benchmark/srl_native_benchmark.rb +20 -16
  5. data/benchmark/srl_ruby_benchmark.rb +14 -12
  6. data/expressir.gemspec +2 -2
  7. data/lib/expressir/changes/item_change.rb +0 -1
  8. data/lib/expressir/changes/mapping_change.rb +0 -1
  9. data/lib/expressir/changes/schema_change.rb +0 -1
  10. data/lib/expressir/changes/version_change.rb +0 -1
  11. data/lib/expressir/commands/changes_import_eengine.rb +2 -2
  12. data/lib/expressir/commands/validate_ascii.rb +0 -1
  13. data/lib/expressir/eengine/arm_compare_report.rb +0 -1
  14. data/lib/expressir/eengine/changes_section.rb +0 -1
  15. data/lib/expressir/eengine/mim_compare_report.rb +0 -1
  16. data/lib/expressir/eengine/modified_object.rb +0 -1
  17. data/lib/expressir/express/builder.rb +64 -21
  18. data/lib/expressir/express/builders/built_in_builder.rb +4 -2
  19. data/lib/expressir/express/builders/entity_decl_builder.rb +8 -4
  20. data/lib/expressir/express/builders/expression_builder.rb +0 -6
  21. data/lib/expressir/express/builders/function_decl_builder.rb +8 -8
  22. data/lib/expressir/express/builders/procedure_decl_builder.rb +8 -8
  23. data/lib/expressir/express/builders/rule_decl_builder.rb +8 -8
  24. data/lib/expressir/express/builders/syntax_builder.rb +2 -44
  25. data/lib/expressir/express/formatters/remark_formatter.rb +1 -3
  26. data/lib/expressir/express/parser.rb +234 -14
  27. data/lib/expressir/express/remark_attacher.rb +79 -169
  28. data/lib/expressir/express/transformer/remark_handling.rb +0 -1
  29. data/lib/expressir/model/declarations/schema.rb +0 -1
  30. data/lib/expressir/model/exp_file.rb +2 -1
  31. data/lib/expressir/model/model_element.rb +1 -1
  32. data/lib/expressir/model/repository.rb +8 -9
  33. data/lib/expressir/model/search_engine.rb +7 -6
  34. data/lib/expressir/package/builder.rb +3 -1
  35. data/lib/expressir/package/metadata.rb +0 -1
  36. data/lib/expressir/schema_manifest.rb +0 -1
  37. data/lib/expressir/schema_manifest_entry.rb +0 -1
  38. data/lib/expressir/version.rb +1 -1
  39. metadata +15 -15
@@ -1,7 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "set"
4
-
5
3
  module Expressir
6
4
  module Express
7
5
  # Handles attaching remarks (comments) to model elements after parsing.
@@ -13,6 +11,17 @@ module Expressir
13
11
  # 2. Proximity-based matching for simple tags
14
12
  # 3. NOT creating spurious schema-level items for ambiguous tags
15
13
  class RemarkAttacher
14
+ # Child collection attributes to walk when traversing model elements.
15
+ # These are the named collections on model elements (e.g., entity.attributes,
16
+ # schema.entities, function.parameters). Extracted to a constant to avoid
17
+ # duplication between calculate_children_end_line and collect_children.
18
+ CHILD_COLLECTION_ATTRIBUTES = %i[
19
+ schemas types entities functions procedures rules constants
20
+ attributes derived_attributes inverse_attributes
21
+ where_rules unique_rules informal_propositions
22
+ parameters variables statements items remark_items
23
+ ].freeze
24
+
16
25
  def initialize(source)
17
26
  @source = source
18
27
  @attached_spans = Set.new
@@ -25,8 +34,22 @@ module Expressir
25
34
  def attach(model)
26
35
  @model = model
27
36
  remarks = extract_all_remarks
28
- attach_tagged_remarks(model, remarks)
29
- attach_untagged_remarks(model, remarks)
37
+
38
+ # Build nodes_with_positions ONCE for both tagged and untagged remark passes.
39
+ # This avoids double tree walk (381K nodes × 2 = 762K visits) which was
40
+ # the largest memory overhead in remark attachment (~430MB for large files).
41
+ nodes_with_positions = build_sorted_nodes_with_positions(model)
42
+
43
+ attach_tagged_remarks(model, remarks, nodes_with_positions)
44
+ attach_untagged_remarks(remarks, nodes_with_positions)
45
+
46
+ # Free expensive data structures after attachment is complete.
47
+ # These are only needed during the attach process.
48
+ @source = nil
49
+ @source_lines = nil
50
+ @scope_map = nil
51
+ @line_cache = nil
52
+
30
53
  model
31
54
  end
32
55
 
@@ -124,9 +147,7 @@ module Expressir
124
147
  @line_cache[position] ||= @source.byteslice(0...position).count("\n") + 1
125
148
  end
126
149
 
127
- alias get_line_number_from_offset get_line_number
128
-
129
- def attach_tagged_remarks(model, remarks)
150
+ def attach_tagged_remarks(model, remarks, nodes_with_positions)
130
151
  tagged = remarks.select { |r| r[:tag] }
131
152
  return if tagged.empty?
132
153
 
@@ -136,12 +157,6 @@ module Expressir
136
157
  # This is the key optimization that makes scope lookup O(1) per remark
137
158
  @scope_map ||= build_scope_map
138
159
 
139
- # Collect nodes with positions for position-based fallback
140
- nodes_with_positions = []
141
- collect_nodes_with_positions(model, nodes_with_positions)
142
- # Use stable sort to ensure deterministic ordering across Ruby versions
143
- nodes_with_positions.sort_by!.with_index { |n, i| [n[:position] || Float::INFINITY, i] }
144
-
145
160
  tagged.sort_by { |r| r[:position] }.each do |remark|
146
161
  next if @attached_spans.include?(remark[:position])
147
162
 
@@ -242,7 +257,8 @@ module Expressir
242
257
  end
243
258
  # Fall back to schema prefix
244
259
  if target.nil?
245
- target = create_implicit_remark_item(model, tag, get_schema_ids(model))
260
+ target = create_implicit_remark_item(model, tag,
261
+ get_schema_ids(model))
246
262
  end
247
263
  end
248
264
 
@@ -280,6 +296,7 @@ module Expressir
280
296
 
281
297
  nil
282
298
  end
299
+
283
300
  # Done once per RemarkAttacher instance (O(file_lines)).
284
301
  # Each find_containing_scope call then becomes O(1).
285
302
  def build_scope_map
@@ -557,102 +574,6 @@ module Expressir
557
574
  find_containing_scope_position(remark_line, nodes_with_positions)
558
575
  end
559
576
 
560
- def find_scope_by_text_search(remark_line)
561
- lines = source_lines
562
- return nil if remark_line < 1 || remark_line > lines.length
563
-
564
- # Track nested scopes by searching backwards from remark_line
565
- scope_stack = []
566
-
567
- lines.each_with_index do |line, idx|
568
- line_num = idx + 1
569
- break if line_num > remark_line
570
-
571
- # Check for START keywords first
572
- if line =~ /^\s*SCHEMA\s+(\w+)/i
573
- scope_stack << { type: :schema, name: $1, line: line_num }
574
- end
575
-
576
- if line =~ /^\s*FUNCTION\s+(\w+)/i
577
- scope_stack << { type: :function, name: $1, line: line_num }
578
- end
579
-
580
- if line =~ /^\s*PROCEDURE\s+(\w+)/i
581
- scope_stack << { type: :procedure, name: $1, line: line_num }
582
- end
583
-
584
- if line =~ /^\s*RULE\s+(\w+)/i
585
- scope_stack << { type: :rule, name: $1, line: line_num }
586
- end
587
-
588
- if line =~ /^\s*ENTITY\s+(\w+)/i
589
- scope_stack << { type: :entity, name: $1, line: line_num }
590
- end
591
-
592
- if line =~ /^\s*TYPE\s+(\w+)/i
593
- scope_stack << { type: :type, name: $1, line: line_num }
594
- end
595
-
596
- # Then check for END keywords (to handle inline closures on same line)
597
- if (line =~ /END_TYPE/i) && (scope_stack.last&.dig(:type) == :type)
598
- scope_stack.pop
599
- end
600
- if (line =~ /END_FUNCTION/i) && (scope_stack.last&.dig(:type) == :function)
601
- scope_stack.pop
602
- end
603
- if (line =~ /END_PROCEDURE/i) && (scope_stack.last&.dig(:type) == :procedure)
604
- scope_stack.pop
605
- end
606
- if (line =~ /END_RULE/i) && (scope_stack.last&.dig(:type) == :rule)
607
- scope_stack.pop
608
- end
609
- if (line =~ /END_ENTITY/i) && (scope_stack.last&.dig(:type) == :entity)
610
- scope_stack.pop
611
- end
612
- if (line =~ /END_SCHEMA/i) && (scope_stack.last&.dig(:type) == :schema)
613
- scope_stack.pop
614
- end
615
- end
616
-
617
- # Find the innermost scope and get the corresponding model node
618
- return nil if scope_stack.empty?
619
-
620
- innermost = scope_stack.last
621
- find_scope_node(innermost[:type], innermost[:name])
622
- end
623
-
624
- def find_scope_node(type, name)
625
- return nil unless @model && name
626
-
627
- @model.schemas.each do |schema|
628
- # Check schema itself
629
- if type == :schema && schema.id == name
630
- return schema
631
- end
632
-
633
- # Check schema-level declarations
634
- case type
635
- when :function
636
- found = schema.functions&.find { |f| f.id == name }
637
- return found if found
638
- when :procedure
639
- found = schema.procedures&.find { |p| p.id == name }
640
- return found if found
641
- when :rule
642
- found = schema.rules&.find { |r| r.id == name }
643
- return found if found
644
- when :entity
645
- found = schema.entities&.find { |e| e.id == name }
646
- return found if found
647
- when :type
648
- found = schema.types&.find { |t| t.id == name }
649
- return found if found
650
- end
651
- end
652
-
653
- nil
654
- end
655
-
656
577
  def build_scope_path(node)
657
578
  return nil unless node
658
579
 
@@ -673,36 +594,6 @@ module Expressir
673
594
  parts.empty? ? nil : parts.join(".")
674
595
  end
675
596
 
676
- def find_containing_scope_for_ip(remark_line, nodes_with_positions)
677
- # First try scope map (O(1))
678
- scope = find_containing_scope_by_name(remark_line)
679
- if scope && supports_informal_propositions?(scope)
680
- return scope
681
- end
682
-
683
- # Fallback to position-based detection
684
- containing_nodes = nodes_with_positions.select do |n|
685
- n[:line] && n[:end_line] && remark_line >= n[:line] && remark_line <= n[:end_line] &&
686
- !repository?(n[:node]) && !cache?(n[:node])
687
- end
688
-
689
- # Find the innermost node that supports informal propositions
690
- if containing_nodes.any?
691
- containing_nodes.reverse_each do |n|
692
- node = n[:node]
693
- if supports_informal_propositions?(node)
694
- return node
695
- end
696
-
697
- # Fallback to schema
698
- return node if node.is_a?(Model::Declarations::Schema)
699
- end
700
- end
701
-
702
- # Fallback: search for containing entity/type/rule by source text
703
- find_scope_by_source_text(remark_line)
704
- end
705
-
706
597
  def find_scope_by_source_text(remark_line)
707
598
  # Search backwards from remark_line for containing scope
708
599
  lines = source_lines
@@ -908,15 +799,10 @@ module Expressir
908
799
  item
909
800
  end
910
801
 
911
- def attach_untagged_remarks(model, remarks)
802
+ def attach_untagged_remarks(remarks, nodes_with_positions)
912
803
  untagged = remarks.reject { |r| r[:tag] }
913
804
  return unless untagged.any?
914
805
 
915
- nodes_with_positions = []
916
- collect_nodes_with_positions(model, nodes_with_positions)
917
- # Use stable sort to preserve original order for equal keys
918
- nodes_with_positions.sort_by!.with_index { |n, i| [n[:position] || Float::INFINITY, i] }
919
-
920
806
  untagged.each do |remark|
921
807
  next if @attached_spans.include?(remark[:position])
922
808
 
@@ -1021,21 +907,41 @@ module Expressir
1021
907
  # The parser always provides this via Slice#offset
1022
908
  if node.source_offset
1023
909
  pos = node.source_offset
1024
- line = get_line_number(pos)
1025
- source_end_line = get_line_number(pos + node.source.length)
1026
-
1027
- # For container nodes, use the maximum end_line from children
1028
- # This is needed because source.length only covers the declaration, not the body
1029
- children_end_line = calculate_children_end_line(node)
1030
- end_line = [source_end_line,
1031
- children_end_line].compact.max || source_end_line
1032
-
1033
- result << {
1034
- node: node,
1035
- position: pos,
1036
- line: line,
1037
- end_line: end_line,
1038
- }
910
+ # Validate offset: native parser returns 0 for leaf nodes (WhereRule)
911
+ # where it can't determine the actual position. These have short
912
+ # expression-like source ("TRUE;") that doesn't appear at file start.
913
+ # Container nodes (Schema, Entity, Type) have declaration-like source
914
+ # that either starts at position 0 legitimately or is clearly valid.
915
+ valid = pos.positive?
916
+ if !valid && pos.zero? && node.source
917
+ src = node.source.to_s
918
+ # Accept position=0 if source is a declaration keyword line
919
+ valid = src.start_with?("SCHEMA", "ENTITY", "TYPE", "FUNCTION",
920
+ "PROCEDURE", "RULE", "CONSTANT", "VARIABLE",
921
+ "USE", "REFERENCE", "END_SCHEMA", "END_ENTITY",
922
+ "END_TYPE", "END_FUNCTION", "END_PROCEDURE",
923
+ "END_RULE", "END_CONSTANT", "END_VARIABLE")
924
+ end
925
+ if valid
926
+ line = get_line_number(pos)
927
+ source_end_line = get_line_number(pos + node.source.length)
928
+
929
+ # For container nodes, use the maximum end_line from children
930
+ # This is needed because source.length only covers the declaration, not the body
931
+ children_end_line = calculate_children_end_line(node)
932
+ end_line = [source_end_line,
933
+ children_end_line].compact.max || source_end_line
934
+
935
+ result << {
936
+ node: node,
937
+ position: pos,
938
+ line: line,
939
+ end_line: end_line,
940
+ }
941
+ else
942
+ # Invalid offset — treat as unknown position
943
+ result << { node: node, position: nil, line: nil, end_line: nil }
944
+ end
1039
945
  else
1040
946
  # No source_offset available - should not happen if parser provides Slice
1041
947
  result << { node: node, position: nil, line: nil, end_line: nil }
@@ -1063,10 +969,7 @@ module Expressir
1063
969
  end
1064
970
 
1065
971
  # Check specific child collections
1066
- %i[schemas types entities functions procedures rules constants
1067
- attributes derived_attributes inverse_attributes
1068
- where_rules unique_rules informal_propositions
1069
- parameters variables statements items remark_items].each do |attr|
972
+ CHILD_COLLECTION_ATTRIBUTES.each do |attr|
1070
973
  collection = safe_get_collection(node, attr)
1071
974
  collection&.each do |child|
1072
975
  if child.is_a?(Model::ModelElement) && child.source_offset && child.source
@@ -1085,10 +988,7 @@ module Expressir
1085
988
  collect_nodes_with_positions(child, result, visited)
1086
989
  end
1087
990
 
1088
- %i[schemas types entities functions procedures rules constants
1089
- attributes derived_attributes inverse_attributes
1090
- where_rules unique_rules informal_propositions
1091
- parameters variables statements items remark_items].each do |attr|
991
+ CHILD_COLLECTION_ATTRIBUTES.each do |attr|
1092
992
  collection = safe_get_collection(node, attr)
1093
993
  collection&.each do |item|
1094
994
  collect_nodes_with_positions(item, result, visited)
@@ -1096,6 +996,16 @@ module Expressir
1096
996
  end
1097
997
  end
1098
998
 
999
+ # Build sorted nodes_with_positions ONCE for both tagged and untagged remark passes.
1000
+ # This merges the two separate tree walks into one, cutting node visits in half.
1001
+ def build_sorted_nodes_with_positions(model)
1002
+ nodes_with_positions = []
1003
+ collect_nodes_with_positions(model, nodes_with_positions)
1004
+ # Stable sort: nil positions last, ties broken by insertion order
1005
+ nodes_with_positions.sort_by!.with_index { |n, i| [n[:position] || Float::INFINITY, i] }
1006
+ nodes_with_positions
1007
+ end
1008
+
1099
1009
  def find_nearest_node(remark, nodes)
1100
1010
  remark_line = remark[:line]
1101
1011
 
@@ -1,7 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "parsanol"
4
- require "set"
5
4
 
6
5
  module Expressir
7
6
  module Express
@@ -21,7 +21,6 @@ module Expressir
21
21
  attribute :selected, :boolean, default: false
22
22
  attribute :formatted, :string
23
23
  attribute :file_basename, :string
24
- attribute :full_source, :string
25
24
 
26
25
  key_value do
27
26
  map "_class", to: :_class, render_default: true
@@ -14,7 +14,8 @@ module Expressir
14
14
  include ScopeContainer
15
15
 
16
16
  attribute :path, :string
17
- attribute :schemas, Declarations::Schema, collection: true, initialize_empty: true
17
+ attribute :schemas, Declarations::Schema, collection: true,
18
+ initialize_empty: true
18
19
  attribute :_class, :string, default: -> { self.class.name }
19
20
 
20
21
  key_value do
@@ -208,7 +208,7 @@ module Expressir
208
208
  def remark_infos
209
209
  return [] if untagged_remarks.nil?
210
210
 
211
- untagged_remarks.select { |r| r.is_a?(RemarkInfo) }
211
+ untagged_remarks.grep(RemarkInfo)
212
212
  end
213
213
 
214
214
  private
@@ -32,8 +32,8 @@ module Expressir
32
32
  map "files", to: :files
33
33
  end
34
34
 
35
- def initialize(*args, base_dir: nil, schemas: nil, **kwargs)
36
- super(*args, **kwargs)
35
+ def initialize(*, base_dir: nil, schemas: nil, **)
36
+ super(*, **)
37
37
  @base_dir = base_dir
38
38
  @entity_index = nil
39
39
  @type_index = nil
@@ -41,9 +41,7 @@ module Expressir
41
41
  @_schemas = []
42
42
 
43
43
  # Support direct schemas initialization
44
- if schemas
45
- schemas.each { |schema| @_schemas << schema }
46
- end
44
+ schemas&.each { |schema| @_schemas << schema }
47
45
  end
48
46
 
49
47
  # Get all schemas (from both files and direct storage)
@@ -63,6 +61,7 @@ module Expressir
63
61
  # @param schema [Declarations::Schema] Schema to add
64
62
  def add_schema(schema)
65
63
  return unless schema
64
+
66
65
  @_schemas << schema
67
66
  end
68
67
 
@@ -342,16 +341,16 @@ module Expressir
342
341
  # @param format [Symbol] Output format
343
342
  # @yield [item] Block to convert item to hash
344
343
  # @return [Array, String] Formatted output
345
- def format_output(items, format, &block)
344
+ def format_output(items, format, &)
346
345
  case format
347
346
  when :hash
348
- items.map(&block)
347
+ items.map(&)
349
348
  when :json
350
349
  require "json"
351
- items.map(&block).to_json
350
+ items.map(&).to_json
352
351
  when :yaml
353
352
  require "yaml"
354
- items.map(&block).to_yaml
353
+ items.map(&).to_yaml
355
354
  else
356
355
  items
357
356
  end
@@ -84,10 +84,10 @@ module Expressir
84
84
  # @param max_depth [Integer] Maximum path depth (1=schema, 2=entity, 3=attribute)
85
85
  # @param options [Hash] Additional search options
86
86
  # @return [Array<Hash>] Filtered results
87
- def search_with_depth(pattern:, max_depth:, **options)
87
+ def search_with_depth(pattern:, max_depth:, **)
88
88
  return [] if max_depth <= 0
89
89
 
90
- results = search(pattern: pattern, **options)
90
+ results = search(pattern: pattern, **)
91
91
  filter_by_depth(results, max_depth)
92
92
  end
93
93
 
@@ -97,8 +97,8 @@ module Expressir
97
97
  # @param boost_prefix [Integer] Score boost for prefix matches
98
98
  # @param options [Hash] Additional search options
99
99
  # @return [Array<Hash>] Ranked results with :relevance_score
100
- def search_ranked(pattern:, boost_exact: 10, boost_prefix: 5, **options)
101
- results = search(pattern: pattern, **options)
100
+ def search_ranked(pattern:, boost_exact: 10, boost_prefix: 5, **)
101
+ results = search(pattern: pattern, **)
102
102
  rank_results(results, pattern, boost_exact, boost_prefix)
103
103
  end
104
104
 
@@ -108,8 +108,8 @@ module Expressir
108
108
  # @param ranked [Boolean] Enable relevance ranking
109
109
  # @param options [Hash] Additional filters and search options
110
110
  # @return [Array<Hash>] Filtered and optionally ranked results
111
- def search_advanced(pattern:, max_depth: nil, ranked: false, **options)
112
- results = search(pattern: pattern, **options)
111
+ def search_advanced(pattern:, max_depth: nil, ranked: false, **)
112
+ results = search(pattern: pattern, **)
113
113
 
114
114
  results = filter_by_depth(results, max_depth) if max_depth
115
115
  results = rank_results(results, pattern, 10, 5) if ranked
@@ -485,6 +485,7 @@ module Expressir
485
485
  current = element
486
486
  while current
487
487
  return current if current.is_a?(Declarations::Schema)
488
+
488
489
  current = current.parent
489
490
  end
490
491
  rescue StandardError
@@ -152,7 +152,9 @@ module Expressir
152
152
 
153
153
  # Get schemas that are not already in files
154
154
  file_schema_ids = normalized.files.flat_map(&:schemas).compact.map(&:id)
155
- direct_schemas = repository.schemas.reject { |s| file_schema_ids.include?(s.id) }
155
+ direct_schemas = repository.schemas.reject do |s|
156
+ file_schema_ids.include?(s.id)
157
+ end
156
158
 
157
159
  # Create an ExpFile for direct schemas
158
160
  if direct_schemas.any?
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Expressir
5
4
  module Package
6
5
  # Package metadata with serialization support
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Expressir
5
4
  class SchemaManifest < Lutaml::Model::Serializable
6
5
  attribute :schemas, "Expressir::SchemaManifestEntry",
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
-
4
3
  module Expressir
5
4
  class SchemaManifestEntry < Lutaml::Model::Serializable
6
5
  attribute :id, :string
@@ -1,3 +1,3 @@
1
1
  module Expressir
2
- VERSION = "2.3.0".freeze
2
+ VERSION = "2.3.2".freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: expressir
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.0
4
+ version: 2.3.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ribose Inc.
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-03-24 00:00:00.000000000 Z
11
+ date: 2026-04-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: base64
@@ -109,7 +109,7 @@ dependencies:
109
109
  - !ruby/object:Gem::Version
110
110
  version: '0'
111
111
  - !ruby/object:Gem::Dependency
112
- name: paint
112
+ name: nokogiri
113
113
  requirement: !ruby/object:Gem::Requirement
114
114
  requirements:
115
115
  - - ">="
@@ -123,39 +123,39 @@ dependencies:
123
123
  - !ruby/object:Gem::Version
124
124
  version: '0'
125
125
  - !ruby/object:Gem::Dependency
126
- name: parsanol
126
+ name: paint
127
127
  requirement: !ruby/object:Gem::Requirement
128
128
  requirements:
129
- - - "~>"
130
- - !ruby/object:Gem::Version
131
- version: 1.3.7
132
129
  - - ">="
133
130
  - !ruby/object:Gem::Version
134
- version: 1.3.7
131
+ version: '0'
135
132
  type: :runtime
136
133
  prerelease: false
137
134
  version_requirements: !ruby/object:Gem::Requirement
138
135
  requirements:
139
- - - "~>"
140
- - !ruby/object:Gem::Version
141
- version: 1.3.7
142
136
  - - ">="
143
137
  - !ruby/object:Gem::Version
144
- version: 1.3.7
138
+ version: '0'
145
139
  - !ruby/object:Gem::Dependency
146
- name: nokogiri
140
+ name: parsanol
147
141
  requirement: !ruby/object:Gem::Requirement
148
142
  requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: 1.3.9
149
146
  - - ">="
150
147
  - !ruby/object:Gem::Version
151
- version: '0'
148
+ version: 1.3.9
152
149
  type: :runtime
153
150
  prerelease: false
154
151
  version_requirements: !ruby/object:Gem::Requirement
155
152
  requirements:
153
+ - - "~>"
154
+ - !ruby/object:Gem::Version
155
+ version: 1.3.9
156
156
  - - ">="
157
157
  - !ruby/object:Gem::Version
158
- version: '0'
158
+ version: 1.3.9
159
159
  - !ruby/object:Gem::Dependency
160
160
  name: ruby-progressbar
161
161
  requirement: !ruby/object:Gem::Requirement