obvious 0.0.2 → 0.0.3

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/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: