wukong 3.0.0.pre2 → 3.0.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (146) hide show
  1. data/Gemfile +13 -0
  2. data/README.md +182 -6
  3. data/bin/wu-local +13 -5
  4. data/bin/wu-server +1 -1
  5. data/examples/Gemfile +2 -1
  6. data/examples/basic/string_reverser.rb +23 -0
  7. data/examples/{tiny_count.rb → basic/tiny_count.rb} +0 -0
  8. data/examples/{word_count → basic/word_count}/accumulator.rb +0 -0
  9. data/examples/{word_count → basic/word_count}/tokenizer.rb +0 -0
  10. data/examples/{word_count → basic/word_count}/word_count.rb +0 -0
  11. data/examples/deploy_pack/Gemfile +7 -0
  12. data/examples/deploy_pack/README.md +6 -0
  13. data/examples/{text/latinize_text.rb → deploy_pack/a/b/c/.gitkeep} +0 -0
  14. data/examples/deploy_pack/app/processors/string_reverser.rb +5 -0
  15. data/examples/deploy_pack/config/environment.rb +1 -0
  16. data/examples/{dataflow → dsl/dataflow}/fibonacci_series.rb +0 -0
  17. data/examples/dsl/dataflow/scraper_macro_flow.rb +28 -0
  18. data/examples/{dataflow → dsl/dataflow}/simple.rb +0 -0
  19. data/examples/{dataflow → dsl/dataflow}/telegram.rb +0 -0
  20. data/examples/{workflow → dsl/workflow}/cherry_pie.dot +0 -0
  21. data/examples/{workflow → dsl/workflow}/cherry_pie.md +0 -0
  22. data/examples/{workflow → dsl/workflow}/cherry_pie.png +0 -0
  23. data/examples/{workflow → dsl/workflow}/cherry_pie.rb +0 -0
  24. data/examples/empty/.gitkeep +0 -0
  25. data/examples/graph/implied_geolocation/README.md +63 -0
  26. data/examples/graph/{minimum_spanning_tree.rb → minimum_spanning_tree/airfares_graphviz.rb} +0 -0
  27. data/examples/munging/airline_flights/indexable.rb +75 -0
  28. data/examples/munging/airline_flights/indexable_spec.rb +90 -0
  29. data/examples/munging/geo/geonames_models.rb +29 -0
  30. data/examples/munging/wikipedia/dbpedia/dbpedia_common.rb +1 -0
  31. data/examples/munging/wikipedia/dbpedia/extract_links-cruft.rb +66 -0
  32. data/examples/munging/wikipedia/dbpedia/extract_links.rb +213 -146
  33. data/examples/rake_helper.rb +12 -0
  34. data/examples/ruby_project/Gemfile +7 -0
  35. data/examples/ruby_project/README.md +6 -0
  36. data/examples/ruby_project/a/b/c/.gitkeep +0 -0
  37. data/examples/serverlogs/geo_ip_mapping/munge_geolite.rb +82 -0
  38. data/examples/serverlogs/models/logline.rb +102 -0
  39. data/examples/{dataflow/parse_apache_logs.rb → serverlogs/parser/apache_parser_widget.rb} +0 -0
  40. data/examples/serverlogs/visit_paths/common.rb +4 -0
  41. data/examples/serverlogs/visit_paths/page_counts.pig +48 -0
  42. data/examples/serverlogs/visit_paths/serverlogs-01-parse-script.rb +11 -0
  43. data/examples/serverlogs/visit_paths/serverlogs-02-histograms-full.rb +31 -0
  44. data/examples/serverlogs/visit_paths/serverlogs-02-histograms-mapper.rb +12 -0
  45. data/examples/serverlogs/visit_paths/serverlogs-03-breadcrumbs-full.rb +67 -0
  46. data/examples/serverlogs/visit_paths/serverlogs-04-page_page_edges-full.rb +38 -0
  47. data/examples/text/{pig_latin.rb → pig_latin/pig_latinizer.rb} +0 -0
  48. data/examples/{dataflow/pig_latinizer.rb → text/pig_latin/pig_latinizer_widget.rb} +0 -0
  49. data/lib/hanuman/graph.rb +6 -1
  50. data/lib/wu/geo.rb +4 -0
  51. data/lib/wu/geo/geo_grids.numbers +0 -0
  52. data/lib/wu/geo/geolocated.rb +331 -0
  53. data/lib/wu/geo/quadtile.rb +69 -0
  54. data/{examples → lib/wu}/graph/union_find.rb +0 -0
  55. data/lib/wu/model/reconcilable.rb +63 -0
  56. data/{examples/munging/wikipedia/utils/munging_utils.rb → lib/wu/munging.rb} +7 -4
  57. data/lib/wu/social/models/twitter.rb +31 -0
  58. data/{examples/models/wikipedia.rb → lib/wu/wikipedia/models.rb} +0 -0
  59. data/lib/wukong.rb +9 -4
  60. data/lib/wukong/boot.rb +10 -1
  61. data/lib/wukong/driver.rb +65 -71
  62. data/lib/wukong/logger.rb +93 -0
  63. data/lib/wukong/processor.rb +38 -29
  64. data/lib/wukong/runner.rb +144 -0
  65. data/lib/wukong/server.rb +119 -0
  66. data/lib/wukong/spec_helpers.rb +1 -0
  67. data/lib/wukong/spec_helpers/integration_driver.rb +22 -9
  68. data/lib/wukong/spec_helpers/integration_driver_matchers.rb +26 -4
  69. data/lib/wukong/spec_helpers/processor_helpers.rb +4 -10
  70. data/lib/wukong/spec_helpers/shared_examples.rb +12 -13
  71. data/lib/wukong/version.rb +1 -1
  72. data/lib/wukong/widget/processors.rb +13 -0
  73. data/lib/wukong/widget/serializers.rb +55 -65
  74. data/lib/wukong/widgets.rb +0 -2
  75. data/spec/hanuman/graph_spec.rb +14 -0
  76. data/spec/spec_helper.rb +4 -30
  77. data/spec/support/{wukong_test_helpers.rb → example_test_helpers.rb} +29 -2
  78. data/spec/support/integration_helper.rb +38 -0
  79. data/spec/support/model_test_helpers.rb +115 -0
  80. data/spec/wu/geo/geolocated_spec.rb +247 -0
  81. data/spec/wu/model/reconcilable_spec.rb +152 -0
  82. data/spec/wukong/widget/processors_spec.rb +0 -1
  83. data/spec/wukong/widget/serializers_spec.rb +88 -62
  84. data/spec/wukong/wu_local_spec.rb +125 -0
  85. data/wukong.gemspec +3 -16
  86. metadata +72 -266
  87. data/examples/dataflow/apache_log_line.rb +0 -100
  88. data/examples/jabberwocky.txt +0 -36
  89. data/examples/munging/Gemfile +0 -8
  90. data/examples/munging/airline_flights/airline.rb +0 -57
  91. data/examples/munging/airline_flights/airport.rb +0 -211
  92. data/examples/munging/airline_flights/flight.rb +0 -156
  93. data/examples/munging/airline_flights/models.rb +0 -4
  94. data/examples/munging/airline_flights/parse.rb +0 -26
  95. data/examples/munging/airline_flights/route.rb +0 -35
  96. data/examples/munging/airline_flights/timezone_fixup.rb +0 -62
  97. data/examples/munging/airports/40_wbans.txt +0 -40
  98. data/examples/munging/airports/filter_weather_reports.rb +0 -37
  99. data/examples/munging/airports/join.pig +0 -31
  100. data/examples/munging/airports/to_tsv.rb +0 -33
  101. data/examples/munging/airports/usa_wbans.pig +0 -19
  102. data/examples/munging/airports/usa_wbans.txt +0 -2157
  103. data/examples/munging/airports/wbans.pig +0 -19
  104. data/examples/munging/airports/wbans.txt +0 -2310
  105. data/examples/munging/rake_helper.rb +0 -62
  106. data/examples/munging/weather/.gitignore +0 -1
  107. data/examples/munging/weather/Gemfile +0 -4
  108. data/examples/munging/weather/Rakefile +0 -28
  109. data/examples/munging/weather/extract_ish.rb +0 -13
  110. data/examples/munging/weather/models/weather.rb +0 -119
  111. data/examples/munging/weather/utils/noaa_downloader.rb +0 -46
  112. data/examples/munging/wikipedia/README.md +0 -34
  113. data/examples/munging/wikipedia/Rakefile +0 -193
  114. data/examples/munging/wikipedia/n1_subuniverse/n1_nodes.pig +0 -18
  115. data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb +0 -21
  116. data/examples/munging/wikipedia/page_metadata/extract_page_metadata.rb.old +0 -27
  117. data/examples/munging/wikipedia/pagelinks/augment_pagelinks.pig +0 -29
  118. data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb +0 -14
  119. data/examples/munging/wikipedia/pagelinks/extract_pagelinks.rb.old +0 -25
  120. data/examples/munging/wikipedia/pagelinks/undirect_pagelinks.pig +0 -29
  121. data/examples/munging/wikipedia/pageviews/augment_pageviews.pig +0 -32
  122. data/examples/munging/wikipedia/pageviews/extract_pageviews.rb +0 -85
  123. data/examples/munging/wikipedia/pig_style_guide.md +0 -25
  124. data/examples/munging/wikipedia/redirects/redirects_page_metadata.pig +0 -19
  125. data/examples/munging/wikipedia/subuniverse/sub_articles.pig +0 -23
  126. data/examples/munging/wikipedia/subuniverse/sub_page_metadata.pig +0 -24
  127. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_from.pig +0 -22
  128. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_into.pig +0 -22
  129. data/examples/munging/wikipedia/subuniverse/sub_pagelinks_within.pig +0 -26
  130. data/examples/munging/wikipedia/subuniverse/sub_pageviews.pig +0 -29
  131. data/examples/munging/wikipedia/subuniverse/sub_undirected_pagelinks_within.pig +0 -24
  132. data/examples/munging/wikipedia/utils/get_namespaces.rb +0 -86
  133. data/examples/munging/wikipedia/utils/namespaces.json +0 -1
  134. data/examples/string_reverser.rb +0 -26
  135. data/examples/twitter/locations.rb +0 -29
  136. data/examples/twitter/models.rb +0 -24
  137. data/examples/twitter/pt1-fiddle.pig +0 -8
  138. data/examples/twitter/pt2-simple_parse.pig +0 -31
  139. data/examples/twitter/pt2-simple_parse.rb +0 -18
  140. data/examples/twitter/pt3-join_on_zips.pig +0 -39
  141. data/examples/twitter/pt4-strong_links.rb +0 -20
  142. data/examples/twitter/pt5-lnglat_and_strong_links.pig +0 -16
  143. data/examples/twitter/states.tsv +0 -50
  144. data/examples/workflow/package_gem.rb +0 -55
  145. data/lib/wukong/widget/sink.rb +0 -16
  146. data/lib/wukong/widget/source.rb +0 -14
@@ -1,8 +1,18 @@
1
1
  module Wukong
2
2
  class Processor
3
3
 
4
- # An empty parent class for all Serializers to subclass.
4
+ SerializerError = Class.new(StandardError)
5
+
5
6
  class Serializer < Processor
7
+ field :on_error, String, default: 'log'
8
+
9
+ def handle_error(record, err)
10
+ case on_error
11
+ when 'log' then log.warn "Bad record: #{record}"
12
+ when 'notify' then notify('error', record: record, error: err)
13
+ end
14
+ end
15
+
6
16
  end
7
17
 
8
18
  # A widget for serializing inputs to JSON.
@@ -15,19 +25,23 @@ module Wukong
15
25
  #
16
26
  # @see FromJson
17
27
  class ToJson < Serializer
28
+ field :pretty, :boolean, default: false
29
+
18
30
  # Yields the input `record` serialized as JSON.
19
31
  #
20
32
  # @param [Object] record
21
33
  # @yield [json] the serialized json output
22
34
  # @yieldparam [String] json
23
35
  def process(record)
24
- begin
25
- json = ::MultiJson.dump(record)
26
- rescue => e
27
- # FIXME -- should we log here or what?
28
- return
36
+ raise SerializerError.new("Cannot serialize: <nil>") if record.nil?
37
+ if record.respond_to?(:to_json) && !record.is_a?(Hash) # We only want to invoke to_json if it has been explicitly defined
38
+ json = record.to_json(pretty: pretty)
39
+ else
40
+ json = MultiJson.dump(record.try(:to_wire) || record, pretty: pretty)
29
41
  end
30
42
  yield json
43
+ rescue => e
44
+ handle_error(record, e)
31
45
  end
32
46
  register
33
47
  end
@@ -47,14 +61,15 @@ module Wukong
47
61
  # @param [String] json
48
62
  # @yield [obj] the deserialized object
49
63
  # @yieldparam [Object] obj
50
- def process(json)
51
- begin
52
- obj = ::MultiJson.load(json)
53
- rescue => e
54
- # FIXME -- should we log here or what?
55
- return
64
+ def process(record)
65
+ if record.respond_to?(:from_json)
66
+ obj = record.from_json
67
+ else
68
+ obj = MultiJson.load(record)
56
69
  end
57
70
  yield obj
71
+ rescue => e
72
+ handle_error(record, e)
58
73
  end
59
74
  register
60
75
  end
@@ -75,13 +90,16 @@ module Wukong
75
90
  # @yield [tsv] the serialized TSV output
76
91
  # @yieldparam [String] tsv
77
92
  def process(record)
78
- begin
79
- tsv = record.map(&:to_s).join("\t")
80
- rescue => e
81
- # FIXME -- should we log here or what?
82
- return
93
+ if record.respond_to?(:to_tsv)
94
+ tsv = record.to_tsv
95
+ else
96
+ wire_format = record.try(:to_wire) || record
97
+ raise SerializerError.new("Record must be in Array format to be serialized as TSV") unless wire_format.respond_to?(:map)
98
+ tsv = wire_format.map(&:to_s).join("\t")
83
99
  end
84
100
  yield tsv
101
+ rescue => e
102
+ handle_error(record, e)
85
103
  end
86
104
  register
87
105
  end
@@ -101,14 +119,15 @@ module Wukong
101
119
  # @param [String] tsv
102
120
  # @yield [obj] the deserialized object
103
121
  # @yieldparam [Object] obj
104
- def process(tsv)
105
- begin
106
- record = tsv.split(/\t/)
107
- rescue => e
108
- # FIXME -- should we log here or what?
109
- return
122
+ def process(record)
123
+ if record.respond_to?(:from_tsv)
124
+ obj = record.from_tsv
125
+ else
126
+ obj = record.split(/\t/)
110
127
  end
111
- yield record
128
+ yield obj
129
+ rescue => e
130
+ handle_error(record, e)
112
131
  end
113
132
  register
114
133
  end
@@ -236,52 +255,23 @@ module Wukong
236
255
  # @param [Array<Object>]
237
256
  # @yield [inspected]
238
257
  # @yieldparam [String] inspected
239
- def process(*args)
240
- yield args.size == 1 ? args.first.inspect : args.inspect
258
+ def process(record)
259
+ yield record.inspect
241
260
  end
242
261
  register
243
262
  end
263
+
264
+ class Recordize < Serializer
265
+ field :model, Whatever
244
266
 
245
- # A widget for pretty printing input records.
246
- #
247
- # @example Pretty printing JSON on the command-line
248
- #
249
- # $ cat input
250
- # {"id": 1, "word": "apple" }
251
- # $ cat input | wu-local pretty
252
- # {
253
- # "id":2,
254
- # "parent_id":3
255
- # }
256
- class Pretty < Serializer
257
- # Pretty print `record` if we can.
258
- #
259
- # @param [Object] record
260
- # @yield [pretty]
261
- # @yieldparam [String] pretty the pretty-printed record
262
- def process record
263
- if record.is_a?(String) && record =~ /^\s*\{/
264
- yield pretty_json(record)
265
- else
266
- yield record.to_s
267
- end
268
- end
269
-
270
- # Attempt to pretty-print the given `json`, returning the
271
- # original on an error.
272
- #
273
- # @param [String] json ugly JSON
274
- # @return [String] prettier JSON
275
- def pretty_json json
276
- begin
277
- MultiJson.dump(MultiJson.load(json), :pretty => true)
278
- rescue => e
279
- json
280
- end
267
+ def process(record)
268
+ wire_format = record.try(:to_wire) || record
269
+ raise SerializerError.new("Record must be in hash format to be recordized") unless wire_format.is_a?(Hash)
270
+ yield model.receive(wire_format)
271
+ rescue => e
272
+ handle_error(record, e)
281
273
  end
282
-
283
274
  register
284
- end
285
-
275
+ end
286
276
  end
287
277
  end
@@ -1,5 +1,3 @@
1
- require 'wukong/widget/source'
2
- require 'wukong/widget/sink'
3
1
  require 'wukong/widget/processors'
4
2
  require 'wukong/widget/reducers'
5
3
  require 'wukong/widget/serializers'
@@ -32,6 +32,20 @@ describe Hanuman::GraphBuilder, :hanuman => true do
32
32
  end
33
33
 
34
34
  context '#build' do
35
+ before do
36
+ Wukong.processor(:foo) do
37
+ field :bones, String
38
+ end
39
+ Wukong.dataflow(:bar) do
40
+ foo
41
+ end
42
+ end
43
+
44
+ let(:graph){ Wukong.registry.retrieve(:bar) }
45
+ it 'builds stages with specific options' do
46
+ built = graph.build(foo: { bones: 'lala' })
47
+ built.stages[:foo].bones.should eq('lala')
48
+ end
35
49
  end
36
50
 
37
51
  context '#serialize' do
data/spec/spec_helper.rb CHANGED
@@ -1,39 +1,13 @@
1
- require 'bundler' ; Bundler.setup(:default, :development, :test)
2
-
3
- if ENV['WUKONG_COV']
4
- require 'simplecov'
5
-
6
- SimpleCov.start do
7
- add_filter '/gorillib/'
8
- add_filter '/away/'
9
- add_group 'Hanuman', '/hanuman/'
10
- end
11
- end
12
-
13
1
  require 'wukong'
14
2
  require 'wukong/spec_helpers'
15
- require_relative './support/shared_examples_for_builders'
16
3
  require_relative './support/shared_examples_for_shortcuts'
4
+ require_relative './support/shared_examples_for_builders'
5
+ require_relative './support/integration_helper'
17
6
  require_relative './support/shared_context_for_reducers'
18
7
 
19
- # require 'gorillib/pathname'
20
- # require 'gorillib/type/extended'
21
- # require 'wukong/model/faker'
22
-
23
- # Pathname.register_path(:wukong_root, File.expand_path('..', File.dirname(__FILE__)))
24
- # Pathname.register_path(:examples, :wukong_root, 'examples')
25
- # Pathname.register_path(:tmp, :wukong_root, 'tmp')
26
- # Pathname.register_path(:data, :wukong_root, 'data')
27
- # Pathname.path_to(:tmp).mkpath
28
-
29
- # Dir[ Pathname.path_to('spec', 'support', '*.rb') ].each{|f| require f }
30
-
31
- # result = `dot -V 2>&1` rescue nil
32
- # GRAPHVIZ = ($?.exitstatus == 0) && (result =~ /dot - graphviz version/)
33
- # puts 'Some specs require graphviz to run -- brew/apt install graphviz, it is pretty awesome' unless GRAPHVIZ
34
-
35
8
  RSpec.configure do |config|
36
9
  config.mock_with :rspec
37
10
  include Wukong::SpecHelpers
38
- # config.treat_symbols_as_metadata_keys_with_true_values = true
11
+ include Wukong::Local::IntegrationHelper
12
+ config.treat_symbols_as_metadata_keys_with_true_values = true
39
13
  end
@@ -1,4 +1,31 @@
1
- require 'gorillib/utils/capture_output'
1
+ if ENV['WUKONG_COV']
2
+ require 'simplecov'
3
+ SimpleCov.start do
4
+ add_filter '/gorillib/'
5
+ add_filter '/away/'
6
+ add_group 'Hanuman', '/hanuman/'
7
+ end
8
+ end
9
+
10
+ require 'gorillib/pathname'
11
+ require 'gorillib/type/extended'
12
+ require 'wukong/model/faker'
13
+
14
+ Pathname.register_path(:wukong_root, File.expand_path('..', File.dirname(__FILE__)))
15
+ Pathname.register_path(:examples, :wukong_root, 'examples')
16
+ Pathname.register_path(:tmp, :wukong_root, 'tmp')
17
+ Pathname.register_path(:data, :wukong_root, 'data')
18
+ Pathname.path_to(:tmp).mkpath
19
+
20
+ Dir[ Pathname.path_to('spec', 'support', '*.rb') ].each{|f| require f }
21
+
22
+ result = `dot -V 2>&1` rescue nil
23
+ GRAPHVIZ = ($?.exitstatus == 0) && (result =~ /dot - graphviz version/)
24
+ puts 'Some specs require graphviz to run -- brew/apt install graphviz, it is pretty awesome' unless GRAPHVIZ
25
+
26
+ RSpec.configure do |config|
27
+ config.treat_symbols_as_metadata_keys_with_true_values = true
28
+ end
2
29
 
3
30
  shared_context 'wukong', :helpers => true do
4
31
 
@@ -21,6 +48,7 @@ shared_context 'wukong', :helpers => true do
21
48
  let(:test_dataflow) { Wukong.dataflow(:test_dataflow) }
22
49
  end
23
50
 
51
+ require 'gorillib/utils/capture_output'
24
52
 
25
53
  module WukongTestHelpers
26
54
 
@@ -65,5 +93,4 @@ RSpec::Core::DSL.module_eval do
65
93
  yield "#{basename}.dot" if block_given?
66
94
  end
67
95
  end
68
-
69
96
  end
@@ -0,0 +1,38 @@
1
+ module Wukong
2
+ module Local
3
+ module IntegrationHelper
4
+
5
+ def root
6
+ @root ||= Pathname.new(File.expand_path('../../..', __FILE__))
7
+ end
8
+
9
+ def lib_dir *args
10
+ root.join('lib', *args)
11
+ end
12
+
13
+ def bin_dir *args
14
+ root.join('bin', *args)
15
+ end
16
+
17
+ def examples_dir *args
18
+ root.join('examples', *args)
19
+ end
20
+
21
+ def integration_env
22
+ {
23
+ "PATH" => [bin_dir.to_s, ENV["PATH"]].compact.join(':'),
24
+ "RUBYLIB" => [lib_dir.to_s, ENV["RUBYLIB"]].compact.join(':')
25
+ }
26
+ end
27
+
28
+ def integration_cwd
29
+ root.to_s
30
+ end
31
+
32
+ def example_script *args
33
+ examples_dir(*args)
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,115 @@
1
+ require 'gorillib/utils/nuke_constants'
2
+ require 'gorillib/utils/capture_output'
3
+
4
+ module Gorillib ; module Test ; end ; end
5
+ module Meta ; module Gorillib ; module Test ; end ; end ; end
6
+
7
+ shared_context 'model', :model_spec do
8
+ include Gorillib::TestHelpers
9
+
10
+ after(:each){ Gorillib::Test.nuke_constants ; Meta::Gorillib::Test.nuke_constants }
11
+
12
+ let(:mock_val){ mock('mock value') }
13
+
14
+ let(:smurf_class) do
15
+ class Gorillib::Test::Smurf
16
+ include Gorillib::Model
17
+ field :name, String
18
+ field :smurfiness, Integer
19
+ field :weapon, Symbol
20
+ end
21
+ Gorillib::Test::Smurf
22
+ end
23
+ let(:papa_smurf ){ smurf_class.receive(:name => 'Papa Smurf', :smurfiness => 9, :weapon => 'staff') }
24
+ let(:smurfette ){ smurf_class.receive(:name => 'Smurfette', :smurfiness => 11, :weapon => 'charm') }
25
+
26
+ let(:smurf_collection_class) do
27
+ smurf_class
28
+ class Gorillib::Test::SmurfCollection < Gorillib::ModelCollection
29
+ include Gorillib::Collection::ItemsBelongTo
30
+ self.item_type = Gorillib::Test::Smurf
31
+ self.parentage_method = :village
32
+ end
33
+ Gorillib::Test::SmurfCollection
34
+ end
35
+
36
+ let(:smurf_village_class) do
37
+ smurf_class ; smurf_collection_class
38
+ module Gorillib::Test
39
+ class SmurfVillage
40
+ include Gorillib::Model
41
+ field :name, Symbol
42
+ collection :smurfs, SmurfCollection, item_type: Smurf, key_method: :name
43
+ end
44
+ end
45
+ Gorillib::Test::SmurfVillage
46
+ end
47
+
48
+ let(:smurfhouse_class) do
49
+ module Gorillib::Test
50
+ class Smurfhouse
51
+ include Gorillib::Model
52
+ field :shape, Symbol
53
+ field :color, Symbol
54
+ end
55
+ end
56
+ Gorillib::Test::Smurfhouse
57
+ end
58
+
59
+ end
60
+
61
+ shared_context 'builder', :model_spec, :builder_spec do
62
+ let(:engine_class) do
63
+ class Gorillib::Test::Engine
64
+ include Gorillib::Builder
65
+ magic :name, Symbol, :default => ->{ "#{owner? ? owner.name : ''} engine"}
66
+ magic :carburetor, Symbol, :default => :stock
67
+ magic :volume, Integer, :doc => 'displacement volume, in in^3'
68
+ magic :cylinders, Integer
69
+ member :owner, Whatever
70
+ self
71
+ end
72
+ Gorillib::Test::Engine
73
+ end
74
+
75
+ let(:car_class) do
76
+ engine_class
77
+ class Gorillib::Test::Car
78
+ include Gorillib::Builder
79
+ magic :name, Symbol
80
+ magic :make_model, String
81
+ magic :year, Integer
82
+ magic :doors, Integer
83
+ member :engine, Gorillib::Test::Engine
84
+ self
85
+ end
86
+ Gorillib::Test::Car
87
+ end
88
+
89
+ let(:garage_class) do
90
+ car_class
91
+ class Gorillib::Test::Garage
92
+ include Gorillib::Builder
93
+ collection :cars, Gorillib::Test::Car, key_method: :name
94
+ self
95
+ end
96
+ Gorillib::Test::Garage
97
+ end
98
+
99
+ let(:wildcat) do
100
+ car_class.receive( :name => :wildcat,
101
+ :make_model => 'Buick Wildcat', :year => 1968, :doors => 2,
102
+ :engine => { :volume => 455, :cylinders => 8 } )
103
+ end
104
+ let(:ford_39) do
105
+ car_class.receive( :name => :ford_39,
106
+ :make_model => 'Ford Tudor Sedan', :year => 1939, :doors => 2, )
107
+ end
108
+ let(:garage) do
109
+ garage_class.new
110
+ end
111
+ let(:example_engine) do
112
+ engine_class.new( :name => 'Geo Metro 1.0L', :volume => 61, :cylinders => 3 )
113
+ end
114
+
115
+ end