feedjira 2.2.0 → 3.1.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 (91) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/feed-parsing.md +15 -0
  3. data/.rubocop.yml +32 -8
  4. data/.rubocop_todo.yml +11 -0
  5. data/.travis.yml +3 -7
  6. data/CHANGELOG.md +18 -9
  7. data/CODE_OF_CONDUCT.md +74 -0
  8. data/Gemfile +8 -5
  9. data/README.md +46 -99
  10. data/Rakefile +8 -6
  11. data/feedjira.gemspec +31 -20
  12. data/lib/feedjira.rb +75 -41
  13. data/lib/feedjira/atom_entry_utilities.rb +51 -0
  14. data/lib/feedjira/configuration.rb +8 -10
  15. data/lib/feedjira/core_ext.rb +5 -3
  16. data/lib/feedjira/core_ext/date.rb +2 -1
  17. data/lib/feedjira/core_ext/string.rb +2 -1
  18. data/lib/feedjira/core_ext/time.rb +12 -12
  19. data/lib/feedjira/date_time_utilities.rb +8 -10
  20. data/lib/feedjira/date_time_utilities/date_time_epoch_parser.rb +3 -2
  21. data/lib/feedjira/date_time_utilities/date_time_language_parser.rb +4 -4
  22. data/lib/feedjira/date_time_utilities/date_time_pattern_parser.rb +11 -15
  23. data/lib/feedjira/feed.rb +12 -82
  24. data/lib/feedjira/feed_entry_utilities.rb +14 -7
  25. data/lib/feedjira/feed_utilities.rb +5 -4
  26. data/lib/feedjira/parser.rb +6 -1
  27. data/lib/feedjira/parser/atom.rb +6 -5
  28. data/lib/feedjira/parser/atom_entry.rb +4 -21
  29. data/lib/feedjira/parser/atom_feed_burner.rb +7 -6
  30. data/lib/feedjira/parser/atom_feed_burner_entry.rb +7 -18
  31. data/lib/feedjira/parser/atom_google_alerts.rb +26 -0
  32. data/lib/feedjira/parser/atom_google_alerts_entry.rb +21 -0
  33. data/lib/feedjira/parser/atom_youtube.rb +4 -3
  34. data/lib/feedjira/parser/atom_youtube_entry.rb +9 -8
  35. data/lib/feedjira/parser/globally_unique_identifier.rb +21 -0
  36. data/lib/feedjira/parser/google_docs_atom.rb +6 -6
  37. data/lib/feedjira/parser/google_docs_atom_entry.rb +3 -19
  38. data/lib/feedjira/parser/itunes_rss.rb +4 -3
  39. data/lib/feedjira/parser/itunes_rss_category.rb +6 -5
  40. data/lib/feedjira/parser/itunes_rss_item.rb +5 -8
  41. data/lib/feedjira/parser/itunes_rss_owner.rb +2 -1
  42. data/lib/feedjira/parser/json_feed.rb +41 -0
  43. data/lib/feedjira/parser/json_feed_item.rb +57 -0
  44. data/lib/feedjira/parser/podlove_chapter.rb +4 -3
  45. data/lib/feedjira/parser/rss.rb +5 -3
  46. data/lib/feedjira/parser/rss_entry.rb +3 -24
  47. data/lib/feedjira/parser/rss_feed_burner.rb +4 -3
  48. data/lib/feedjira/parser/rss_feed_burner_entry.rb +6 -26
  49. data/lib/feedjira/parser/rss_image.rb +2 -0
  50. data/lib/feedjira/preprocessor.rb +4 -4
  51. data/lib/feedjira/rss_entry_utilities.rb +53 -0
  52. data/lib/feedjira/version.rb +3 -1
  53. data/spec/feedjira/configuration_spec.rb +11 -16
  54. data/spec/feedjira/date_time_utilities_spec.rb +22 -20
  55. data/spec/feedjira/feed_entry_utilities_spec.rb +20 -18
  56. data/spec/feedjira/feed_spec.rb +17 -229
  57. data/spec/feedjira/feed_utilities_spec.rb +75 -73
  58. data/spec/feedjira/parser/atom_entry_spec.rb +41 -38
  59. data/spec/feedjira/parser/atom_feed_burner_entry_spec.rb +22 -20
  60. data/spec/feedjira/parser/atom_feed_burner_spec.rb +122 -118
  61. data/spec/feedjira/parser/atom_google_alerts_entry_spec.rb +34 -0
  62. data/spec/feedjira/parser/atom_google_alerts_spec.rb +62 -0
  63. data/spec/feedjira/parser/atom_spec.rb +83 -77
  64. data/spec/feedjira/parser/atom_youtube_entry_spec.rb +41 -39
  65. data/spec/feedjira/parser/atom_youtube_spec.rb +21 -19
  66. data/spec/feedjira/parser/google_docs_atom_entry_spec.rb +10 -8
  67. data/spec/feedjira/parser/google_docs_atom_spec.rb +25 -21
  68. data/spec/feedjira/parser/itunes_rss_item_spec.rb +39 -37
  69. data/spec/feedjira/parser/itunes_rss_owner_spec.rb +7 -5
  70. data/spec/feedjira/parser/itunes_rss_spec.rb +120 -116
  71. data/spec/feedjira/parser/json_feed_item_spec.rb +81 -0
  72. data/spec/feedjira/parser/json_feed_spec.rb +55 -0
  73. data/spec/feedjira/parser/podlove_chapter_spec.rb +14 -12
  74. data/spec/feedjira/parser/rss_entry_spec.rb +56 -34
  75. data/spec/feedjira/parser/rss_feed_burner_entry_spec.rb +36 -34
  76. data/spec/feedjira/parser/rss_feed_burner_spec.rb +49 -45
  77. data/spec/feedjira/parser/rss_spec.rb +38 -36
  78. data/spec/feedjira/preprocessor_spec.rb +9 -7
  79. data/spec/feedjira_spec.rb +166 -0
  80. data/spec/sample_feeds.rb +32 -29
  81. data/spec/sample_feeds/HuffPostCanada.xml +279 -0
  82. data/spec/sample_feeds/Permalinks.xml +22 -0
  83. data/spec/sample_feeds/a10.xml +72 -0
  84. data/spec/sample_feeds/google_alerts_atom.xml +1 -0
  85. data/spec/sample_feeds/json_feed.json +156 -0
  86. data/spec/spec_helper.rb +7 -5
  87. metadata +59 -70
  88. data/Dangerfile +0 -1
  89. data/fixtures/vcr_cassettes/fetch_failure.yml +0 -62
  90. data/fixtures/vcr_cassettes/parse_error.yml +0 -222
  91. data/fixtures/vcr_cassettes/success.yml +0 -281
data/Rakefile CHANGED
@@ -1,17 +1,19 @@
1
- require 'rspec/core/rake_task'
2
- require 'rubocop/rake_task'
3
- require 'yard'
1
+ # frozen_string_literal: true
2
+
3
+ require "rspec/core/rake_task"
4
+ require "rubocop/rake_task"
5
+ require "yard"
4
6
 
5
7
  RSpec::Core::RakeTask.new(:spec) do |t|
6
8
  t.verbose = false
7
9
  end
8
10
 
9
11
  RuboCop::RakeTask.new(:rubocop) do |t|
10
- t.options = ['--display-cop-names']
12
+ t.options = ["--display-cop-names"]
11
13
  end
12
14
 
13
15
  YARD::Rake::YardocTask.new do |t|
14
- t.files = ['lib/**/*.rb', '-', 'LICENSE']
16
+ t.files = ["lib/**/*.rb", "-", "LICENSE"]
15
17
  end
16
18
 
17
- task default: [:spec, :rubocop]
19
+ task default: %i[spec rubocop]
@@ -1,31 +1,42 @@
1
- # -*- encoding: utf-8 -*-
2
- require File.expand_path('../lib/feedjira/version', __FILE__)
1
+ # frozen_string_literal: true
2
+
3
+ require File.expand_path("lib/feedjira/version", __dir__)
3
4
 
4
5
  Gem::Specification.new do |s|
5
- s.authors = ['Paul Dix', 'Julien Kirch', 'Ezekiel Templin', 'Jon Allured']
6
- s.email = 'feedjira@gmail.com'
7
- s.homepage = 'http://feedjira.com'
8
- s.license = 'MIT'
9
- s.name = 'feedjira'
6
+ s.authors = [
7
+ "Adam Hess",
8
+ "Akinori Musha",
9
+ "Ezekiel Templin",
10
+ "Jon Allured",
11
+ "Julien Kirch",
12
+ "Michael Stock",
13
+ "Paul Dix"
14
+ ]
15
+ s.homepage = "https://github.com/feedjira/feedjira"
16
+ s.license = "MIT"
17
+ s.name = "feedjira"
10
18
  s.platform = Gem::Platform::RUBY
11
- s.summary = 'A feed fetching and parsing library'
19
+ s.summary = "A feed parsing library"
12
20
  s.version = Feedjira::VERSION
13
21
 
22
+ s.metadata = {
23
+ "homepage_uri" => "http://feedjira.com",
24
+ "source_code_uri" => "https://github.com/feedjira/feedjira",
25
+ "changelog_uri" => "https://github.com/feedjira/feedjira/blob/master/CHANGELOG.md"
26
+ }
27
+
14
28
  s.files = `git ls-files`.split("\n")
15
- s.require_paths = ['lib']
29
+ s.require_paths = ["lib"]
16
30
  s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
31
 
18
- s.required_ruby_version = '>=1.9.3'
32
+ s.required_ruby_version = ">=2.2"
19
33
 
20
- s.add_dependency 'faraday', '>= 0.9'
21
- s.add_dependency 'faraday_middleware', '>= 0.9'
22
- s.add_dependency 'loofah', '>= 2.0'
23
- s.add_dependency 'sax-machine', '>= 1.0'
34
+ s.add_dependency "loofah", ">= 2.3.1"
35
+ s.add_dependency "sax-machine", ">= 1.0"
24
36
 
25
- s.add_development_dependency 'danger'
26
- s.add_development_dependency 'danger-commit_lint'
27
- s.add_development_dependency 'rspec'
28
- s.add_development_dependency 'rubocop', '0.46'
29
- s.add_development_dependency 'vcr'
30
- s.add_development_dependency 'yard', '~> 0.9'
37
+ s.add_development_dependency "faraday"
38
+ s.add_development_dependency "rspec"
39
+ s.add_development_dependency "rubocop"
40
+ s.add_development_dependency "vcr"
41
+ s.add_development_dependency "yard"
31
42
  end
@@ -1,47 +1,81 @@
1
- require 'zlib'
2
- require 'faraday'
3
- require 'faraday_middleware'
4
- require 'sax-machine'
5
- require 'loofah'
6
- require 'logger'
7
-
8
- require 'feedjira/core_ext'
9
- require 'feedjira/configuration'
10
- require 'feedjira/date_time_utilities/date_time_epoch_parser'
11
- require 'feedjira/date_time_utilities/date_time_language_parser'
12
- require 'feedjira/date_time_utilities/date_time_pattern_parser'
13
- require 'feedjira/date_time_utilities'
14
- require 'feedjira/date_time_utilities'
15
- require 'feedjira/feed_entry_utilities'
16
- require 'feedjira/feed_utilities'
17
- require 'feedjira/feed'
18
- require 'feedjira/parser'
19
- require 'feedjira/parser/rss_entry'
20
- require 'feedjira/parser/rss_image'
21
- require 'feedjira/parser/rss'
22
- require 'feedjira/parser/atom_entry'
23
- require 'feedjira/parser/atom'
24
- require 'feedjira/preprocessor'
25
- require 'feedjira/version'
26
-
27
- require 'feedjira/parser/rss_feed_burner_entry'
28
- require 'feedjira/parser/rss_feed_burner'
29
- require 'feedjira/parser/podlove_chapter'
30
- require 'feedjira/parser/itunes_rss_owner'
31
- require 'feedjira/parser/itunes_rss_category'
32
- require 'feedjira/parser/itunes_rss_item'
33
- require 'feedjira/parser/itunes_rss'
34
- require 'feedjira/parser/atom_feed_burner_entry'
35
- require 'feedjira/parser/atom_feed_burner'
36
- require 'feedjira/parser/google_docs_atom_entry'
37
- require 'feedjira/parser/google_docs_atom'
38
- require 'feedjira/parser/atom_youtube_entry'
39
- require 'feedjira/parser/atom_youtube'
1
+ # frozen_string_literal: true
2
+
3
+ require "zlib"
4
+ require "sax-machine"
5
+ require "loofah"
6
+ require "logger"
7
+ require "json"
8
+
9
+ require "feedjira/core_ext"
10
+ require "feedjira/configuration"
11
+ require "feedjira/date_time_utilities/date_time_epoch_parser"
12
+ require "feedjira/date_time_utilities/date_time_language_parser"
13
+ require "feedjira/date_time_utilities/date_time_pattern_parser"
14
+ require "feedjira/date_time_utilities"
15
+ require "feedjira/feed_entry_utilities"
16
+ require "feedjira/feed_utilities"
17
+ require "feedjira/feed"
18
+ require "feedjira/rss_entry_utilities"
19
+ require "feedjira/atom_entry_utilities"
20
+ require "feedjira/parser"
21
+ require "feedjira/parser/globally_unique_identifier"
22
+ require "feedjira/parser/rss_entry"
23
+ require "feedjira/parser/rss_image"
24
+ require "feedjira/parser/rss"
25
+ require "feedjira/parser/atom_entry"
26
+ require "feedjira/parser/atom"
27
+ require "feedjira/preprocessor"
28
+ require "feedjira/version"
29
+
30
+ require "feedjira/parser/rss_feed_burner_entry"
31
+ require "feedjira/parser/rss_feed_burner"
32
+ require "feedjira/parser/podlove_chapter"
33
+ require "feedjira/parser/itunes_rss_owner"
34
+ require "feedjira/parser/itunes_rss_category"
35
+ require "feedjira/parser/itunes_rss_item"
36
+ require "feedjira/parser/itunes_rss"
37
+ require "feedjira/parser/atom_feed_burner_entry"
38
+ require "feedjira/parser/atom_feed_burner"
39
+ require "feedjira/parser/atom_google_alerts_entry"
40
+ require "feedjira/parser/atom_google_alerts"
41
+ require "feedjira/parser/google_docs_atom_entry"
42
+ require "feedjira/parser/google_docs_atom"
43
+ require "feedjira/parser/atom_youtube_entry"
44
+ require "feedjira/parser/atom_youtube"
45
+ require "feedjira/parser/json_feed"
46
+ require "feedjira/parser/json_feed_item"
40
47
 
41
48
  # Feedjira
42
49
  module Feedjira
43
- class NoParserAvailable < StandardError; end
44
- class FetchFailure < StandardError; end
50
+ NoParserAvailable = Class.new(StandardError)
45
51
 
46
52
  extend Configuration
53
+
54
+ # Parse XML with first compatible parser
55
+ #
56
+ # @example
57
+ # xml = HTTParty.get("http://example.com").body
58
+ # Feedjira.parse(xml)
59
+ def parse(xml, parser: nil, &block)
60
+ parser ||= parser_for_xml(xml)
61
+
62
+ if parser.nil?
63
+ raise NoParserAvailable, "No valid parser for XML."
64
+ end
65
+
66
+ parser.parse(xml, &block)
67
+ end
68
+ module_function :parse # rubocop:disable Style/AccessModifierDeclarations
69
+
70
+ # Find compatible parser for given XML
71
+ #
72
+ # @example
73
+ # xml = HTTParty.get("http://example.com").body
74
+ # parser = Feedjira.parser_for_xml(xml)
75
+ # parser.parse(xml)
76
+ def parser_for_xml(xml)
77
+ start_of_doc = xml.slice(0, 2000)
78
+ Feedjira.parsers.detect { |klass| klass.able_to_parse?(start_of_doc) }
79
+ end
80
+ module_function :parser_for_xml # rubocop:disable Style/AccessModifierDeclarations
47
81
  end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Feedjira
4
+ module AtomEntryUtilities
5
+ def self.included(mod)
6
+ mod.class_exec do
7
+ element :title, as: :raw_title, with: { type: "html" }
8
+ element :title, as: :raw_title, with: { type: "xhtml" }
9
+ element :title, as: :raw_title, with: { type: "xml" }
10
+ element :title, as: :title, with: { type: "text" }
11
+ element :title, as: :title, with: { type: nil }
12
+ element :title, as: :title_type, value: :type
13
+
14
+ element :name, as: :author
15
+ element :content
16
+ element :summary
17
+ element :enclosure, as: :image, value: :href
18
+
19
+ element :published
20
+ element :id, as: :entry_id
21
+ element :created, as: :published
22
+ element :issued, as: :published
23
+ element :updated
24
+ element :modified, as: :updated
25
+
26
+ elements :category, as: :categories, value: :term
27
+
28
+ element :link, as: :url, value: :href, with: {
29
+ type: "text/html",
30
+ rel: "alternate"
31
+ }
32
+
33
+ elements :link, as: :links, value: :href
34
+ end
35
+ end
36
+
37
+ def title
38
+ @title ||=
39
+ case @raw_title
40
+ when String
41
+ Loofah.fragment(@raw_title).xpath("normalize-space(.)")
42
+ else
43
+ @title
44
+ end
45
+ end
46
+
47
+ def url
48
+ @url ||= links.first
49
+ end
50
+ end
51
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Feedjira::Configuration
2
4
  module Feedjira
3
5
  # Provides global configuration options for Feedjira
@@ -8,12 +10,9 @@ module Feedjira
8
10
  # end
9
11
  module Configuration
10
12
  attr_accessor(
11
- :follow_redirect_limit,
12
13
  :logger,
13
14
  :parsers,
14
- :request_timeout,
15
- :strip_whitespace,
16
- :user_agent
15
+ :strip_whitespace
17
16
  )
18
17
 
19
18
  # Modify Feedjira's current configuration
@@ -42,20 +41,17 @@ module Feedjira
42
41
 
43
42
  # @private
44
43
  def set_default_configuration
45
- self.follow_redirect_limit = 3
46
44
  self.logger = default_logger
47
45
  self.parsers = default_parsers
48
- self.request_timeout = 30
49
46
  self.strip_whitespace = false
50
- self.user_agent = "Feedjira #{Feedjira::VERSION}"
51
47
  end
52
48
 
53
49
  private
54
50
 
55
51
  # @private
56
52
  def default_logger
57
- Logger.new(STDOUT).tap do |logger|
58
- logger.progname = 'Feedjira'
53
+ Logger.new($stdout).tap do |logger|
54
+ logger.progname = "Feedjira"
59
55
  logger.level = Logger::WARN
60
56
  end
61
57
  end
@@ -67,9 +63,11 @@ module Feedjira
67
63
  Feedjira::Parser::GoogleDocsAtom,
68
64
  Feedjira::Parser::AtomYoutube,
69
65
  Feedjira::Parser::AtomFeedBurner,
66
+ Feedjira::Parser::AtomGoogleAlerts,
70
67
  Feedjira::Parser::Atom,
71
68
  Feedjira::Parser::ITunesRSS,
72
- Feedjira::Parser::RSS
69
+ Feedjira::Parser::RSS,
70
+ Feedjira::Parser::JSONFeed
73
71
  ]
74
72
  end
75
73
  end
@@ -1,3 +1,5 @@
1
- require 'feedjira/core_ext/time'
2
- require 'feedjira/core_ext/date'
3
- require 'feedjira/core_ext/string'
1
+ # frozen_string_literal: true
2
+
3
+ require "feedjira/core_ext/time"
4
+ require "feedjira/core_ext/date"
5
+ require "feedjira/core_ext/string"
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Date code pulled and adapted from:
2
4
  # Ruby Cookbook by Lucas Carlson and Leonard Richardson
3
5
  # Published by O'Reilly
4
6
  # ISBN: 0-596-52369-6
5
- # rubocop:disable Style/DocumentationMethod
6
7
  class Date
7
8
  def feed_utils_to_gm_time
8
9
  feed_utils_to_time(new_offset, :gm)
@@ -1,4 +1,5 @@
1
- # rubocop:disable Style/DocumentationMethod Style/Documentation
1
+ # frozen_string_literal: true
2
+
2
3
  class String
3
4
  def sanitize!
4
5
  replace(sanitize)
@@ -1,7 +1,8 @@
1
- require 'time'
2
- require 'date'
1
+ # frozen_string_literal: true
2
+
3
+ require "time"
4
+ require "date"
3
5
 
4
- # rubocop:disable Style/DocumentationMethod
5
6
  class Time
6
7
  # Parse a time string and convert it to UTC without raising errors.
7
8
  # Parses a flattened 14-digit time (YYYYmmddHHMMMSS) as UTC.
@@ -11,17 +12,16 @@ class Time
11
12
  #
12
13
  # === Returns
13
14
  # A Time instance in UTC or nil if there were errors while parsing.
14
- # rubocop:disable Metrics/MethodLength
15
- def self.parse_safely(dt)
16
- if dt.is_a?(Time)
17
- dt.utc
18
- elsif dt.respond_to?(:to_datetime)
19
- dt.to_datetime.utc
20
- elsif dt.respond_to? :to_s
21
- parse_string_safely dt.to_s
15
+ def self.parse_safely(datetime)
16
+ if datetime.is_a?(Time)
17
+ datetime.utc
18
+ elsif datetime.respond_to?(:to_datetime)
19
+ datetime.to_datetime.utc
20
+ elsif datetime.respond_to? :to_s
21
+ parse_string_safely datetime.to_s
22
22
  end
23
23
  rescue StandardError => e
24
- Feedjira.logger.debug { "Failed to parse time #{dt}" }
24
+ Feedjira.logger.debug { "Failed to parse time #{datetime}" }
25
25
  Feedjira.logger.debug(e)
26
26
  nil
27
27
  end
@@ -1,4 +1,5 @@
1
- # rubocop:disable Style/Documentation
1
+ # frozen_string_literal: true
2
+
2
3
  module Feedjira
3
4
  module DateTimeUtilities
4
5
  # This is our date parsing heuristic.
@@ -12,16 +13,13 @@ module Feedjira
12
13
 
13
14
  # Parse the given string starting with the most common parser (default ruby)
14
15
  # and going over all other available parsers
15
- # rubocop:disable Metrics/MethodLength
16
16
  def parse_datetime(string)
17
- res = DATE_PARSERS.find do |parser|
18
- begin
19
- return parser.parse(string).feed_utils_to_gm_time
20
- rescue StandardError => e
21
- Feedjira.logger.debug { "Failed to parse date #{string}" }
22
- Feedjira.logger.debug(e)
23
- nil
24
- end
17
+ res = DATE_PARSERS.detect do |parser|
18
+ return parser.parse(string).feed_utils_to_gm_time
19
+ rescue StandardError => e
20
+ Feedjira.logger.debug { "Failed to parse date #{string}" }
21
+ Feedjira.logger.debug(e)
22
+ nil
25
23
  end
26
24
 
27
25
  Feedjira.logger.warn { "Failed to parse date #{string}" } if res.nil?
@@ -1,11 +1,12 @@
1
- # rubocop:disable Style/Documentation
2
- # rubocop:disable Style/DocumentationMethod
1
+ # frozen_string_literal: true
2
+
3
3
  module Feedjira
4
4
  module DateTimeUtilities
5
5
  class DateTimeEpochParser
6
6
  def self.parse(string)
7
7
  epoch_time = string.to_i
8
8
  return Time.at(epoch_time).to_datetime if epoch_time.to_s == string
9
+
9
10
  raise "#{string} is not a valid epoch time"
10
11
  end
11
12
  end
@@ -1,12 +1,12 @@
1
- # rubocop:disable Style/Documentation
2
- # rubocop:disable Style/DocumentationMethod
1
+ # frozen_string_literal: true
2
+
3
3
  module Feedjira
4
4
  module DateTimeUtilities
5
5
  class DateTimeLanguageParser
6
6
  MONTHS_ENGLISH =
7
- %w(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec).freeze
7
+ %w[Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec].freeze
8
8
  MONTHS_SPANISH =
9
- %w(Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic).freeze
9
+ %w[Ene Feb Mar Abr May Jun Jul Ago Sep Oct Nov Dic].freeze
10
10
 
11
11
  def self.parse(string)
12
12
  DateTime.parse(translate(string))