obvious 0.0.7 → 0.0.8

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.
data/.gitignore CHANGED
@@ -1,5 +1,6 @@
1
1
  *.gem
2
2
  *.rbc
3
+ *.swp
3
4
  .bundle
4
5
  .config
5
6
  .yardoc
@@ -30,7 +30,8 @@ module Obvious
30
30
 
31
31
  puts 'Creating actions from descriptors... ' unless descriptors.length.zero?
32
32
  descriptors.each do |file|
33
- descriptor = Obvious::Generators::Descriptor.new file
33
+ yaml = YAML.load_file(file)
34
+ descriptor = Obvious::Generators::Descriptor.new yaml
34
35
  descriptor.to_file
35
36
  end
36
37
 
@@ -150,22 +151,22 @@ class #{k}Contract < Contract
150
151
  end
151
152
  }
152
153
 
153
- snake_name = name.gsub(/(.)([A-Z])/,'\1_\2').downcase
154
+ snake_name = k.gsub(/(.)([A-Z])/,'\1_\2').downcase
154
155
 
155
- filename = "#{@app.dir}/contracts/#{snake_name}_jack_contract.rb"
156
+ filename = "#{@app.dir}/contracts/#{snake_name}_contract.rb"
156
157
  File.open(filename, 'w') {|f| f.write(output) }
157
158
 
158
- output = %Q{require_relative '../../contracts/#{snake_name}_jack_contract'
159
+ output = %Q{require_relative '../../contracts/#{snake_name}_contract'
159
160
 
160
161
  describe #{k}Contract do
161
162
  #{method_specs}
162
163
  end
163
164
  }
164
165
 
165
- filename = "#{@app.dir}/spec/contracts/#{snake_name}_jack_spec.rb"
166
+ filename = "#{@app.dir}/spec/contracts/#{snake_name}_spec.rb"
166
167
  File.open(filename, 'w') {|f| f.write(output) }
167
168
 
168
- output = %Q{require_relative '../../contracts/#{snake_name}_jack_contract'
169
+ output = %Q{require_relative '../../contracts/#{snake_name}_contract'
169
170
 
170
171
  class #{k}Double
171
172
  def self.create behavior
@@ -187,7 +188,7 @@ class #{k}_BadOutput < #{k}Contract
187
188
  end
188
189
  }
189
190
 
190
- filename = "#{@app.dir}/spec/doubles/#{snake_name}_jack_double.rb"
191
+ filename = "#{@app.dir}/spec/doubles/#{snake_name}_double.rb"
191
192
  File.open(filename, 'w') {|f| f.write(output) }
192
193
 
193
194
  end
@@ -4,22 +4,25 @@ require_relative 'helpers/application'
4
4
 
5
5
  module Obvious
6
6
  module Generators
7
+ class InvalidDescriptorError < StandardError; end
8
+
7
9
  class Descriptor
8
10
  def initialize descriptor
9
11
  @descriptor = descriptor
10
12
  end
11
13
 
12
14
  def to_file
13
- action = YAML.load_file @descriptor
15
+ validate_descriptor
16
+
14
17
  @jacks, @entities = {}, {}
15
18
  @code = ''
16
19
 
17
- action['Code'].each do |entry|
20
+ @descriptor['Code'].each do |entry|
18
21
  write_comments_for entry
19
22
  process_requirements_for entry if entry['requires']
20
23
  end
21
24
 
22
- write_action action
25
+ write_action
23
26
  end
24
27
 
25
28
  private
@@ -53,12 +56,12 @@ module Obvious
53
56
  end
54
57
  end # #process_requirements_for
55
58
 
56
- def write_action action
59
+ def write_action
57
60
  jacks_data = process_jacks
58
61
  requirements = require_entities
59
62
 
60
63
  output = %Q{#{requirements}
61
- class #{action['Action']}
64
+ class #{@descriptor['Action']}
62
65
 
63
66
  def initialize #{jacks_data[:inputs]}
64
67
  #{jacks_data[:assignments]} end
@@ -68,24 +71,24 @@ class #{action['Action']}
68
71
  end
69
72
  }
70
73
 
71
- snake_name = action['Action'].gsub(/(.)([A-Z])/,'\1_\2').downcase
74
+ snake_name = @descriptor['Action'].gsub(/(.)([A-Z])/,'\1_\2').downcase
72
75
 
73
76
  filename = "#{Obvious::Generators::Application.instance.dir}/actions/#{snake_name}.rb"
74
77
  File.open(filename, 'w') {|f| f.write(output) }
75
78
 
76
- output = %Q{require_relative '../../actions/#{snake_name}'
79
+ output = %Q{require_relative '../../actions/#{snake_name}'
77
80
 
78
- describe #{action['Action']} do
81
+ describe #{@descriptor['Action']} do
79
82
 
80
- it '#{action['Description']}'
83
+ it '#{@descriptor['Description']}'
81
84
 
82
85
  it 'should raise an error with invalid input'
83
86
 
84
87
  end
85
- }
88
+ }
86
89
 
87
- filename = "#{Obvious::Generators::Application.instance.dir}/spec/actions/#{snake_name}_spec.rb"
88
- File.open(filename, 'w') {|f| f.write(output) }
90
+ filename = "#{Obvious::Generators::Application.instance.dir}/spec/actions/#{snake_name}_spec.rb"
91
+ File.open(filename, 'w') {|f| f.write(output) }
89
92
  end
90
93
 
91
94
  def process_jacks
@@ -116,6 +119,13 @@ end
116
119
 
117
120
  entity_requires
118
121
  end
122
+
123
+ def validate_descriptor
124
+ raise InvalidDescriptorError unless @descriptor
125
+ raise InvalidDescriptorError if @descriptor['Code'].nil?
126
+ raise InvalidDescriptorError if @descriptor['Action'].nil?
127
+ raise InvalidDescriptorError if @descriptor['Description'].nil?
128
+ end
119
129
  end # ::Descriptor
120
130
  end
121
131
  end
@@ -91,8 +91,9 @@ class Contract
91
91
  # of the output_shape.
92
92
  def call_method method, input, input_shape, output_shape
93
93
  if input != nil && input_shape != nil
94
- unless input.has_shape? input_shape
95
- raise ContractInputError, 'incorrect input data format'
94
+ has_shape, error_field = input.has_shape? input_shape, true
95
+ unless has_shape
96
+ raise ContractInputError, "incorrect input data format field #{error_field}"
96
97
  end
97
98
 
98
99
  result = self.send method, input
@@ -106,6 +107,10 @@ class Contract
106
107
  raise ContractOutputError, 'incorrect output data format'
107
108
  end
108
109
 
110
+ if result === {}
111
+ raise DataNotFoundError, 'data was not found'
112
+ end
113
+
109
114
  # we are looking for result to be a True object
110
115
  if output_shape === true
111
116
  if output_shape == result
@@ -120,8 +125,9 @@ class Contract
120
125
  if result.class == Array
121
126
  inner_shape = output_shape[0]
122
127
  result.each do |item|
123
- unless item.has_shape? inner_shape
124
- raise ContractOutputError, 'incorrect output data format'
128
+ has_shape, error_field = item.has_shape? inner_shape, true
129
+ unless has_shape
130
+ raise ContractOutputError, "incorrect output data format field #{error_field}"
125
131
  end
126
132
  end
127
133
 
@@ -140,8 +146,9 @@ class Contract
140
146
  end
141
147
 
142
148
  # we want result to be output_shape's shape
143
- unless result.has_shape? output_shape
144
- raise ContractOutputError, 'incorrect output data format'
149
+ has_shape, error_field = result.has_shape? output_shape, true
150
+ unless has_shape
151
+ raise ContractOutputError, "incorrect output data format field #{error_field}"
145
152
  end
146
153
 
147
154
  result
@@ -162,18 +169,41 @@ class Hash
162
169
  # shape = { k1: Array, k2: { k3: Module } }
163
170
  # h.has_shape?(shape)
164
171
  # #=> true
165
- def has_shape?(shape)
172
+ def has_shape?(shape, return_field = false)
173
+ return_value = lambda { |r, f|
174
+ if return_field
175
+ return r, f
176
+ else
177
+ return r
178
+ end
179
+ }
180
+
166
181
  # I added an empty check
167
182
  if self.empty?
168
- return shape.empty?
169
- end
170
-
171
- shape.all? do |k, v|
183
+ return return_value.call shape.empty?, nil
184
+ end
185
+
186
+ self.each do |k, v|
187
+ return return_value.call false, k if shape[k] == nil
188
+ end
189
+
190
+ shape.each do |k, v|
172
191
  # hash_value
173
192
  hv = self[k]
174
- Hash === hv ? hv.has_shape?(v) : v === hv
193
+ return return_value.call false, k unless self.has_key? k
194
+
195
+ next if hv === nil
196
+
197
+ if Hash === hv
198
+ return hv.has_shape?(v, return_field)
199
+ else
200
+ return return_value.call false, k unless v === hv
201
+ end
175
202
  end
203
+
204
+ return_value.call true, nil
176
205
  end
206
+
177
207
  end
178
208
 
179
209
  class ContractInputError < StandardError
@@ -181,3 +211,7 @@ end
181
211
 
182
212
  class ContractOutputError < StandardError
183
213
  end
214
+
215
+ class DataNotFoundError < StandardError
216
+ end
217
+
@@ -0,0 +1,74 @@
1
+
2
+ module Obvious
3
+
4
+ module EntityMixin
5
+ class << self
6
+ def included(base)
7
+ base.extend ClassMethods
8
+ end
9
+ end
10
+
11
+ module ClassMethods
12
+ attr_reader :shape
13
+ attr_reader :validations
14
+
15
+ def value name, type
16
+ name = name.to_sym
17
+ @shape ||= {}
18
+ @shape[name] = type
19
+ define_method(name) { @values[name] }
20
+ end
21
+
22
+ def validation name, method
23
+ name = "#{name}_validation".to_sym
24
+ @validations ||= []
25
+ @validations << name
26
+ define_method(name) { instance_exec &method }
27
+ end
28
+ end
29
+ end
30
+
31
+ class ShapeError < StandardError; end
32
+ class TypeError < StandardError; end
33
+ class ValidationError < StandardError; end
34
+
35
+ class Entity
36
+ include EntityMixin
37
+
38
+ def initialize input
39
+ shape = self.class.shape
40
+ validations = self.class.validations || []
41
+
42
+ self.class.shape.freeze
43
+ self.class.validations.freeze
44
+
45
+ @values = {}
46
+ input.each do |k, v|
47
+ unless shape[k]
48
+ raise Obvious::ShapeError.new "Invalid input field: #{k}"
49
+ else
50
+ @values[k] = v
51
+ end
52
+ end
53
+
54
+ @values.freeze # this might need to be recursive?
55
+
56
+ shape.each do |k, v|
57
+ unless @values[k].class == v
58
+ msg = "Validation Error: Invalid value for #{k}, should be a #{v}"
59
+ raise Obvious::TypeError.new msg
60
+ end
61
+ end
62
+
63
+ validations.each { |method| send method }
64
+
65
+ freeze
66
+ end
67
+
68
+ def to_hash
69
+ {}.tap {|h| @values.each { |k, v| h[k] = v } }
70
+ end
71
+
72
+ end
73
+ end
74
+
@@ -6,13 +6,19 @@ class FsPlug
6
6
  @filename = filename
7
7
  end
8
8
 
9
- def save input
10
- # open the file
9
+ def load_data
11
10
  contents = File.read @filename
11
+ JSON.parse contents, :symbolize_names => true
12
+ end
12
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
13
20
  data = []
14
- # parse the json list
15
- query = JSON.parse contents, :symbolize_names => true
21
+ query = load_data
16
22
 
17
23
  new_element = true if input[:id] == -1 # by convention set new element flag if id == -1
18
24
 
@@ -27,51 +33,37 @@ class FsPlug
27
33
  end
28
34
 
29
35
  # add data to the list if it's a new element
30
- input[:id] = max_id + 1
31
- data << input if new_element
36
+ if new_element
37
+ input[:id] = max_id + 1
38
+ data << input
39
+ end
32
40
 
33
- # save the data back to FS
34
- json_data = JSON.pretty_generate data
35
- File.open(@filename, 'w') {|f| f.write(json_data) }
41
+ save_data data
36
42
 
37
43
  # return the transformed data
38
44
  input
39
45
  end
40
46
 
41
47
  def list
42
- # open the file
43
- contents = File.read @filename
44
-
45
- # parse the json list
46
- data = JSON.parse contents, :symbolize_names => true
47
-
48
- # return the transformed data
49
- data
48
+ load_data
50
49
  end
51
50
 
52
51
  def get input
53
- # open the file
54
- contents = File.read @filename
55
-
56
52
  data = []
57
- # parse the json list
58
- query = JSON.parse contents, :symbolize_names => true
53
+ query = load_data
59
54
 
60
55
  # transform the data if needed
61
56
  query.each do |h|
62
57
  return h if h[:id] == input[:id]
63
58
  end
64
59
 
65
- {}
60
+ raise Exception.new 'no object found'
66
61
  end
67
62
 
68
63
  def remove input
69
- # open the file
70
- contents = File.read @filename
71
-
72
64
  data = []
73
65
  # parse the json list
74
- query = JSON.parse contents, :symbolize_names => true
66
+ query = load_data
75
67
 
76
68
  # transform the data if needed
77
69
  query.each do |h|
@@ -80,15 +72,28 @@ class FsPlug
80
72
  end
81
73
  end
82
74
 
83
- # save the data back to FS
84
- json_data = JSON.pretty_generate data
85
- File.open(@filename, 'w') {|f| f.write(json_data) }
75
+ save_data data
86
76
 
87
77
  # return true on success
88
78
  true
89
79
  end
90
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
91
95
  end
92
96
 
93
97
 
94
98
 
99
+
@@ -1,5 +1,10 @@
1
1
  require 'moped'
2
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
+
3
8
  class MongoPlug
4
9
  def initialize collection
5
10
  @collection = collection
@@ -0,0 +1,94 @@
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
+
@@ -1,3 +1,3 @@
1
1
  module Obvious
2
- VERSION = "0.0.7"
2
+ VERSION = "0.0.8"
3
3
  end
data/lib/obvious.rb CHANGED
@@ -1,3 +1,4 @@
1
1
  require 'obvious/version'
2
2
  require 'obvious/contract'
3
+ require 'obvious/entity'
3
4
  require_relative 'generators/application_generator'
data/obvious.gemspec CHANGED
@@ -16,4 +16,6 @@ Gem::Specification.new do |gem|
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
17
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
18
  gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency "rspec"
19
21
  end
Binary file
@@ -0,0 +1,63 @@
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
@@ -0,0 +1,75 @@
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
@@ -0,0 +1,34 @@
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
@@ -0,0 +1,3 @@
1
+ RSpec.configure do |c|
2
+ c.mock_with :rspec
3
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: obvious
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 0.0.8
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,8 +9,19 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-01-19 00:00:00.000000000 Z
13
- dependencies: []
12
+ date: 2013-03-30 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &70341046505020 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *70341046505020
14
25
  description: A set of tools to build apps using the Obvious Architecture
15
26
  email:
16
27
  - brianknapp@gmail.com
@@ -30,12 +41,19 @@ files:
30
41
  - lib/generators/helpers/application.rb
31
42
  - lib/obvious.rb
32
43
  - lib/obvious/contract.rb
44
+ - lib/obvious/entity.rb
33
45
  - lib/obvious/files/Rakefile
34
46
  - lib/obvious/files/external/fs_plug.rb
35
47
  - lib/obvious/files/external/mongo_plug.rb
36
48
  - lib/obvious/files/external/mysql_plug.rb
49
+ - lib/obvious/files/external/s3_plug.rb
37
50
  - lib/obvious/version.rb
38
51
  - obvious.gemspec
52
+ - spec/.spec_helper.rb.swp
53
+ - spec/contract_spec.rb
54
+ - spec/entity_spec.rb
55
+ - spec/generators/descriptor_spec.rb
56
+ - spec/spec_helper.rb
39
57
  homepage: http://obvious.retromocha.com/
40
58
  licenses: []
41
59
  post_install_message:
@@ -60,5 +78,10 @@ rubygems_version: 1.8.17
60
78
  signing_key:
61
79
  specification_version: 3
62
80
  summary: Isn't it Obvious?
63
- test_files: []
81
+ test_files:
82
+ - spec/.spec_helper.rb.swp
83
+ - spec/contract_spec.rb
84
+ - spec/entity_spec.rb
85
+ - spec/generators/descriptor_spec.rb
86
+ - spec/spec_helper.rb
64
87
  has_rdoc: