obvious 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # Obvious
2
2
 
3
- TODO: Write a gem description
3
+ Obvious is an architecture framework. The goal is to provide architectural structure for a highly testable system that is
4
+ obvious to understand and where both the front end UI and back end infrastructure are treated as implementation details
5
+ independent of the app logic itself.
6
+
7
+ You can get a full explanation of Obvious at http://obvious.retromocha.com
4
8
 
5
9
  ## Installation
6
10
 
@@ -18,7 +22,19 @@ Or install it yourself as:
18
22
 
19
23
  ## Usage
20
24
 
21
- TODO: Write usage instructions here
25
+ Obvious is designed to be used in two ways - as a gem you require in your Obvious projects and also as an Obvious project
26
+ generation tool.
27
+
28
+ The project generation tool is run by executing...
29
+
30
+ $ obvious generate
31
+
32
+ in a directory containing a decriptors directory. You can read more about descriptors on the Obvious page or see an example
33
+ in the Obvious Status example app: https://github.com/RetroMocha/obvious_status.
34
+
35
+ Currently the footprint of the Obvious library is quite small. The most important things defined so far are the Contract class
36
+ and the Hash.has_shape? method. The rest of what makes an Obvious app interesting is the structure itself, not the libraries Obvious
37
+ provides.
22
38
 
23
39
  ## Contributing
24
40
 
data/bin/obvious CHANGED
@@ -2,6 +2,293 @@
2
2
 
3
3
  require 'obvious'
4
4
 
5
+ require 'yaml'
6
+
7
+ module Obvious
8
+ # Your code goes here...
9
+ def self.generate
10
+ puts 'generate the codes!a'
11
+
12
+
13
+
14
+ #`rm -rf app`
15
+
16
+ app_dir = 'app'
17
+
18
+ counter = 1
19
+ while File.directory? app_dir
20
+ app_dir = "app_#{counter}"
21
+ counter += 1
22
+ end
23
+
24
+ puts "Generating application at: #{app_dir}"
25
+
26
+ dirs = ['/', '/actions', '/contracts', '/entities',
27
+ '/spec', '/spec/actions', '/spec/contracts', '/spec/entities',
28
+ '/spec/doubles']
29
+
30
+ dirs.each do |dir|
31
+ Dir.mkdir app_dir + dir
32
+ end
33
+
34
+
35
+ target_path = File.realpath Dir.pwd
36
+ spec = Gem::Specification.find_by_name("obvious")
37
+ gem_root = spec.gem_dir
38
+ gem_lib = gem_root + "/lib"
39
+
40
+ #`cp #{gem_lib}/obvious/files/contract.rb #{target_path}/app/contracts/contract.rb`
41
+ `cp #{gem_lib}/obvious/files/Rakefile #{target_path}/Rakefile`
42
+ entities = Hash.new
43
+ jacks = Hash.new
44
+
45
+ files = Dir['descriptors/*.yml']
46
+
47
+ files.each do |file|
48
+ action = YAML.load_file file
49
+ code = ''
50
+ #puts action.inspect
51
+
52
+ local_jacks = Hash.new
53
+ local_entities = Hash.new
54
+
55
+ action['Code'].each do |entry|
56
+ code << " \# #{entry['c']}\n"
57
+ code << " \# use: #{entry['requires']}\n" if entry['requires']
58
+ code << " \n"
59
+
60
+ if entry['requires']
61
+ requires = entry['requires'].split(',')
62
+ requires.each do |req|
63
+ req.strip!
64
+ info = req.split '.'
65
+
66
+ if info[0].index 'Jack'
67
+ unless jacks[info[0]]
68
+ jacks[info[0]] = []
69
+ end
70
+
71
+ unless local_jacks[info[0]]
72
+ local_jacks[info[0]] = []
73
+ end
74
+
75
+ jacks[info[0]] << info[1]
76
+ local_jacks[info[0]] << info[1]
77
+ else
78
+ unless entities[info[0]]
79
+ entities[info[0]] = []
80
+ end
81
+
82
+ unless local_entities[info[0]]
83
+ local_entities[info[0]] = []
84
+ end
85
+
86
+ entities[info[0]] << info[1]
87
+ local_entities[info[0]] << info[1]
88
+ end
89
+
90
+ end
91
+ end
92
+
93
+ end
94
+
95
+
96
+ jack_inputs = ''
97
+ jack_assignments = ''
98
+
99
+ local_jacks.each do |k, v|
100
+ name = k.chomp('Jack').downcase
101
+ jack_inputs << "#{name}_jack, "
102
+ jack_assignments << " @#{name}_jack = #{name}_jack\n"
103
+ end
104
+
105
+ jack_inputs.chomp! ', '
106
+
107
+ entity_requires = ''
108
+
109
+ local_entities.each do |k, v|
110
+ name = k.downcase
111
+ entity_requires << "require_relative '../entities/#{name}'\n"
112
+ end
113
+
114
+
115
+ output = <<FIN
116
+ #{entity_requires}
117
+ class #{action['Action']}
118
+
119
+ def initialize #{jack_inputs}
120
+ #{jack_assignments} end
121
+
122
+ def do input
123
+ #{code} end
124
+
125
+ end
126
+ FIN
127
+ snake_name = action['Action'].gsub(/(.)([A-Z])/,'\1_\2').downcase
128
+
129
+ filename = "#{app_dir}/actions/#{snake_name}.rb"
130
+ File.open(filename, 'w') {|f| f.write(output) }
131
+
132
+ #puts output
133
+
134
+ output = <<FIN
135
+ require_relative '../../actions/#{snake_name}'
136
+
137
+ describe #{action['Action']} do
138
+
139
+ it '#{action['Description']}'
140
+
141
+ it 'should raise an error with invalid input'
142
+
143
+ end
144
+
145
+
146
+ FIN
147
+
148
+ filename = "#{app_dir}/spec/actions/#{snake_name}_spec.rb"
149
+ File.open(filename, 'w') {|f| f.write(output) }
150
+
151
+ #puts output
152
+ end
153
+
154
+
155
+ #filter out duplicate methods
156
+
157
+ entities.each do |k, v|
158
+ v.uniq!
159
+ end
160
+
161
+ jacks.each do |k,v|
162
+ v.uniq!
163
+ end
164
+
165
+ #puts entities.inspect
166
+ #puts jacks.inspect
167
+
168
+ entities.each do |k, v|
169
+ name = k
170
+ method_specs = ''
171
+ method_definitions = ''
172
+
173
+ v.each do |method|
174
+ method_definitions << "
175
+ def #{method} input
176
+ nil
177
+ end
178
+ "
179
+
180
+ method_specs << "
181
+ describe '.#{method}' do
182
+ it 'should #{method} with valid input'
183
+
184
+ it 'should raise an error with invalid input'
185
+
186
+ end
187
+ "
188
+
189
+ end
190
+
191
+ output = <<FIN
192
+ class #{name}
193
+ #{method_definitions}
194
+ end
195
+ FIN
196
+ snake_name = name.gsub(/(.)([A-Z])/,'\1_\2').downcase
197
+
198
+ filename = "#{app_dir}/entities/#{snake_name}.rb"
199
+ File.open(filename, 'w') {|f| f.write(output) }
200
+
201
+ output = <<FIN
202
+ require_relative '../../entities/#{snake_name}'
203
+
204
+ describe #{name} do
205
+ #{method_specs}
206
+ end
207
+
208
+
209
+ FIN
210
+ filename = "#{app_dir}/spec/entities/#{snake_name}_spec.rb"
211
+ File.open(filename, 'w') {|f| f.write(output) }
212
+
213
+
214
+ #puts output
215
+ end
216
+
217
+
218
+
219
+ jacks.each do |k, v|
220
+
221
+ name = k.chomp('Jack').downcase
222
+
223
+ method_specs = ''
224
+ method_definitions = ''
225
+
226
+ v.each do |method|
227
+
228
+ method_definitions << "
229
+ def #{method}_contract input
230
+ input_shape = {}
231
+ output_shape = {}
232
+ call_method :#{method}_alias, input, input_shape, output_shape
233
+ end
234
+ "
235
+
236
+ method_specs << "
237
+ describe '.#{method}_contract' do
238
+ it 'should #{method} data with valid input'
239
+
240
+ it 'should raise an error with invalid input'
241
+
242
+ it 'should raise an error with invalid output'
243
+
244
+ end
245
+ "
246
+
247
+ end
248
+
249
+
250
+ output = <<FIN
251
+ require 'obvious'
252
+
253
+ class #{k}Contract < Contract
254
+ contracts :#{v.join(', :')}
255
+ #{method_definitions}
256
+ end
257
+ FIN
258
+
259
+ snake_name = name.gsub(/(.)([A-Z])/,'\1_\2').downcase
260
+
261
+ filename = "#{app_dir}/contracts/#{snake_name}_jack_contract.rb"
262
+ File.open(filename, 'w') {|f| f.write(output) }
263
+
264
+ #puts output
265
+
266
+ output = <<FIN
267
+ require_relative '../../contracts/#{snake_name}_jack_contract'
268
+
269
+ describe #{k}Contract do
270
+ #{method_specs}
271
+ end
272
+
273
+ FIN
274
+
275
+ filename = "#{app_dir}/spec/contracts/#{snake_name}_jack_contract_spec.rb"
276
+ File.open(filename, 'w') {|f| f.write(output) }
277
+
278
+ #puts output
279
+ end
280
+
281
+
282
+
283
+
284
+ end
285
+ end
286
+
287
+
288
+
5
289
  if ARGV[0] == 'generate'
6
290
  Obvious::generate
7
291
  end
292
+
293
+
294
+
@@ -1,24 +1,42 @@
1
1
  class Contract
2
2
  @@disable_override = false
3
3
 
4
- # This method needs to exist because the method_added bit looks for it.
5
- # It intentionally returns an empty array
6
- def self.contracts
4
+ # Provides a default empty array for method_added
5
+ # Overriden by self.contracts
6
+ def self.contract_list
7
7
  []
8
8
  end
9
9
 
10
- # This method will move methods defined in self.contracts into new methods.
11
- # Each entry in self.contracts will cause the method with the same name to
12
- # become method_name_alias and for the original method to point to
10
+ # Public: Sets the contracts
11
+ #
12
+ # contracts - a list of contracts, as String or as Symbols.
13
+ #
14
+ # Examples
15
+ #
16
+ # class FooJackContract < Contract
17
+ # contracts :save, :get, :list
18
+ #
19
+ # end
20
+ #
21
+ # Returns Nothing.
22
+ def self.contracts *contracts
23
+ singleton_class.send :define_method, :contract_list do
24
+ contracts
25
+ end
26
+ end
27
+
28
+ # This method will move methods defined in @contracts into new methods.
29
+ # Each entry in @contracts will cause the method with the same name to
30
+ # become method_name_alias and for the original method to point to
13
31
  # method_name_contract.
14
32
  def self.method_added name
15
33
  unless @@disable_override
16
- self.contracts.each do |method|
34
+ self.contract_list.each do |method|
17
35
  if name == method.to_sym
18
36
  method_alias = "#{method}_alias".to_sym
19
37
  method_contract = "#{method}_contract".to_sym
20
38
 
21
- @@disable_override = true # to stop the new build method
39
+ @@disable_override = true # to stop the new build method
22
40
  self.send :alias_method, method_alias, name
23
41
  self.send :remove_method, name
24
42
  self.send :alias_method, name, method_contract
@@ -26,24 +44,24 @@ class Contract
26
44
  @@disable_override = false
27
45
  else
28
46
  # puts self.inspect
29
- # puts "defining other method #{name}"
47
+ # puts "defining other method #{name}"
30
48
  end
31
49
  end
32
50
  end
33
51
  end
34
52
 
35
53
  # This method is used as a shorthand to mak the contract method calling pattern more DRY
36
- # It starts by checking if you are sending in input and if so will check the input shape for
54
+ # It starts by checking if you are sending in input and if so will check the input shape for
37
55
  # errors. If no errors are found it calls the method via the passed in symbol(method).
38
56
  #
39
57
  # Output checking is more complicated because of the types of output we check for. Nil is
40
- # never valid output. If we pass in the output shape of true, that means we are looking for
58
+ # never valid output. If we pass in the output shape of true, that means we are looking for
41
59
  # result to be the object True. If the output shape is an array, that is actually a shorthand
42
60
  # for telling our output check to look at the output as an array and compare it to the shape
43
61
  # stored in output_shape[0]. If we pass in the symbol :true_false it means we are looking for
44
62
  # the result to be either true or false. The default case will just check if result has the shape
45
63
  # of the output_shape.
46
- def call_method method, input, input_shape, output_shape
64
+ def call_method method, input, input_shape, output_shape
47
65
  if input != nil && input_shape != nil
48
66
  unless input.has_shape? input_shape
49
67
  raise ContractInputError, 'incorrect input data format'
@@ -70,23 +88,23 @@ class Contract
70
88
  end
71
89
 
72
90
  # we want to check the shape of each item in the result array
73
- if output_shape.class == Array
91
+ if output_shape.class == Array
74
92
  if result.class == Array
75
93
  inner_shape = output_shape[0]
76
94
  result.each do |item|
77
95
  unless item.has_shape? inner_shape
78
96
  raise ContractOutputError, 'incorrect output data format'
79
97
  end
80
- end
81
-
82
- return result
83
- end
98
+ end
99
+
100
+ return result
101
+ end
84
102
  raise ContractOutputError, 'incorrect output data format'
85
- end
103
+ end
86
104
 
87
- # we want result to be true or false
105
+ # we want result to be true or false
88
106
  if output_shape == :true_false
89
- unless result == true || result == false
107
+ unless result == true || result == false
90
108
  raise ContractOutputError, 'incorrect output data format'
91
109
  end
92
110
 
@@ -122,8 +140,10 @@ class Hash
122
140
  return shape.empty?
123
141
  end
124
142
 
125
- all? do |k, v|
126
- Hash === v ? v.has_shape?(shape[k]) : shape[k] === v
143
+ shape.all? do |k, v|
144
+ # hash_value
145
+ hv = self[k]
146
+ Hash === hv ? hv.has_shape?(v) : v === hv
127
147
  end
128
148
  end
129
149
  end
@@ -1,3 +1,3 @@
1
1
  module Obvious
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/lib/obvious.rb CHANGED
@@ -1,285 +1,3 @@
1
1
  require 'obvious/version'
2
2
  require 'obvious/contract'
3
- require 'yaml'
4
3
 
5
- module Obvious
6
- # Your code goes here...
7
- def self.generate
8
- puts 'generate the codes!a'
9
-
10
-
11
-
12
- #`rm -rf app`
13
-
14
- app_dir = 'app'
15
-
16
- counter = 1
17
- while File.directory? app_dir
18
- app_dir = "app_#{counter}"
19
- counter += 1
20
- end
21
-
22
- puts "Generating application at: #{app_dir}"
23
-
24
- dirs = ['/', '/actions', '/contracts', '/entities',
25
- '/spec', '/spec/actions', '/spec/contracts', '/spec/entities',
26
- '/spec/doubles']
27
-
28
- dirs.each do |dir|
29
- Dir.mkdir app_dir + dir
30
- end
31
-
32
-
33
- target_path = File.realpath Dir.pwd
34
- spec = Gem::Specification.find_by_name("obvious")
35
- gem_root = spec.gem_dir
36
- gem_lib = gem_root + "/lib"
37
-
38
- #`cp #{gem_lib}/obvious/files/contract.rb #{target_path}/app/contracts/contract.rb`
39
- `cp #{gem_lib}/obvious/files/Rakefile #{target_path}/Rakefile`
40
- entities = Hash.new
41
- jacks = Hash.new
42
-
43
- files = Dir['descriptors/*.yml']
44
-
45
- files.each do |file|
46
- action = YAML.load_file file
47
- code = ''
48
- #puts action.inspect
49
-
50
- local_jacks = Hash.new
51
- local_entities = Hash.new
52
-
53
- action['Code'].each do |entry|
54
- code << " \# #{entry['c']}\n"
55
- code << " \# use: #{entry['requires']}\n" if entry['requires']
56
- code << " \n"
57
-
58
- if entry['requires']
59
- requires = entry['requires'].split(',')
60
- requires.each do |req|
61
- req.strip!
62
- info = req.split '.'
63
-
64
- if info[0].index 'Jack'
65
- unless jacks[info[0]]
66
- jacks[info[0]] = []
67
- end
68
-
69
- unless local_jacks[info[0]]
70
- local_jacks[info[0]] = []
71
- end
72
-
73
- jacks[info[0]] << info[1]
74
- local_jacks[info[0]] << info[1]
75
- else
76
- unless entities[info[0]]
77
- entities[info[0]] = []
78
- end
79
-
80
- unless local_entities[info[0]]
81
- local_entities[info[0]] = []
82
- end
83
-
84
- entities[info[0]] << info[1]
85
- local_entities[info[0]] << info[1]
86
- end
87
-
88
- end
89
- end
90
-
91
- end
92
-
93
-
94
- jack_inputs = ''
95
- jack_assignments = ''
96
-
97
- local_jacks.each do |k, v|
98
- name = k.chomp('Jack').downcase
99
- jack_inputs << "#{name}_jack, "
100
- jack_assignments << " @#{name}_jack = #{name}_jack\n"
101
- end
102
-
103
- jack_inputs.chomp! ', '
104
-
105
- entity_requires = ''
106
-
107
- local_entities.each do |k, v|
108
- name = k.downcase
109
- entity_requires << "require_relative '../entities/#{name}'\n"
110
- end
111
-
112
-
113
- output = <<FIN
114
- #{entity_requires}
115
- class #{action['Action']}
116
-
117
- def initialize #{jack_inputs}
118
- #{jack_assignments} end
119
-
120
- def do input
121
- #{code} end
122
-
123
- end
124
- FIN
125
- snake_name = action['Action'].gsub(/(.)([A-Z])/,'\1_\2').downcase
126
-
127
- filename = "#{app_dir}/actions/#{snake_name}.rb"
128
- File.open(filename, 'w') {|f| f.write(output) }
129
-
130
- #puts output
131
-
132
- output = <<FIN
133
- require_relative '../../actions/#{snake_name}'
134
-
135
- describe #{action['Action']} do
136
-
137
- it '#{action['Description']}'
138
-
139
- it 'should raise an error with invalid input'
140
-
141
- end
142
-
143
-
144
- FIN
145
-
146
- filename = "#{app_dir}/spec/actions/#{snake_name}_spec.rb"
147
- File.open(filename, 'w') {|f| f.write(output) }
148
-
149
- #puts output
150
- end
151
-
152
-
153
- #filter out duplicate methods
154
-
155
- entities.each do |k, v|
156
- v.uniq!
157
- end
158
-
159
- jacks.each do |k,v|
160
- v.uniq!
161
- end
162
-
163
- #puts entities.inspect
164
- #puts jacks.inspect
165
-
166
- entities.each do |k, v|
167
- name = k
168
- method_specs = ''
169
- method_definitions = ''
170
-
171
- v.each do |method|
172
- method_definitions << "
173
- def #{method} input
174
- nil
175
- end
176
- "
177
-
178
- method_specs << "
179
- describe '.#{method}' do
180
- it 'should #{method} with valid input'
181
-
182
- it 'should raise an error with invalid input'
183
-
184
- end
185
- "
186
-
187
- end
188
-
189
- output = <<FIN
190
- class #{name}
191
- #{method_definitions}
192
- end
193
- FIN
194
- snake_name = name.gsub(/(.)([A-Z])/,'\1_\2').downcase
195
-
196
- filename = "#{app_dir}/entities/#{snake_name}.rb"
197
- File.open(filename, 'w') {|f| f.write(output) }
198
-
199
- output = <<FIN
200
- require_relative '../../entities/#{snake_name}'
201
-
202
- describe #{name} do
203
- #{method_specs}
204
- end
205
-
206
-
207
- FIN
208
- filename = "#{app_dir}/spec/entities/#{snake_name}_spec.rb"
209
- File.open(filename, 'w') {|f| f.write(output) }
210
-
211
-
212
- #puts output
213
- end
214
-
215
-
216
-
217
- jacks.each do |k, v|
218
-
219
- name = k.chomp('Jack').downcase
220
-
221
- method_specs = ''
222
- method_definitions = ''
223
-
224
- v.each do |method|
225
-
226
- method_definitions << "
227
- def #{method}_contract input
228
- input_shape = {}
229
- output_shape = {}
230
- call_method :#{method}_alias, input, input_shape, output_shape
231
- end
232
- "
233
-
234
- method_specs << "
235
- describe '.#{method}_contract' do
236
- it 'should #{method} data with valid input'
237
-
238
- it 'should raise an error with invalid input'
239
-
240
- it 'should raise an error with invalid output'
241
-
242
- end
243
- "
244
-
245
- end
246
-
247
-
248
- output = <<FIN
249
- require 'obvious'
250
-
251
- class #{k}Contract < Contract
252
- def self.contracts
253
- #{v.to_s}
254
- end
255
- #{method_definitions}
256
- end
257
- FIN
258
-
259
- snake_name = name.gsub(/(.)([A-Z])/,'\1_\2').downcase
260
-
261
- filename = "#{app_dir}/contracts/#{snake_name}_jack_contract.rb"
262
- File.open(filename, 'w') {|f| f.write(output) }
263
-
264
- #puts output
265
-
266
- output = <<FIN
267
- require_relative '../../contracts/#{snake_name}_jack_contract'
268
-
269
- describe #{k}Contract do
270
- #{method_specs}
271
- end
272
-
273
- FIN
274
-
275
- filename = "#{app_dir}/spec/contracts/#{snake_name}_jack_spec.rb"
276
- File.open(filename, 'w') {|f| f.write(output) }
277
-
278
- #puts output
279
- end
280
-
281
-
282
-
283
-
284
- end
285
- 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.2
4
+ version: 0.0.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-23 00:00:00.000000000 Z
12
+ date: 2013-01-10 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: A set of tools to build apps using the Obvious Architecture
15
15
  email:
@@ -50,9 +50,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
50
50
  version: '0'
51
51
  requirements: []
52
52
  rubyforge_project:
53
- rubygems_version: 1.8.17
53
+ rubygems_version: 1.8.11
54
54
  signing_key:
55
55
  specification_version: 3
56
56
  summary: Isn't it Obvious?
57
57
  test_files: []
58
- has_rdoc: