horn_of_plenty 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +0 -0
  4. data/LICENSE.txt +19 -0
  5. data/README.md +36 -0
  6. data/lib/horn_of_plenty.rb +22 -0
  7. data/lib/horn_of_plenty/adapters/github/client.rb +82 -0
  8. data/lib/horn_of_plenty/adapters/github/collections/card.rb +46 -0
  9. data/lib/horn_of_plenty/adapters/github/configuration.rb +38 -0
  10. data/lib/horn_of_plenty/adapters/github/error.rb +44 -0
  11. data/lib/horn_of_plenty/adapters/github/parsers/board.rb +52 -0
  12. data/lib/horn_of_plenty/adapters/github/parsers/card.rb +91 -0
  13. data/lib/horn_of_plenty/adapters/github/parsers/issue.rb +95 -0
  14. data/lib/horn_of_plenty/adapters/github/parsers/lane.rb +50 -0
  15. data/lib/horn_of_plenty/adapters/github/parsers/pull_request.rb +38 -0
  16. data/lib/horn_of_plenty/adapters/github/queries/list_boards.rb +18 -0
  17. data/lib/horn_of_plenty/adapters/github/queries/list_cards.rb +19 -0
  18. data/lib/horn_of_plenty/adapters/github/queries/list_issues.rb +22 -0
  19. data/lib/horn_of_plenty/adapters/github/queries/list_lanes.rb +19 -0
  20. data/lib/horn_of_plenty/adapters/github/queries/list_pull_requests.rb +22 -0
  21. data/lib/horn_of_plenty/adapters/github/repositories/board.rb +24 -0
  22. data/lib/horn_of_plenty/adapters/github/repositories/card.rb +24 -0
  23. data/lib/horn_of_plenty/adapters/github/repositories/issue.rb +24 -0
  24. data/lib/horn_of_plenty/adapters/github/repositories/lane.rb +24 -0
  25. data/lib/horn_of_plenty/adapters/github/repositories/pull_request.rb +24 -0
  26. data/lib/horn_of_plenty/adapters/github/requests/list_boards.rb +50 -0
  27. data/lib/horn_of_plenty/adapters/github/requests/list_cards.rb +52 -0
  28. data/lib/horn_of_plenty/adapters/github/requests/list_issues.rb +50 -0
  29. data/lib/horn_of_plenty/adapters/github/requests/list_lanes.rb +52 -0
  30. data/lib/horn_of_plenty/adapters/github/requests/list_pull_requests.rb +50 -0
  31. data/lib/horn_of_plenty/adapters/github/response.rb +16 -0
  32. data/lib/horn_of_plenty/adapters/github/responses/list_boards.rb +17 -0
  33. data/lib/horn_of_plenty/adapters/github/responses/list_cards.rb +23 -0
  34. data/lib/horn_of_plenty/adapters/github/responses/list_issues.rb +18 -0
  35. data/lib/horn_of_plenty/adapters/github/responses/list_lanes.rb +17 -0
  36. data/lib/horn_of_plenty/adapters/github/responses/list_pull_requests.rb +17 -0
  37. data/lib/horn_of_plenty/collection.rb +60 -0
  38. data/lib/horn_of_plenty/core_ext/string.rb +95 -0
  39. data/lib/horn_of_plenty/models/board.rb +32 -0
  40. data/lib/horn_of_plenty/models/card.rb +51 -0
  41. data/lib/horn_of_plenty/models/issue.rb +46 -0
  42. data/lib/horn_of_plenty/models/lane.rb +30 -0
  43. data/lib/horn_of_plenty/models/parsable.rb +44 -0
  44. data/lib/horn_of_plenty/models/pull_request.rb +12 -0
  45. data/lib/horn_of_plenty/null_objects/board.rb +10 -0
  46. data/lib/horn_of_plenty/null_objects/card.rb +10 -0
  47. data/lib/horn_of_plenty/null_objects/issue.rb +10 -0
  48. data/lib/horn_of_plenty/null_objects/lane.rb +10 -0
  49. data/lib/horn_of_plenty/null_objects/pull_request.rb +10 -0
  50. data/lib/horn_of_plenty/paginator.rb +109 -0
  51. data/lib/horn_of_plenty/parsers/hash.rb +95 -0
  52. data/lib/horn_of_plenty/queries/fetch.rb +36 -0
  53. data/lib/horn_of_plenty/repositories/board.rb +26 -0
  54. data/lib/horn_of_plenty/repositories/card.rb +26 -0
  55. data/lib/horn_of_plenty/repositories/issue.rb +26 -0
  56. data/lib/horn_of_plenty/repositories/lane.rb +26 -0
  57. data/lib/horn_of_plenty/repositories/pull_request.rb +26 -0
  58. data/lib/horn_of_plenty/repository.rb +29 -0
  59. data/lib/horn_of_plenty/request.rb +32 -0
  60. data/lib/horn_of_plenty/response.rb +44 -0
  61. data/lib/horn_of_plenty/version.rb +4 -0
  62. data/spec/fixtures/responses/board_github.json +29 -0
  63. data/spec/fixtures/responses/card_github.json +7 -0
  64. data/spec/fixtures/responses/issue_github.json +163 -0
  65. data/spec/fixtures/responses/lane_github.json +7 -0
  66. data/spec/fixtures/responses/pull_request_github.json +361 -0
  67. data/spec/lib/horn_of_plenty/adapters/github/client_spec.rb +12 -0
  68. data/spec/lib/horn_of_plenty/adapters/github/collections/card_spec.rb +14 -0
  69. data/spec/lib/horn_of_plenty/adapters/github/configuration_spec.rb +12 -0
  70. data/spec/lib/horn_of_plenty/adapters/github/error_spec.rb +35 -0
  71. data/spec/lib/horn_of_plenty/adapters/github/parsers/board_spec.rb +39 -0
  72. data/spec/lib/horn_of_plenty/adapters/github/parsers/card_spec.rb +64 -0
  73. data/spec/lib/horn_of_plenty/adapters/github/parsers/issue_spec.rb +47 -0
  74. data/spec/lib/horn_of_plenty/adapters/github/parsers/lane_spec.rb +38 -0
  75. data/spec/lib/horn_of_plenty/adapters/github/parsers/pull_request_spec.rb +50 -0
  76. data/spec/lib/horn_of_plenty/adapters/github/queries/list_boards_spec.rb +19 -0
  77. data/spec/lib/horn_of_plenty/adapters/github/queries/list_cards_spec.rb +19 -0
  78. data/spec/lib/horn_of_plenty/adapters/github/queries/list_issues_spec.rb +19 -0
  79. data/spec/lib/horn_of_plenty/adapters/github/queries/list_lanes_spec.rb +19 -0
  80. data/spec/lib/horn_of_plenty/adapters/github/queries/list_pull_requests_spec.rb +19 -0
  81. data/spec/lib/horn_of_plenty/adapters/github/repositories/board_spec.rb +22 -0
  82. data/spec/lib/horn_of_plenty/adapters/github/repositories/card_spec.rb +67 -0
  83. data/spec/lib/horn_of_plenty/adapters/github/repositories/issue_spec.rb +22 -0
  84. data/spec/lib/horn_of_plenty/adapters/github/repositories/lane_spec.rb +25 -0
  85. data/spec/lib/horn_of_plenty/adapters/github/repositories/pull_request_spec.rb +24 -0
  86. data/spec/lib/horn_of_plenty/adapters/github/requests/list_boards_spec.rb +42 -0
  87. data/spec/lib/horn_of_plenty/adapters/github/requests/list_cards_spec.rb +51 -0
  88. data/spec/lib/horn_of_plenty/adapters/github/requests/list_issues_spec.rb +42 -0
  89. data/spec/lib/horn_of_plenty/adapters/github/requests/list_lanes_spec.rb +51 -0
  90. data/spec/lib/horn_of_plenty/adapters/github/requests/list_pull_requests_spec.rb +42 -0
  91. data/spec/lib/horn_of_plenty/adapters/github/responses/list_boards_spec.rb +78 -0
  92. data/spec/lib/horn_of_plenty/adapters/github/responses/list_cards_spec.rb +86 -0
  93. data/spec/lib/horn_of_plenty/adapters/github/responses/list_issues_spec.rb +104 -0
  94. data/spec/lib/horn_of_plenty/adapters/github/responses/list_lanes_spec.rb +86 -0
  95. data/spec/lib/horn_of_plenty/adapters/github/responses/list_pull_requests_spec.rb +109 -0
  96. data/spec/lib/horn_of_plenty/models/board_spec.rb +10 -0
  97. data/spec/lib/horn_of_plenty/models/card_spec.rb +10 -0
  98. data/spec/lib/horn_of_plenty/models/issue_spec.rb +10 -0
  99. data/spec/lib/horn_of_plenty/models/lane_spec.rb +10 -0
  100. data/spec/lib/horn_of_plenty/models/pull_request_spec.rb +10 -0
  101. data/spec/lib/horn_of_plenty/null_objects/board_spec.rb +10 -0
  102. data/spec/lib/horn_of_plenty/null_objects/card_spec.rb +10 -0
  103. data/spec/lib/horn_of_plenty/null_objects/issue_spec.rb +10 -0
  104. data/spec/lib/horn_of_plenty/null_objects/lane_spec.rb +10 -0
  105. data/spec/lib/horn_of_plenty/null_objects/pull_request_spec.rb +10 -0
  106. data/spec/lib/horn_of_plenty/queries/fetch_spec.rb +15 -0
  107. data/spec/lib/horn_of_plenty/repositories/board_spec.rb +48 -0
  108. data/spec/lib/horn_of_plenty/repositories/card_spec.rb +51 -0
  109. data/spec/lib/horn_of_plenty/repositories/issue_spec.rb +48 -0
  110. data/spec/lib/horn_of_plenty/repositories/lane_spec.rb +51 -0
  111. data/spec/lib/horn_of_plenty/repositories/pull_request_spec.rb +50 -0
  112. data/spec/support/patterns.rb +1 -0
  113. metadata +396 -0
  114. metadata.gz.sig +0 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9cdc7e2d2e74f65483ee29e79ddb51aae050b456
4
+ data.tar.gz: ec90581bc9e24528c446223a84f9e38a0b20e308
5
+ SHA512:
6
+ metadata.gz: 8920b5e59342b853fe777b2d6180f339109d5c9e908f4ac41234ff65dab2bb67db79d5761faaf5118c48e95e66bd7abce544810570202505ec57749f79b856b5
7
+ data.tar.gz: 93fefa8c018f9eeb63b5a8a1bcbce9da66dbb1c277e87fd843adc9ca18c808b32a11928a72e71d764165e972cf63d0ff8858645f911d482729c4f0cb17f08c31
Binary file
Binary file
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2016 The Kompanee, Ltd
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
@@ -0,0 +1,36 @@
1
+ # Horn of Plenty
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/carleton`. 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 'horn_of_plenty'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install horn_of_plenty
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]/horn_of_plenty.
36
+
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+ require 'horn_of_plenty/version'
3
+
4
+ require 'horn_of_plenty/core_ext/string'
5
+ require 'horn_of_plenty/adapters/github/configuration'
6
+ require 'horn_of_plenty/repositories/board'
7
+ require 'horn_of_plenty/repositories/lane'
8
+ require 'horn_of_plenty/repositories/card'
9
+ require 'horn_of_plenty/repositories/issue'
10
+
11
+ module HornOfPlenty
12
+ def self.adapter_class(name)
13
+ adapter_classified = HornOfPlenty::CoreExt::String.camelize(name)
14
+ adapter_class_name = "HornOfPlenty::Adapters::#{adapter_classified}"
15
+
16
+ HornOfPlenty::CoreExt::String.constantize(adapter_class_name)
17
+ end
18
+
19
+ def self.adapter_config(name)
20
+ adapter_class(name).config
21
+ end
22
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ require 'securerandom'
3
+ require 'connection_pool'
4
+ require 'faraday'
5
+ require 'faraday_middleware'
6
+ require 'wisper'
7
+ require 'horn_of_plenty/adapters/github/configuration'
8
+ require 'horn_of_plenty/adapters/github/error'
9
+
10
+ module HornOfPlenty
11
+ module Adapters
12
+ module Github
13
+ class Client
14
+ include Wisper::Publisher
15
+
16
+ RETRY_BACKOFF_MULTIPLIER = 3.5
17
+
18
+ attr_accessor :url,
19
+ :version,
20
+ :token,
21
+ :logger
22
+
23
+ def initialize(url: configuration.base_url,
24
+ version: configuration.api_version,
25
+ token: configuration.token,
26
+ logger: configuration.logger)
27
+
28
+ self.url = url
29
+ self.version = version
30
+ self.token = token
31
+ self.logger = logger
32
+ end
33
+
34
+ # rubocop:disable Metrics/LineLength
35
+ def request(request, retries = 3, _retry_delay_in_ms = 1000)
36
+ broadcast(:horn_of_plenty_request_sent, request.class.name, request.method, request.path, request.to_h, url, retries)
37
+
38
+ response = connection.public_send(request.method, request.path, request.to_h)
39
+
40
+ broadcast(:horn_of_plenty_response_received, response.class.name, request.method, request.path, response.body, url)
41
+
42
+ request.response_class.parse(response)
43
+ rescue Github::Errors => exception
44
+ retries -= 1
45
+
46
+ broadcast(:horn_of_plenty_request_retried, exception, retries)
47
+
48
+ retry if retries.positive?
49
+
50
+ raise exception
51
+ end
52
+ # rubocop:enable Metrics/LineLength
53
+
54
+ def self.pool
55
+ @pool ||= ConnectionPool::Wrapper.new(size: 7, timeout: 20) { new }
56
+ end
57
+
58
+ def self.configuration
59
+ Github::Configuration.instance
60
+ end
61
+
62
+ private
63
+
64
+ def configuration
65
+ @configuration ||= self.class.configuration
66
+ end
67
+
68
+ def connection
69
+ @connection ||= Faraday.new(url: url) do |conn|
70
+ conn.response :follow_redirects
71
+
72
+ conn.adapter :net_http
73
+
74
+ conn.headers['Accept'] = "application/vnd.github.#{version}+json"
75
+ conn.headers['User-Agent'] = 'Horn-Of-Plenty-Gem'
76
+ conn.headers['Authorization'] = "Token #{token}"
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+ require 'horn_of_plenty/collection'
3
+ require 'horn_of_plenty/adapters/github/repositories/issue'
4
+ require 'horn_of_plenty/adapters/github/repositories/pull_request'
5
+
6
+ module HornOfPlenty
7
+ module Adapters
8
+ module Github
9
+ module Collections
10
+ class Card < HornOfPlenty::Collection
11
+ protected
12
+
13
+ # rubocop:disable Metrics/AbcSize
14
+ def transform(raw_item)
15
+ card = super
16
+
17
+ if %w{Issue PullRequest}.include? card.content_type
18
+ issues = Repositories::Issue.fetch(
19
+ repository: card.repository,
20
+ query: { id: card.content_id },
21
+ )
22
+
23
+ card.content_type = issues.first.has_pull_request? ? 'PullRequest' : 'Issue'
24
+ card.title = issues.first.title
25
+ card.description = issues.first.description
26
+ card.labels = issues.first.labels
27
+ card.status = issues.first.status
28
+
29
+ if issues.first.has_pull_request?
30
+ pull_requests = Repositories::PullRequest.fetch(
31
+ repository: card.repository,
32
+ query: { id: card.content_id },
33
+ )
34
+
35
+ card.status = pull_requests.first.status if pull_requests.first
36
+ end
37
+ end
38
+
39
+ card
40
+ end
41
+ # rubocop:enable Metrics/AbcSize
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+ require 'singleton'
3
+ require 'logger'
4
+
5
+ module HornOfPlenty
6
+ module Adapters
7
+ module Github
8
+ def self.configure
9
+ yield Configuration.instance
10
+ end
11
+
12
+ def self.config
13
+ return Configuration.instance unless block_given?
14
+
15
+ yield Configuration.instance
16
+ end
17
+
18
+ class Configuration
19
+ include Singleton
20
+
21
+ attr_accessor :token,
22
+ :logger
23
+
24
+ def base_url
25
+ 'https://api.github.com'
26
+ end
27
+
28
+ def api_version
29
+ 'inertia-preview'
30
+ end
31
+
32
+ def logger
33
+ @logger || Logger.new(STDOUT)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ require 'horn_of_plenty/core_ext/string'
3
+ require 'faraday'
4
+
5
+ module HornOfPlenty
6
+ module Adapters
7
+ module Github
8
+ class Error
9
+ # rubocop:disable Metrics/LineLength
10
+ STATUS_CODE_ERROR_MAPPING = {
11
+ 400 => ['BadRequest', 'There was a general issue with your request.'],
12
+ 401 => ['AuthenticationFailure', 'There was a problem with the credentials you provided.'],
13
+ 403 => ['Forbidden', 'You are not authorized to access this resource.'],
14
+ 404 => ['NotFound', 'The resource you requested cannot be found.'],
15
+ 422 => ['UnprocessableEntity', 'We could not process your request.'],
16
+ 500 => ['ServerError', 'Something went wrong on the server.'],
17
+ }.freeze
18
+ # rubocop:enable Metrics/LineLength
19
+
20
+ def self.lookup(http_status_code, message = nil)
21
+ error_data = STATUS_CODE_ERROR_MAPPING[http_status_code]
22
+
23
+ fail StandardError, "Unknown Status Code: #{http_status_code}" unless error_data
24
+
25
+ error_namespace = 'HornOfPlenty::Adapters::Github::Errors'
26
+ error_class = HornOfPlenty::CoreExt::String.constantize(
27
+ "#{error_namespace}::#{error_data[0]}",
28
+ )
29
+
30
+ error_class.new("#{error_data[1]}\n#{message}")
31
+ end
32
+ end
33
+
34
+ module Errors
35
+ Github::Error::STATUS_CODE_ERROR_MAPPING.each do |_code, (error_name, _error_message)|
36
+ module_eval <<-HEREDOC
37
+ class #{error_name} < StandardError
38
+ end
39
+ HEREDOC
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+ require 'horn_of_plenty/parsers/hash'
3
+ require 'horn_of_plenty/null_objects/board'
4
+ require 'horn_of_plenty/models/board'
5
+
6
+ module HornOfPlenty
7
+ module Adapters
8
+ module Github
9
+ module Parsers
10
+ class Board
11
+ include HornOfPlenty::Parsers::Hash
12
+
13
+ attr_accessor :raw
14
+
15
+ def initialize(raw:)
16
+ self.raw = raw
17
+ end
18
+
19
+ def id
20
+ @id ||= parse_text(raw, 'number')
21
+ end
22
+
23
+ def author_id
24
+ @author_id ||= parse_text(raw, 'creator/:/login')
25
+ end
26
+
27
+ def title
28
+ @title ||= parse_text(raw, 'name')
29
+ end
30
+
31
+ def description
32
+ @description ||= parse_text(raw, 'body')
33
+ end
34
+
35
+ def created_at
36
+ @created_at ||= parse_time(raw, 'created_at')
37
+ end
38
+
39
+ def updated_at
40
+ @updated_at ||= parse_time(raw, 'updated_at')
41
+ end
42
+
43
+ def self.to_model(raw_item)
44
+ return NullObjects::Board.instance unless raw_item
45
+
46
+ Models::Board.from_parser(parser: new(raw: raw_item))
47
+ end
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,91 @@
1
+ # frozen_string_literal: true
2
+ require 'horn_of_plenty/parsers/hash'
3
+ require 'horn_of_plenty/null_objects/card'
4
+ require 'horn_of_plenty/models/card'
5
+ require 'horn_of_plenty/core_ext/string'
6
+
7
+ # rubocop:disable Style/SpaceAroundOperators
8
+ module HornOfPlenty
9
+ module Adapters
10
+ module Github
11
+ module Parsers
12
+ class Card
13
+ include HornOfPlenty::Parsers::Hash
14
+
15
+ attr_accessor :raw
16
+
17
+ def initialize(raw:)
18
+ self.raw = raw
19
+ end
20
+
21
+ def id
22
+ @id ||= parse_text(raw, 'id')
23
+ end
24
+
25
+ def lane_id
26
+ @lane_id ||= parse_text(raw, 'column_url') do |url|
27
+ url.match(%r{\A.*/(\d+)\z})[1]
28
+ end
29
+ end
30
+
31
+ def repository
32
+ @repository ||= parse_text(raw, 'column_url') do |url|
33
+ repo_name = url.match(%r{\A.*/repos/((?:[^/]+/){2}).*\z})[1]
34
+
35
+ repo_name && repo_name[0..-2]
36
+ end
37
+ end
38
+
39
+ def content_type
40
+ @content_type ||= parse_text(raw,
41
+ 'content_url',
42
+ yield_if_nil: true) do |url|
43
+ if url
44
+ CoreExt::String.singularize(
45
+ CoreExt::String.camelize(url.match(%r{\A.*/(.*?)/\d+\z})[1]),
46
+ )
47
+ else
48
+ 'Note'
49
+ end
50
+ end
51
+ end
52
+
53
+ def content_id
54
+ @content_id ||= parse_text(raw, 'content_url') do |url|
55
+ url.match(%r{\A.*/(\d+)\z})[1]
56
+ end
57
+ end
58
+
59
+ def title
60
+ @title ||= parse_text(raw, 'note') do |note|
61
+ note.delete("\r").match(/\A(.*)$/)[1]
62
+ end
63
+ end
64
+
65
+ def description
66
+ @description ||= parse_text(raw, 'note') do |note|
67
+ match = note.delete("\r").match(/\A.*?(?:\n)+(.*)/m)
68
+
69
+ match[1] if match
70
+ end
71
+ end
72
+
73
+ def created_at
74
+ @created_at ||= parse_time(raw, 'created_at')
75
+ end
76
+
77
+ def updated_at
78
+ @updated_at ||= parse_time(raw, 'updated_at')
79
+ end
80
+
81
+ def self.to_model(raw_item)
82
+ return NullObjects::Card.instance unless raw_item
83
+
84
+ Models::Card.from_parser(parser: new(raw: raw_item))
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
91
+ # rubocop:enable Style/SpaceAroundOperators