extjs-mvc 0.3.4 → 0.3.5

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.rdoc CHANGED
@@ -62,7 +62,7 @@ E.g. with the following definition:
62
62
  class User < ActiveRecord::Base
63
63
  include ExtJS::Model
64
64
 
65
- extjs_fieldset :grid, [:name, :description, :company => [:name, :description]]
65
+ extjs_fieldset :grid, fields => [:name, :description, :company => [:name, :description]]
66
66
  extjs_fieldset :combo, [:full_name]
67
67
 
68
68
  def full_name
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.3.4
1
+ 0.3.5
data/lib/extjs-mvc.rb CHANGED
@@ -16,6 +16,8 @@ module ExtJS
16
16
  require 'model/data_mapper'
17
17
  elsif defined?(MongoMapper)
18
18
  require 'model/mongo_mapper'
19
+ else
20
+ raise StandardError.new("extjs-mvc could not detect an ORM framework. Be sure to include your ORM framework before initializing extjs-mvc Gem.")
19
21
  end
20
22
 
21
23
  # Rails-style Array#extract_options! used heavily
@@ -68,21 +68,17 @@ module ExtJS
68
68
  # @return {Array}
69
69
  #
70
70
  def extjs_associations
71
- #if @extjs_associations.nil?
72
- extjs_associations = {}
73
- self.reflections.keys.each do |key|
74
- assn = self.reflections[key]
75
- type = (assn.macro === :has_many || assn.macro === :has_and_belongs_to_many) ? :many : assn.macro
76
- extjs_associations[key.to_sym] = {
77
- :name => key.to_sym,
78
- :type => type,
79
- :class => assn.options[:polymorphic] ? nil : assn.class_name.constantize,
80
- :foreign_key => assn.association_foreign_key.to_sym,
81
- :is_polymorphic => !!assn.options[:polymorphic]
82
- }
83
- end
84
- #end
85
- extjs_associations
71
+ @extjs_associations ||= self.reflections.inject({}) do |memo, (key, assn)|
72
+ type = (assn.macro === :has_many || assn.macro === :has_and_belongs_to_many) ? :many : assn.macro
73
+ memo[key.to_sym] = {
74
+ :name => key.to_sym,
75
+ :type => type,
76
+ :class => assn.options[:polymorphic] ? nil : assn.class_name.constantize,
77
+ :foreign_key => assn.association_foreign_key.to_sym,
78
+ :is_polymorphic => !!assn.options[:polymorphic]
79
+ }
80
+ memo
81
+ end
86
82
  end
87
83
  end
88
84
  end
data/lib/model/base.rb CHANGED
@@ -21,23 +21,40 @@ module ExtJS
21
21
 
22
22
  ##
23
23
  # Converts a model instance to a record compatible with ExtJS
24
- # @params {Mixed} params A list of fields to use instead of this Class's extjs_record_fields
25
24
  #
25
+ # The first parameter should be the fieldset for which the record will be returned.
26
+ # If no parameter is provided, then the default fieldset will be choosen
27
+ # Alternativly the first parameter can be a Hash with a :fields member to directly specify
28
+ # the fields to use for the record.
29
+ #
30
+ # All these are valid calls:
31
+ #
32
+ # user.to_record # returns record for :default fieldset
33
+ # # (fieldset is autmatically defined, if not set)
34
+ #
35
+ # user.to_record :fieldset # returns record for :fieldset fieldset
36
+ # # (fieldset is autmatically defined, if not set)
37
+ #
38
+ # user.to_record :fields => [:id, :password]
39
+ # # returns record for the fields 'id' and 'password'
40
+ #
41
+ # For even more valid options for this method (which all should not be neccessary to use)
42
+ # have a look at ExtJS::Model::Util.extract_fieldset_and_options
26
43
  def to_record(*params)
27
- fieldset, params = self.class.extjs_extract_fieldset! params
44
+ fieldset, options = Util.extract_fieldset_and_options params
28
45
 
29
46
  fields = []
30
- if params.empty?
47
+ if options[:fields].empty?
31
48
  fields = self.class.extjs_get_fields_for_fieldset(fieldset)
32
49
  else
33
- fields = self.class.process_fields(*params)
50
+ fields = self.class.process_fields(*options[:fields])
34
51
  end
35
52
 
36
53
  assns = self.class.extjs_associations
37
54
  pk = self.class.extjs_primary_key
38
55
 
39
56
  # build the initial field data-hash
40
- data = extjs_prepare_data(pk)
57
+ data = {pk => self.send(pk)}
41
58
 
42
59
  fields.each do |field|
43
60
  next if data.has_key? field[:name] # already processed (e.g. explicit mentioning of :id)
@@ -45,25 +62,34 @@ module ExtJS
45
62
  value = nil
46
63
  if association_reflection = assns[field[:name]] # if field is an association
47
64
  association = self.send(field[:name])
65
+
66
+ # skip this association if we already visited it
67
+ # otherwise we could end up in a cyclic reference
68
+ next if options[:visited_classes].include? association.class
69
+
48
70
  case association_reflection[:type]
49
- when :belongs_to
71
+ when :belongs_to, :has_one
50
72
  if association.respond_to? :to_record
51
73
  assn_fields = field[:fields]
52
74
  if assn_fields.nil?
53
75
  assn_fields = association.class.extjs_get_fields_for_fieldset(field.fetch(:fieldset, fieldset))
54
76
  end
55
- value = association.to_record *assn_fields
77
+
78
+ value = association.to_record :fields => assn_fields,
79
+ :visited_classes => options[:visited_classes] + [self.class]
56
80
  else
57
81
  value = {}
58
82
  (field[:fields]||[]).each do |sub_field|
59
83
  value[sub_field[:name]] = association.send(sub_field[:name]) if association.respond_to? sub_field[:name]
60
84
  end
61
85
  end
62
- # Append associations foreign_key to data
63
- data[association_reflection[:foreign_key]] = self.send(association_reflection[:foreign_key])
64
- if association_reflection[:is_polymorphic]
65
- foreign_type = self.class.extjs_polymorphic_type(association_reflection[:foreign_key])
66
- data[foreign_type] = self.send(foreign_type)
86
+ if association_reflection[:type] == :belongs_to
87
+ # Append associations foreign_key to data
88
+ data[association_reflection[:foreign_key]] = self.send(association_reflection[:foreign_key])
89
+ if association_reflection[:is_polymorphic]
90
+ foreign_type = self.class.extjs_polymorphic_type(association_reflection[:foreign_key])
91
+ data[foreign_type] = self.send(foreign_type)
92
+ end
67
93
  end
68
94
  when :many
69
95
  value = association.collect { |r| r.to_record } # use carefully, can get HUGE
@@ -76,15 +102,6 @@ module ExtJS
76
102
  end
77
103
  data
78
104
  end
79
-
80
- ##
81
- # prepares the initial data-hash. Had to implement this to fix a MongoMapper issue where pk
82
- # is an Object. Messed things up when converting to JSON. Perhaps a better way is possible.
83
- # Any adapter can override this but typical relational dbs with Integer pks won't need to.
84
- #
85
- def extjs_prepare_data(pk)
86
- {pk => self.send(pk)}
87
- end
88
105
  end
89
106
 
90
107
  ##
@@ -95,12 +112,31 @@ module ExtJS
95
112
  # render AR columns to Ext.data.Record.create format
96
113
  # eg: {name:'foo', type: 'string'}
97
114
  #
98
- def extjs_record(*fields)
99
- fieldset, fields = self.extjs_extract_fieldset! fields
100
- if fields.empty?
115
+ # The first parameter should be the fieldset for which the record definition will be returned.
116
+ # If no parameter is provided, then the default fieldset will be choosen
117
+ # Alternativly the first parameter can be a Hash with a :fields member to directly specify
118
+ # the fields to use for the record config.
119
+ #
120
+ # All these are valid calls:
121
+ #
122
+ # User.extjs_record # returns record config for :default fieldset
123
+ # # (fieldset is autmatically defined, if not set)
124
+ #
125
+ # User.extjs_record :fieldset # returns record config for :fieldset fieldset
126
+ # # (fieldset is autmatically defined, if not set)
127
+ #
128
+ # User.extjs_record :fields => [:id, :password]
129
+ # # returns record config for the fields 'id' and 'password'
130
+ #
131
+ # For even more valid options for this method (which all should not be neccessary to use)
132
+ # have a look at ExtJS::Model::Util.extract_fieldset_and_options
133
+ def extjs_record(*params)
134
+ fieldset, options = Util.extract_fieldset_and_options params
135
+
136
+ if options[:fields].empty?
101
137
  fields = self.extjs_get_fields_for_fieldset(fieldset)
102
138
  else
103
- fields = self.process_fields(*fields)
139
+ fields = self.process_fields(*options[:fields])
104
140
  end
105
141
 
106
142
  associations = self.extjs_associations
@@ -109,17 +145,23 @@ module ExtJS
109
145
  rs = []
110
146
 
111
147
  fields.each do |field|
148
+
112
149
  field = Marshal.load(Marshal.dump(field)) # making a deep copy
113
150
 
114
151
  if col = columns[field[:name]] # <-- column on this model
115
152
  rs << self.extjs_field(field, col)
116
153
  elsif assn = associations[field[:name]]
154
+ # skip this association if we already visited it
155
+ # otherwise we could end up in a cyclic reference
156
+ next if options[:visited_classes].include? assn[:class]
157
+
117
158
  assn_fields = field[:fields]
118
159
  if assn[:class].respond_to?(:extjs_record) # <-- exec extjs_record on assn Model.
119
160
  if assn_fields.nil?
120
161
  assn_fields = assn[:class].extjs_get_fields_for_fieldset(field.fetch(:fieldset, fieldset))
121
162
  end
122
- record = assn[:class].extjs_record(field.fetch(:fieldset, fieldset), assn_fields)
163
+
164
+ record = assn[:class].extjs_record(field.fetch(:fieldset, fieldset), { :visited_classes => options[:visited_classes] + [self], :fields => assn_fields})
123
165
  rs.concat(record[:fields].collect { |assn_field|
124
166
  self.extjs_field(assn_field, :parent_trail => field[:name], :mapping => field[:name], :allowBlank => true) # <-- allowBlank on associated data?
125
167
  })
@@ -170,12 +212,9 @@ module ExtJS
170
212
  # end
171
213
  #
172
214
  def extjs_fieldset(*params)
173
- fieldset, params = self.extjs_extract_fieldset! params
174
- # creates a method storing the fieldset-to-field association
175
- # using a method and not an class-level variable because the latter
176
- # is bogus when we deal with inheritance
215
+ fieldset, options = Util.extract_fieldset_and_options params
177
216
  var_name = :"@extjs_fieldsets__#{fieldset}"
178
- self.instance_variable_set( var_name, self.process_fields(*params) )
217
+ self.instance_variable_set( var_name, self.process_fields(*options[:fields]) )
179
218
  end
180
219
 
181
220
  def extjs_get_fields_for_fieldset(fieldset)
@@ -194,7 +233,9 @@ module ExtJS
194
233
  # shortcut to define the default fieldset. For backwards-compatibility.
195
234
  #
196
235
  def extjs_fields(*params)
197
- self.extjs_fieldset(:default, params)
236
+ self.extjs_fieldset(:default, {
237
+ :fields => params
238
+ })
198
239
  end
199
240
 
200
241
  ##
@@ -229,9 +270,10 @@ module ExtJS
229
270
  elsif f.has_key?(:name) # already a valid Hash, just copy it over
230
271
  fields << f
231
272
  else
232
- raise ArgumentError, "encountered a Hash that I don't know anyting to do with `#{f.inspect}:#{f.class}`"
273
+ raise ArgumentError, "encountered a Hash that I don't know anything to do with `#{f.inspect}:#{f.class}`"
233
274
  end
234
275
  else # should be a String or Symbol
276
+ raise ArgumentError, "encountered a fields Array that I don't understand: #{params.inspect} -- `#{f.inspect}:#{f.class}` is not a Symbol or String" unless f.is_a?(Symbol) || f.is_a?(String)
235
277
  fields << {:name => f.to_sym}
236
278
  end
237
279
  end
@@ -239,20 +281,6 @@ module ExtJS
239
281
  fields
240
282
  end
241
283
 
242
- ##
243
- # returns the fieldset from the arguments.
244
- # @return [{Symbol}, Array]
245
- def extjs_extract_fieldset! arguments
246
- fieldset = :default
247
- if arguments.size > 1 && arguments[0].is_a?(Symbol) && arguments[1].is_a?(Array)
248
- fieldset = arguments.shift
249
- arguments = arguments[0]
250
- elsif arguments.size == 1 && arguments[0].is_a?(Symbol)
251
- fieldset = arguments.shift
252
- end
253
- [fieldset, arguments]
254
- end
255
-
256
284
  ##
257
285
  # Render a column-config object
258
286
  # @param {Hash/Column} field Field-configuration Hash, probably has :name already set and possibly Ext.data.Field options.
@@ -302,6 +330,39 @@ module ExtJS
302
330
  # @extjs_used_associations
303
331
  # end
304
332
  end
333
+
334
+ module Util
335
+
336
+ ##
337
+ # returns the fieldset from the arguments and normalizes the options.
338
+ # @return [{Symbol}, {Hash}]
339
+ def self.extract_fieldset_and_options arguments
340
+ orig_args = arguments
341
+ fieldset = :default
342
+ options = { # default options
343
+ :visited_classes => [],
344
+ :fields => []
345
+ }
346
+ if arguments.size > 2 || (arguments.size == 2 && !arguments[0].is_a?(Symbol))
347
+ raise ArgumentError, "Don't know how to handle #{arguments.inspect}"
348
+ elsif arguments.size == 2 && arguments[0].is_a?(Symbol)
349
+ fieldset = arguments.shift
350
+ if arguments[0].is_a?(Array)
351
+ options.update({
352
+ :fields => arguments[0]
353
+ })
354
+ elsif arguments[0].is_a?(Hash)
355
+ options.update(arguments[0])
356
+ end
357
+ elsif arguments.size == 1 && arguments[0].is_a?(Symbol)
358
+ fieldset = arguments.shift
359
+ elsif arguments.size == 1 && arguments[0].is_a?(Hash)
360
+ fieldset = arguments[0].delete(:fieldset) || :default
361
+ options.update(arguments[0])
362
+ end
363
+ [fieldset, options]
364
+ end
365
+ end
305
366
  end
306
367
  end
307
368
 
@@ -4,12 +4,6 @@
4
4
 
5
5
  module ExtJS
6
6
  module Model
7
- module InstanceMethods
8
- def extjs_prepare_data(pk)
9
- {pk => self.send(pk).to_s}
10
- end
11
- end
12
-
13
7
  ##
14
8
  # ClassMethods
15
9
  #
@@ -28,19 +22,16 @@ module ExtJS
28
22
  end
29
23
 
30
24
  def extjs_associations
31
- if @extjs_associations.nil?
32
- @extjs_associations = {}
33
- self.associations.keys.each do |key|
34
- @extjs_associations[key.to_sym] = {
35
- :name => key,
36
- :type => self.associations[key].type,
37
- :class => self.associations[key].class_name.constantize,
38
- :foreign_key => self.associations[key].foreign_key,
39
- :is_polymorphic => false # <-- no impl. for MM is_polymorhpic yet. Anyone care to implement this?
40
- }
41
- end
25
+ @extjs_associations ||= self.associations.inject({}) do |memo, (key, assn)|
26
+ memo[key.to_sym] = {
27
+ :name => key.to_sym,
28
+ :type => assn.type,
29
+ :class => assn.class_name.constantize,
30
+ :foreign_key => assn.foreign_key,
31
+ :is_polymorphic => false
32
+ }
33
+ memo
42
34
  end
43
- @extjs_associations
44
35
  end
45
36
 
46
37
  def extjs_type(col)
data/test/model_test.rb CHANGED
@@ -166,6 +166,48 @@ class ModelTest < Test::Unit::TestCase
166
166
  end
167
167
  end
168
168
 
169
+ context "Person with User association (has_one relationship)" do
170
+ setup do
171
+ clean_all
172
+ User.extjs_fields(:id, :password)
173
+ Person.extjs_fields(:id, :user)
174
+ end
175
+ should "produce a valid store config" do
176
+ fields = Person.extjs_record[:fields]
177
+ assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" }
178
+ assert_array_has_item(fields, 'has user_id') {|f| f[:name] === "user_id" and f[:mapping] == 'user.id' }
179
+ assert_array_has_item(fields, 'has user_password') {|f| f[:name] === "user_password"and f[:mapping] == 'user.password' }
180
+ end
181
+ should "produce a valid to_record record" do
182
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
183
+ user = User.create!(:person_id => person.id, :password => 'password')
184
+ record = person.reload.to_record
185
+ assert_equal(person.id, record[:id])
186
+ assert_equal(user.id, record[:user][:id])
187
+ assert_equal('password', record[:user][:password])
188
+ end
189
+ end
190
+
191
+ context "Person with User association (has_one/belongs_to relationship) cyclic reference" do
192
+ setup do
193
+ clean_all
194
+ User.extjs_fields(:id, :person)
195
+ Person.extjs_fields(:id, :user)
196
+ end
197
+ should "produce a valid store config for Person" do
198
+ fields = Person.extjs_record[:fields]
199
+ assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" }
200
+ assert_array_has_item(fields, 'has user_id') {|f| f[:name] === "user_id" and f[:mapping] == 'user.id' }
201
+ end
202
+ should "produce a valid to_record record for Person" do
203
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
204
+ user = User.create!(:person_id => person.id, :password => 'password')
205
+ record = person.reload.to_record
206
+ assert_equal(person.id, record[:id])
207
+ assert_equal(user.id, record[:user][:id])
208
+ end
209
+ end
210
+
169
211
  context "Fields should render with correct, ExtJS-compatible data-types" do
170
212
  setup do
171
213
  clean_all
@@ -204,7 +246,7 @@ class ModelTest < Test::Unit::TestCase
204
246
  end
205
247
  end
206
248
 
207
- context "polymorphic assosiations" do
249
+ context "polymorphic associations" do
208
250
  setup do
209
251
  clean_all
210
252
  end
@@ -220,7 +262,7 @@ class ModelTest < Test::Unit::TestCase
220
262
  assert_array_has_item(fields, 'addressable_type') {|f| f[:name] === 'addressable_type' && !f[:mapping] }
221
263
  end
222
264
 
223
- should "create the right store config when including members of the polymorpic association" do
265
+ should "create the right store config when including members of the polymorphic association" do
224
266
  Address.extjs_fields :street, :addressable => [:name]
225
267
  fields = Address.extjs_record[:fields]
226
268
  assert_array_has_item(fields, "has addressable_name") {|f| f[:name] === 'addressable_name' && f[:mapping] === 'addressable.name'}
@@ -246,13 +288,13 @@ class ModelTest < Test::Unit::TestCase
246
288
  clean_all
247
289
  end
248
290
 
249
- should "fieldsets should be accessible from decendants" do
291
+ should "fieldsets should be accessible from descendants" do
250
292
  Location.extjs_fieldset :on_location, [:street]
251
293
  fields = House.extjs_record(:on_location)[:fields]
252
294
  assert_array_has_item(fields, 'has street') {|f| f[:name] === 'street' }
253
295
  assert_array_has_not_item(fields, 'has name') {|f| f[:name] === 'name' }
254
296
  end
255
- should "fieldsets should be overrideable from decendants" do
297
+ should "fieldsets should be overrideable from descendants" do
256
298
  Location.extjs_fieldset :override, [:street]
257
299
  House.extjs_fieldset :override, [:name]
258
300
  fields = House.extjs_record(:override)[:fields]
@@ -261,11 +303,11 @@ class ModelTest < Test::Unit::TestCase
261
303
  end
262
304
  end
263
305
 
264
- context "ExtJS::Model::ClassMethods" do
265
-
266
- context "#extjs_extract_fieldset! default" do
306
+ context "ExtJS::Model::Util" do
307
+ context "#extract_fieldset_and_options default" do
267
308
  setup do
268
- @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:one, :two, :three]
309
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options [:fields => [:one, :two, :three]]
310
+ @fields = @options[:fields]
269
311
  end
270
312
  should "return :default when no fieldset provided" do
271
313
  assert_equal(:'default', @fieldset)
@@ -275,9 +317,10 @@ class ModelTest < Test::Unit::TestCase
275
317
  end
276
318
  end
277
319
 
278
- context "#extjs_extract_fieldset! with explicit fieldset definition" do
320
+ context "#extract_fieldset_and_options with explicit fieldset definition and array with fields" do
279
321
  setup do
280
- @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:explicit, [:one, :two, :three]]
322
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options [:explicit, [:one, :two, :three]]
323
+ @fields = @options[:fields]
281
324
  end
282
325
  should "return :default when no fieldset provided" do
283
326
  assert_equal(:'explicit', @fieldset)
@@ -287,18 +330,55 @@ class ModelTest < Test::Unit::TestCase
287
330
  end
288
331
  end
289
332
 
290
- context "#extjs_extract_fieldset! edge cases" do
333
+ context "#extract_fieldset_and_options with explicit fieldset definition and hash with fields" do
334
+ setup do
335
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options [:explicit, {:fields => [:one, :two, :three]}]
336
+ @fields = @options[:fields]
337
+ end
338
+ should "return :default when no fieldset provided" do
339
+ assert_equal(:'explicit', @fieldset)
340
+ end
341
+ should "not alter the fields array" do
342
+ assert_equal([:one, :two, :three], @fields)
343
+ end
344
+ end
345
+
346
+ context "#extract_fieldset_and_options with only a hash" do
347
+ setup do
348
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options [{:fieldset => :explicit, :fields => [:one, :two, :three]}]
349
+ @fields = @options[:fields]
350
+ end
351
+ should "return :default when no fieldset provided" do
352
+ assert_equal(:'explicit', @fieldset)
353
+ end
354
+ should "not alter the fields array" do
355
+ assert_equal([:one, :two, :three], @fields)
356
+ end
357
+ end
358
+
359
+ context "#extract_fieldset_and_options edge cases" do
291
360
  should "called without arguments" do
292
- @fieldset, @fields = BogusModel.extjs_extract_fieldset! []
361
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options []
362
+ @fields = @options[:fields]
293
363
  assert_equal(:'default', @fieldset)
294
364
  assert_equal([], @fields)
295
365
  end
296
366
  should "called with only the fieldset and no field arguments" do
297
- @fieldset, @fields = BogusModel.extjs_extract_fieldset! [:explicit]
367
+ @fieldset, @options = ExtJS::Model::Util.extract_fieldset_and_options [:explicit]
368
+ @fields = @options[:fields]
298
369
  assert_equal(:'explicit', @fieldset)
299
370
  assert_equal([], @fields)
300
371
  end
372
+ should "raise error when called with more than 2 arguments" do
373
+ assert_raise(ArgumentError) { ExtJS::Model::Util.extract_fieldset_and_options [:explicit, :some, {}] }
374
+ end
375
+ should "raise error when called with 2 arguments and the first one is no symbol" do
376
+ assert_raise(ArgumentError) { ExtJS::Model::Util.extract_fieldset_and_options [{ :fields => [] }, :explicit] }
377
+ end
301
378
  end
379
+ end
380
+
381
+ context "ExtJS::Model::ClassMethods" do
302
382
 
303
383
  context "#process_fields" do
304
384
  should "handle a simple Array of Symbols" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: extjs-mvc
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Scott
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2010-02-20 00:00:00 -05:00
12
+ date: 2010-02-21 00:00:00 -05:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency