obvious 0.0.7 → 0.0.8

Sign up to get free protection for your applications and to get access to all the features.
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: