obvious 0.0.8 → 0.2.0
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.
- checksums.yaml +7 -0
- data/.github/workflows/obvious.yml +19 -0
- data/.gitignore +0 -1
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +19 -0
- data/README.md +4 -33
- data/Rakefile +8 -0
- data/lib/obvious/contract.rb +154 -144
- data/lib/obvious/obj.rb +36 -0
- data/lib/obvious/version.rb +1 -1
- data/lib/obvious.rb +1 -1
- data/obvious.gemspec +5 -7
- data/test/contract_test.rb +62 -0
- data/test/entity_test.rb +76 -0
- data/test/obj_test.rb +65 -0
- metadata +26 -48
- data/bin/obvious +0 -7
- data/lib/generators/application_generator.rb +0 -199
- data/lib/generators/descriptor.rb +0 -131
- data/lib/generators/helpers/application.rb +0 -47
- data/lib/obvious/files/Rakefile +0 -4
- data/lib/obvious/files/external/fs_plug.rb +0 -99
- data/lib/obvious/files/external/mongo_plug.rb +0 -62
- data/lib/obvious/files/external/mysql_plug.rb +0 -38
- data/lib/obvious/files/external/s3_plug.rb +0 -94
- data/spec/.spec_helper.rb.swp +0 -0
- data/spec/contract_spec.rb +0 -63
- data/spec/entity_spec.rb +0 -75
- data/spec/generators/descriptor_spec.rb +0 -34
- data/spec/spec_helper.rb +0 -3
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
|
3
|
-
class FsPlug
|
4
|
-
|
5
|
-
def initialize filename
|
6
|
-
@filename = filename
|
7
|
-
end
|
8
|
-
|
9
|
-
def load_data
|
10
|
-
contents = File.read @filename
|
11
|
-
JSON.parse contents, :symbolize_names => true
|
12
|
-
end
|
13
|
-
|
14
|
-
def save_data input
|
15
|
-
json_data = JSON.pretty_generate input
|
16
|
-
File.open(@filename, 'w') {|f| f.write(json_data) }
|
17
|
-
end
|
18
|
-
|
19
|
-
def save input
|
20
|
-
data = []
|
21
|
-
query = load_data
|
22
|
-
|
23
|
-
new_element = true if input[:id] == -1 # by convention set new element flag if id == -1
|
24
|
-
|
25
|
-
max_id = -1
|
26
|
-
# transform the data if needed
|
27
|
-
query.each do |h|
|
28
|
-
if input[:id] == h[:id]
|
29
|
-
h = input
|
30
|
-
end
|
31
|
-
max_id = h[:id] if h[:id] > max_id
|
32
|
-
data << h
|
33
|
-
end
|
34
|
-
|
35
|
-
# add data to the list if it's a new element
|
36
|
-
if new_element
|
37
|
-
input[:id] = max_id + 1
|
38
|
-
data << input
|
39
|
-
end
|
40
|
-
|
41
|
-
save_data data
|
42
|
-
|
43
|
-
# return the transformed data
|
44
|
-
input
|
45
|
-
end
|
46
|
-
|
47
|
-
def list
|
48
|
-
load_data
|
49
|
-
end
|
50
|
-
|
51
|
-
def get input
|
52
|
-
data = []
|
53
|
-
query = load_data
|
54
|
-
|
55
|
-
# transform the data if needed
|
56
|
-
query.each do |h|
|
57
|
-
return h if h[:id] == input[:id]
|
58
|
-
end
|
59
|
-
|
60
|
-
raise Exception.new 'no object found'
|
61
|
-
end
|
62
|
-
|
63
|
-
def remove input
|
64
|
-
data = []
|
65
|
-
# parse the json list
|
66
|
-
query = load_data
|
67
|
-
|
68
|
-
# transform the data if needed
|
69
|
-
query.each do |h|
|
70
|
-
unless h[:id] == input[:id]
|
71
|
-
data << h
|
72
|
-
end
|
73
|
-
end
|
74
|
-
|
75
|
-
save_data data
|
76
|
-
|
77
|
-
# return true on success
|
78
|
-
true
|
79
|
-
end
|
80
|
-
|
81
|
-
def find input
|
82
|
-
data = []
|
83
|
-
query = load_data
|
84
|
-
|
85
|
-
key = input.keys[0]
|
86
|
-
|
87
|
-
query.each do |h|
|
88
|
-
if input[key] == h[key]
|
89
|
-
return h
|
90
|
-
end
|
91
|
-
end
|
92
|
-
|
93
|
-
raise Exception.new 'no object found'
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
@@ -1,62 +0,0 @@
|
|
1
|
-
require 'moped'
|
2
|
-
|
3
|
-
# To get the mongo system to work you need to have a counters collection. The
|
4
|
-
# code here will increment the seq field which we use to set the id. If you are
|
5
|
-
# having problems, you probably need to create the collection and add entries
|
6
|
-
# for each other collection you want to have an id sequence.
|
7
|
-
|
8
|
-
class MongoPlug
|
9
|
-
def initialize collection
|
10
|
-
@collection = collection
|
11
|
-
@session = MONGO_SESSION
|
12
|
-
end
|
13
|
-
|
14
|
-
def list
|
15
|
-
result = @session[@collection].find.entries
|
16
|
-
result.map! do |entry|
|
17
|
-
clean_up entry
|
18
|
-
end
|
19
|
-
|
20
|
-
result
|
21
|
-
end
|
22
|
-
|
23
|
-
def get input
|
24
|
-
result = symbolize_keys @session[@collection].find(:id => input[:id]).first
|
25
|
-
clean_up result
|
26
|
-
end
|
27
|
-
|
28
|
-
def save input
|
29
|
-
if input[:id] == -1
|
30
|
-
id = @session[:counters].find(:_id => @collection).modify({ "$inc" => { seq: 1 } }, new:true)["seq"]
|
31
|
-
input[:id] = id
|
32
|
-
end
|
33
|
-
result = @session[@collection].find(:id => input[:id]).modify(input, upsert: true, new: true)
|
34
|
-
clean_up result
|
35
|
-
end
|
36
|
-
|
37
|
-
def remove input
|
38
|
-
@session[@collection].find(:id => input[:id]).remove
|
39
|
-
true
|
40
|
-
end
|
41
|
-
|
42
|
-
def clean_up input
|
43
|
-
result = symbolize_keys input
|
44
|
-
result.delete :_id
|
45
|
-
result
|
46
|
-
end
|
47
|
-
|
48
|
-
def symbolize_keys hash
|
49
|
-
hash.inject({}){|result, (key, value)|
|
50
|
-
new_key = case key
|
51
|
-
when String then key.to_sym
|
52
|
-
else key
|
53
|
-
end
|
54
|
-
new_value = case value
|
55
|
-
when Hash then symbolize_keys(value)
|
56
|
-
else value
|
57
|
-
end
|
58
|
-
result[new_key] = new_value
|
59
|
-
result
|
60
|
-
}
|
61
|
-
end
|
62
|
-
end
|
@@ -1,38 +0,0 @@
|
|
1
|
-
require 'sequel'
|
2
|
-
|
3
|
-
# assume that DB is defined as a global constant elsewhere
|
4
|
-
|
5
|
-
class MysqlPlug
|
6
|
-
|
7
|
-
def initialize table
|
8
|
-
@table = table
|
9
|
-
end
|
10
|
-
|
11
|
-
def save input
|
12
|
-
table = DB[@table]
|
13
|
-
input[:id] = nil if input[:id] == -1
|
14
|
-
|
15
|
-
# this does an upsert
|
16
|
-
result = table.on_duplicate_key_update.insert input
|
17
|
-
|
18
|
-
input[:id] = result
|
19
|
-
input
|
20
|
-
end
|
21
|
-
|
22
|
-
def list
|
23
|
-
table = DB[@table]
|
24
|
-
table.all
|
25
|
-
end
|
26
|
-
|
27
|
-
def get input
|
28
|
-
table = DB[@table]
|
29
|
-
table.first :id => input[:id]
|
30
|
-
end
|
31
|
-
|
32
|
-
def remove input
|
33
|
-
table = DB[@table]
|
34
|
-
result = table.where(:id => input[:id]).delete
|
35
|
-
return true if result == 1
|
36
|
-
false
|
37
|
-
end
|
38
|
-
end
|
@@ -1,94 +0,0 @@
|
|
1
|
-
require 'json'
|
2
|
-
require 'aws/s3'
|
3
|
-
|
4
|
-
class S3Plug
|
5
|
-
include AWS::S3
|
6
|
-
|
7
|
-
def initialize input
|
8
|
-
@filename = input[:filename]
|
9
|
-
@bucket = input[:bucket]
|
10
|
-
|
11
|
-
# you can test locally with the fake-s3 gem: https://github.com/jubos/fake-s3
|
12
|
-
# or just swap out for the filesystem plug locally
|
13
|
-
s3_key = ENV['S3_KEY'] || '123'
|
14
|
-
s3_secret = ENV['S3_SECRET'] || 'abc'
|
15
|
-
s3_server = ENV['S3_SERVER'] || '0.0.0.0'
|
16
|
-
s3_port = ENV['S3_PORT'] || '10001'
|
17
|
-
|
18
|
-
AWS::S3::Base.establish_connection!(:access_key_id => s3_key,
|
19
|
-
:secret_access_key => s3_secret,
|
20
|
-
:server => s3_server,
|
21
|
-
:port => s3_port)
|
22
|
-
end
|
23
|
-
|
24
|
-
def load_data
|
25
|
-
contents = S3Object.value @filename, @bucket
|
26
|
-
JSON.parse contents, :symbolize_names => true
|
27
|
-
end
|
28
|
-
|
29
|
-
def save_data data
|
30
|
-
json_data = JSON.pretty_generate data
|
31
|
-
S3Object.store @filename, json_data, @bucket
|
32
|
-
end
|
33
|
-
|
34
|
-
def save input
|
35
|
-
data = []
|
36
|
-
result = load_data
|
37
|
-
new_element = true if input[:id] == -1 # by convention set new element flag if id == -1
|
38
|
-
|
39
|
-
max_id = -1
|
40
|
-
# transform the data if needed
|
41
|
-
result.each do |h|
|
42
|
-
if input[:id] == h[:id]
|
43
|
-
h = input
|
44
|
-
end
|
45
|
-
max_id = h[:id] if h[:id] > max_id
|
46
|
-
data << h
|
47
|
-
end
|
48
|
-
|
49
|
-
# add data to the list if it's a new element
|
50
|
-
if new_element
|
51
|
-
input[:id] = max_id + 1
|
52
|
-
data << input
|
53
|
-
end
|
54
|
-
|
55
|
-
save_data data
|
56
|
-
|
57
|
-
# return the transformed data
|
58
|
-
input
|
59
|
-
end
|
60
|
-
|
61
|
-
def list
|
62
|
-
load_data
|
63
|
-
end
|
64
|
-
|
65
|
-
def get input
|
66
|
-
data = []
|
67
|
-
result = load_data
|
68
|
-
|
69
|
-
# transform the data if needed
|
70
|
-
result.each do |h|
|
71
|
-
return h if h[:id] == input[:id]
|
72
|
-
end
|
73
|
-
|
74
|
-
{}
|
75
|
-
end
|
76
|
-
|
77
|
-
def remove input
|
78
|
-
data = []
|
79
|
-
result = load_data
|
80
|
-
|
81
|
-
# transform the data if needed
|
82
|
-
result.each do |h|
|
83
|
-
unless h[:id] == input[:id]
|
84
|
-
data << h
|
85
|
-
end
|
86
|
-
end
|
87
|
-
|
88
|
-
save_data data
|
89
|
-
|
90
|
-
# return true on success
|
91
|
-
true
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
data/spec/.spec_helper.rb.swp
DELETED
Binary file
|
data/spec/contract_spec.rb
DELETED
@@ -1,63 +0,0 @@
|
|
1
|
-
require_relative 'spec_helper'
|
2
|
-
require_relative '../lib/obvious/contract'
|
3
|
-
|
4
|
-
|
5
|
-
describe Hash do
|
6
|
-
describe '#has_shape?' do
|
7
|
-
it 'should return true for a valid shape' do
|
8
|
-
{ id: 1 }.has_shape?(id: Fixnum).should be true
|
9
|
-
end
|
10
|
-
|
11
|
-
it 'should return false for an invalid shape' do
|
12
|
-
{ id: 1 }.has_shape?(id: String).should be false
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'should retrn the invalid field if return_field flag is set' do
|
16
|
-
{ id: 1 }.has_shape?({id: String}, true).should eq [false, :id]
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'should allow for nil values to be returned' do
|
20
|
-
{ id: nil }.has_shape?({id: String}).should be true
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class TestContract < Contract
|
26
|
-
contract_for :test, {
|
27
|
-
input: { id: Fixnum },
|
28
|
-
output: { id: Fixnum, value: String }
|
29
|
-
}
|
30
|
-
|
31
|
-
def test input
|
32
|
-
{ id: 1, value: 'this is a test' }
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
describe Contract do
|
37
|
-
|
38
|
-
describe "#call_method" do
|
39
|
-
it 'should return the correct output for valid input and output shapes' do
|
40
|
-
tc = TestContract.new
|
41
|
-
result = tc.test id: 1
|
42
|
-
result.should eq id: 1, value: 'this is a test'
|
43
|
-
end
|
44
|
-
|
45
|
-
it 'should raise a contract input error with bad input' do
|
46
|
-
tc = TestContract.new
|
47
|
-
expect { tc.test Hash.new }.to raise_error ContractInputError
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'should raise a DataNotFound error if {} is returned' do
|
51
|
-
tc = TestContract.new
|
52
|
-
tc.should_receive(:test_alias).and_return({})
|
53
|
-
expect { tc.test id: 1 }.to raise_error DataNotFoundError
|
54
|
-
end
|
55
|
-
|
56
|
-
it 'should raise a contract output error if nil is returned' do
|
57
|
-
tc = TestContract.new
|
58
|
-
tc.should_receive(:test_alias).and_return(nil)
|
59
|
-
expect { tc.test id: 1 }.to raise_error ContractOutputError
|
60
|
-
end
|
61
|
-
|
62
|
-
end
|
63
|
-
end
|
data/spec/entity_spec.rb
DELETED
@@ -1,75 +0,0 @@
|
|
1
|
-
require_relative '../lib/obvious/entity'
|
2
|
-
|
3
|
-
class Thing < Obvious::Entity
|
4
|
-
value :id, Fixnum
|
5
|
-
value :name, String
|
6
|
-
end
|
7
|
-
|
8
|
-
class Thing2 < Obvious::Entity
|
9
|
-
value :foo , String
|
10
|
-
|
11
|
-
validation :something, Proc.new {
|
12
|
-
if foo != "hello world"
|
13
|
-
msg = "Validation Error: Invalid value for foo, should be 'hello world'"
|
14
|
-
raise Obvious::ValidationError.new msg
|
15
|
-
end
|
16
|
-
}
|
17
|
-
|
18
|
-
def modify_foo
|
19
|
-
@values[:foo] = 100
|
20
|
-
end
|
21
|
-
|
22
|
-
end
|
23
|
-
|
24
|
-
class Thing3 < Obvious::Entity
|
25
|
-
value :foo , String
|
26
|
-
|
27
|
-
validation :something, Proc.new {
|
28
|
-
@values[:foo] = 12
|
29
|
-
}
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
# To test the entity, we are going to use classes that inherit from it instead
|
34
|
-
# of poking at it directly. In this case, I think that makes the most sense.
|
35
|
-
describe Thing do
|
36
|
-
it 'should create a valid object with valid input' do
|
37
|
-
input = { name: 'Thing', id: 1 }
|
38
|
-
t = Thing.new input
|
39
|
-
t.name.should eq 'Thing'
|
40
|
-
t.id.should eq 1
|
41
|
-
end
|
42
|
-
|
43
|
-
it 'should raise an error with invalid input types' do
|
44
|
-
input = { name: nil, id: nil }
|
45
|
-
expect { Thing.new input }.to raise_error Obvious::TypeError
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'should raise an error with extra fields in input' do
|
49
|
-
input = { name: 'Thing', id: 1, extra: 'should explode' }
|
50
|
-
expect { Thing.new input }.to raise_error Obvious::ShapeError
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'should raise an error when a method tries to modify a value' do
|
54
|
-
t = Thing2.new foo: 'hello world'
|
55
|
-
expect { t.modify_foo }.to raise_error RuntimeError
|
56
|
-
end
|
57
|
-
|
58
|
-
describe '#to_hash' do
|
59
|
-
it 'should return a hash representation of the object' do
|
60
|
-
input = { name: 'Thing', id: 1 }
|
61
|
-
t = Thing.new input
|
62
|
-
t.to_hash.should eq input
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
describe 'validation' do
|
67
|
-
it 'should raise a validation error on a failed validation' do
|
68
|
-
expect { Thing2.new foo: 'not valid I promise!' }.to raise_error Obvious::ValidationError
|
69
|
-
end
|
70
|
-
|
71
|
-
it 'should raise an error when trying to modify an object in a validation' do
|
72
|
-
expect { Thing3.new foo: 'hello world' }.to raise_error RuntimeError
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
@@ -1,34 +0,0 @@
|
|
1
|
-
require_relative '../../lib/generators/descriptor'
|
2
|
-
|
3
|
-
require File.expand_path('spec/spec_helper')
|
4
|
-
|
5
|
-
module Obvious
|
6
|
-
module Generators
|
7
|
-
describe Descriptor do
|
8
|
-
subject {Descriptor.new(yaml_file)}
|
9
|
-
|
10
|
-
describe "#to_file" do
|
11
|
-
|
12
|
-
context "when the descriptor is empty" do
|
13
|
-
let( :yaml_file ) { {} }
|
14
|
-
|
15
|
-
it "should raise a meaningful error" do
|
16
|
-
expect {subject.to_file}.to raise_error(InvalidDescriptorError)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
["Action", "Code", "Description"].each do |section|
|
21
|
-
context "when the '#{section}' section is omitted" do
|
22
|
-
let( :yaml_file ) {
|
23
|
-
{"Action" => "Jackson", "Description" => "This is something"}.delete(section)
|
24
|
-
}
|
25
|
-
|
26
|
-
it "should raise a meaningful error" do
|
27
|
-
expect {subject.to_file}.to raise_error(InvalidDescriptorError)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/spec/spec_helper.rb
DELETED