Skip to content

Commit af52f6b

Browse files
committed
BMT class structure
1 parent 7a5dc05 commit af52f6b

File tree

14 files changed

+536
-3
lines changed

14 files changed

+536
-3
lines changed

bin/console

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#!/usr/bin/env ruby
22

33
require 'bundler/setup'
4-
require 'vrt'
4+
require 'bmt'
55
require 'pry'
66

7-
# An interactive console for the VRT gem
7+
# An interactive console for the BMT gem
88

99
Pry.start

bmt.gemspec

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ Gem::Specification.new do |spec|
1111
spec.email = ['[email protected]']
1212
spec.date = Date.today.to_s
1313
spec.summary = 'Ruby wrapper for Bugcrowd\'s Methodology Taxonomy'
14-
spec.homepage = 'https://github.com/bugcrowd/vrt-ruby'
14+
spec.homepage = 'https://github.com/bugcrowd/bmt-ruby'
1515
spec.license = 'MIT'
1616
spec.require_paths = ['lib']
1717
spec.required_ruby_version = '>= 2.5'

lib/bmt.rb

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,72 @@
1+
require 'bmt/item'
2+
require 'bmt/methodology'
3+
require 'bmt/step'
4+
5+
require 'date'
6+
require 'json'
7+
require 'pathname'
8+
19
module BMT
10+
class VersionNotFoundError < StandardError; end
11+
class MethodologyNotFoundError < StandardError; end
12+
DATA_DIR = Pathname.new(__dir__).join('data')
13+
14+
# memoize loaded
15+
@methodologies = {}
16+
@methodology_keys = {}
17+
18+
module_function
19+
20+
# returns a Methodology object given a key and a version
21+
def find(key, version: current_version)
22+
raise VersionNotFoundError unless versions.include?(version)
23+
raise MethodologyNotFoundError unless methodology_keys(version: version).include?(key)
24+
25+
@methodologies[version].nil? && @methodologies[version] = {}
26+
27+
@methodologies[version][key] ||= Methodology.new(
28+
key: key,
29+
version: version,
30+
attributes: methodology_json(key, version: version)
31+
)
32+
33+
@methodologies[version][key]
34+
end
35+
36+
def current_version
37+
versions.first
38+
end
39+
40+
# returns available methodology keys for a given version
41+
def methodology_keys(version: current_version)
42+
@methodology_keys[version] ||=
43+
DATA_DIR.join(version, 'methodologies').entries
44+
.map(&:basename)
45+
.map(&:to_s)
46+
.select { |dirname| dirname =~ /json/ }
47+
.map { |filepath| File.basename(filepath, File.extname(filepath)) }
48+
end
49+
50+
# Infer the avialable versions of the BMT from the names of the files
51+
# in the repo.
52+
# The returned list is in order with the current version first.
53+
def versions
54+
@versions ||= json_dir_names.sort.reverse!
55+
end
56+
57+
def methodology_json(key, version: current_version)
58+
JSON.parse(methodology_pathname(key, version: version).read)
59+
end
60+
61+
def methodology_pathname(key, version: current_version)
62+
DATA_DIR.join(version, 'methodologies', "#{key}.json")
63+
end
64+
65+
# Get names of directories matching lib/data/<major>-<minor>/
66+
def json_dir_names
67+
DATA_DIR.entries
68+
.map(&:basename)
69+
.map(&:to_s)
70+
.select { |dirname| dirname =~ /^[0-9]+\.[0-9]/ }.sort
71+
end
272
end

lib/bmt/item.rb

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
module BMT
2+
class Item
3+
attr_reader :key, :title, :description, :vrt_category, :step
4+
5+
def initialize(step:, attributes:)
6+
@step = step
7+
@key = attributes['key']
8+
@title = attributes['title']
9+
@description = attributes['description']
10+
@vrt_category = attributes['vrt_category']
11+
end
12+
end
13+
end

lib/bmt/methodology.rb

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
module BMT
2+
class Methodology
3+
attr_reader :key, :version, :title, :description, :vrt_version
4+
5+
def initialize(key:, version:, attributes: {})
6+
@key = key
7+
@version = version
8+
@title = attributes.dig('metadata', 'title')
9+
@release_date = attributes.dig('metadata', 'release_date')
10+
@description = attributes.dig('metadata', 'description')
11+
@vrt_version = attributes.dig('metadata', 'vrt_version')
12+
13+
@steps_data = attributes.dig('content', 'steps')
14+
end
15+
16+
def release_date
17+
Date.parse(@release_date)
18+
end
19+
20+
def steps
21+
@steps ||= @steps_data.map do |step_data|
22+
Step.new(
23+
methodology: self,
24+
attributes: step_data
25+
)
26+
end
27+
end
28+
end
29+
end

lib/bmt/step.rb

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
module BMT
2+
class Step
3+
attr_reader :key, :title, :description, :methodology
4+
5+
def initialize(methodology:, attributes:)
6+
@methodology = methodology
7+
@key = attributes['key']
8+
@title = attributes['title']
9+
@description = attributes['description']
10+
@items_data = attributes['items']
11+
end
12+
13+
def items
14+
@items ||= @items_data.map do |item_data|
15+
Item.new(
16+
step: self,
17+
attributes: item_data
18+
)
19+
end
20+
end
21+
end
22+
end

spec/bmt/item_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe BMT::Item do
6+
let(:step) { methodology.steps.first }
7+
let(:methodology) { BMT.find(methodology_key) }
8+
let(:methodology_json) { BMT.methodology_json(methodology_key) }
9+
let(:attributes) { methodology_json.dig('content', 'steps', 0, 'items', 0) }
10+
let(:methodology_key) { 'outback-animal-testing' }
11+
12+
describe '#new' do
13+
subject do
14+
described_class.new(
15+
step: step,
16+
attributes: attributes
17+
)
18+
end
19+
20+
it 'initialize an item' do
21+
expect(subject).to be_a(described_class)
22+
23+
expect(subject.key).to eq('marsupial')
24+
expect(subject.title).to be_a(String)
25+
expect(subject.description).to be_a(String)
26+
expect(subject.vrt_category).to be_a(String)
27+
expect(subject.step).to eq(step)
28+
end
29+
end
30+
end

spec/bmt/methodology_spec.rb

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe BMT::Methodology do
6+
describe '#new' do
7+
subject do
8+
described_class.new(key: methodology_key,
9+
version: version,
10+
attributes: methodology_json)
11+
end
12+
13+
context 'with valid args' do
14+
let(:methodology_key) { 'outback-animal-testing' }
15+
let(:version) { '2.1' }
16+
let(:methodology_json) { BMT.methodology_json(methodology_key) }
17+
18+
it 'initialize a methodology' do
19+
expect(subject).to be_a(described_class)
20+
21+
expect(subject.key).to eq('outback-animal-testing')
22+
expect(subject.title).to eq('The Outback Animal Checklist')
23+
expect(subject.description).to be_a(String)
24+
end
25+
end
26+
end
27+
28+
describe '#steps' do
29+
subject do
30+
described_class.new(key: methodology_key,
31+
version: version,
32+
attributes: methodology_json).steps
33+
end
34+
35+
let(:methodology_key) { 'outback-animal-testing' }
36+
let(:version) { '2.1' }
37+
let(:methodology_json) { BMT.methodology_json(methodology_key) }
38+
39+
it 'returns a list of BMT Steps' do
40+
expect(subject.first).to be_a(BMT::Step)
41+
42+
expect(subject).to match_array(
43+
[
44+
an_object_having_attributes(
45+
key: 'koala',
46+
title: 'Check that you have a Koala',
47+
description: an_instance_of(String),
48+
items: match_array(
49+
[
50+
an_object_having_attributes(
51+
key: 'marsupial',
52+
title: an_instance_of(String),
53+
description: an_instance_of(String),
54+
vrt_category: an_instance_of(String)
55+
),
56+
an_object_having_attributes(
57+
key: 'diet',
58+
title: an_instance_of(String),
59+
description: an_instance_of(String)
60+
),
61+
an_object_having_attributes(
62+
key: 'behavior',
63+
title: an_instance_of(String),
64+
description: an_instance_of(String)
65+
)
66+
]
67+
)
68+
),
69+
an_object_having_attributes(
70+
key: 'kangaroo',
71+
title: 'Ensure you have a kangaroo',
72+
description: an_instance_of(String),
73+
items: match_array(
74+
[
75+
an_object_having_attributes(
76+
key: 'marsupial',
77+
title: an_instance_of(String),
78+
description: an_instance_of(String)
79+
),
80+
an_object_having_attributes(
81+
key: 'tail',
82+
title: an_instance_of(String),
83+
description: an_instance_of(String)
84+
),
85+
an_object_having_attributes(
86+
key: 'jump',
87+
title: an_instance_of(String),
88+
description: an_instance_of(String)
89+
)
90+
]
91+
)
92+
)
93+
]
94+
)
95+
end
96+
end
97+
end

spec/bmt/step_spec.rb

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
# frozen_string_literal: true
2+
3+
require 'spec_helper'
4+
5+
describe BMT::Step do
6+
let(:methodology) { BMT.find(methodology_key) }
7+
let(:methodology_json) { BMT.methodology_json(methodology_key) }
8+
let(:attributes) { methodology_json.dig('content', 'steps', 0) }
9+
let(:methodology_key) { 'outback-animal-testing' }
10+
11+
describe '#new' do
12+
subject do
13+
described_class.new(
14+
methodology: methodology,
15+
attributes: attributes
16+
)
17+
end
18+
19+
it 'initialize a step' do
20+
expect(subject).to be_a(described_class)
21+
22+
expect(subject.key).to eq('koala')
23+
expect(subject.title).to be_a(String)
24+
expect(subject.description).to be_a(String)
25+
expect(subject.methodology).to eq(methodology)
26+
end
27+
end
28+
29+
describe '#items' do
30+
subject do
31+
described_class.new(
32+
methodology: methodology,
33+
attributes: attributes
34+
).items
35+
end
36+
37+
it 'returns a list of BMT Items' do
38+
expect(subject.first).to be_a(BMT::Item)
39+
40+
expect(subject).to match_array(
41+
[
42+
an_object_having_attributes(key: 'marsupial'),
43+
an_object_having_attributes(key: 'diet'),
44+
an_object_having_attributes(key: 'behavior')
45+
]
46+
)
47+
end
48+
end
49+
end

0 commit comments

Comments
 (0)