sencha-model 0.5.0

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.
@@ -0,0 +1,20 @@
1
+ class Test::Unit::TestCase
2
+ ##
3
+ # Asserts that the passed list of fields are specified in the whorm_fields call
4
+ # in the model class.
5
+ # @fieldset {Symbol} fieldset (optional, set to :default if missing)
6
+ # @options {Hash} Should contain a :fields key with an array of fields to check for
7
+ #
8
+ def self.should_have_whorm_fields_for_fieldset fieldset = :default, fields = []
9
+ klass = described_type
10
+ should "have the correct extjs_fields" do
11
+ assert !fields.empty?, "an array of fields must be passed. Example: (:fieldset_name, [:name, :id])"
12
+ fields.each do |field|
13
+ found_record = klass.whorm_get_fields_for_fieldset(fieldset).find do|record_field|
14
+ record_field[:name].to_sym == field.to_sym
15
+ end
16
+ assert_not_nil found_record, "whorm field #{field} isn't listed in the #{klass.name} model"
17
+ end
18
+ end
19
+ end
20
+ end
File without changes
@@ -0,0 +1,70 @@
1
+ # TODO: Figure out how to iterate each ORM framework AR, DM, MM and test each.
2
+ require 'active_record'
3
+ require 'active_support'
4
+ require 'sencha-model'
5
+ require 'extlib/inflection'
6
+
7
+ gem 'sqlite3-ruby'
8
+
9
+ class Test::App
10
+
11
+ attr_reader :models
12
+
13
+ def initialize(orm = :active_record)
14
+ @orm = orm
15
+ @config = YAML::load(IO.read("#{ROOT}/config/database.yml"))
16
+
17
+ # Load ORM
18
+ send("boot_#{orm.to_s}")
19
+
20
+ load_models
21
+
22
+ require 'db/schema'
23
+
24
+ end
25
+
26
+ ##
27
+ # Reset a model's @sencha_fieldsets
28
+ #
29
+ def clean_all
30
+ @models.map { |klass| clean klass }
31
+ end
32
+
33
+
34
+ private
35
+
36
+ def boot_active_record
37
+ ActiveRecord::Base.establish_connection(@config['test'])
38
+ end
39
+
40
+ def boot_mongo_mapper
41
+
42
+ end
43
+
44
+ def boot_data_mapper
45
+
46
+ end
47
+
48
+ ##
49
+ # Do a dir on /models and constantize each filename
50
+ #
51
+ def load_models
52
+ @models = []
53
+ # Load Models and Schema for corresponding orm
54
+ re = /^.*\/(.*).rb$/
55
+ Dir["#{ROOT}/models/#{@orm.to_s}/*"].each { |c|
56
+ require c
57
+ match = c.match(re)
58
+ @models << Extlib::Inflection.constantize(Extlib::Inflection.camelize(match[1])) if match
59
+ }
60
+ end
61
+
62
+ def clean klass
63
+ klass.instance_variables.each do |var_name|
64
+ if /\A@sencha_fieldsets__/ =~ var_name.to_s
65
+ klass.instance_variable_set( var_name.to_sym, nil )
66
+ end
67
+ end
68
+ end
69
+
70
+ end
@@ -0,0 +1,3 @@
1
+ test:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,75 @@
1
+
2
+ ##
3
+ # build simple database
4
+ #
5
+ # people
6
+ #
7
+ ActiveRecord::Base.connection.create_table :users, :force => true do |table|
8
+ table.column :id, :serial
9
+ table.column :person_id, :integer
10
+ table.column :password, :string
11
+ table.column :created_at, :date
12
+ table.column :disabled, :boolean, :default => true
13
+ end
14
+ ##
15
+ # people
16
+ #
17
+ ActiveRecord::Base.connection.create_table :people, :force => true do |table|
18
+ table.column :id, :serial
19
+ table.column :first, :string, :null => false
20
+ table.column :last, :string, :null => false
21
+ table.column :email, :string, :null => false
22
+ end
23
+ ##
24
+ # user_groups, join table
25
+ #
26
+ ActiveRecord::Base.connection.create_table :user_groups, :force => true do |table|
27
+ table.column :user_id, :integer
28
+ table.column :group_id, :integer
29
+ end
30
+
31
+ ##
32
+ # groups
33
+ #
34
+ ActiveRecord::Base.connection.create_table :groups, :force => true do |table|
35
+ table.column :id, :serial
36
+ table.column :title, :string
37
+ end
38
+
39
+ ##
40
+ # locations
41
+ #
42
+ ActiveRecord::Base.connection.create_table :locations, :force => true do |table|
43
+ table.column :id, :serial
44
+ table.column :name, :string
45
+ table.column :street, :string
46
+ table.column :type, :string
47
+ end
48
+
49
+ ##
50
+ # addresses
51
+ #
52
+ ActiveRecord::Base.connection.create_table :addresses, :force => true do |table|
53
+ table.column :id, :serial
54
+ table.column :addressable_type, :string
55
+ table.column :addressable_id, :integer
56
+ table.column :street, :string
57
+ end
58
+
59
+ ##
60
+ # Mock a Model for testing data-types
61
+ #
62
+ ActiveRecord::Base.connection.create_table :data_types, :force => true do |table|
63
+ table.column :id, :serial
64
+ table.column :string_column, :string
65
+ table.column :decimal_column, :decimal
66
+ table.column :float_column, :float
67
+ table.column :date_column, :date
68
+ table.column :datetime_column, :datetime
69
+ table.column :time_column, :time
70
+ table.column :email, :string
71
+ table.column :integer_column, :integer
72
+ table.column :notnull_column, :string, :null => false
73
+ table.column :default_column, :boolean, :default => true
74
+ table.column :boolean_column, :boolean
75
+ end
@@ -0,0 +1,4 @@
1
+ class Address < ActiveRecord::Base
2
+ belongs_to :addressable, :polymorphic => true
3
+ include Sencha::Model
4
+ end
@@ -0,0 +1,3 @@
1
+ class DataType < ActiveRecord::Base
2
+ include Sencha::Model
3
+ end
@@ -0,0 +1,4 @@
1
+ class Group < ActiveRecord::Base
2
+ has_many :users
3
+ include Sencha::Model
4
+ end
@@ -0,0 +1,4 @@
1
+ require "#{File.dirname(__FILE__)}/location"
2
+
3
+ class House < Location
4
+ end
@@ -0,0 +1,5 @@
1
+ class Location < ActiveRecord::Base
2
+ has_one :address, :as => :addressable
3
+ include Sencha::Model
4
+ end
5
+
@@ -0,0 +1,4 @@
1
+ class Person < ActiveRecord::Base
2
+ has_one :user
3
+ include Sencha::Model
4
+ end
@@ -0,0 +1,6 @@
1
+ class User < ActiveRecord::Base
2
+ include Sencha::Model
3
+ belongs_to :person
4
+
5
+ has_and_belongs_to_many :groups, :join_table => :user_groups
6
+ end
@@ -0,0 +1,4 @@
1
+ class UserGroup < ActiveRecord::Base
2
+ belongs_to :user
3
+ belongs_to :group
4
+ end
File without changes
@@ -0,0 +1,526 @@
1
+ require 'test_helper'
2
+
3
+ ##
4
+ # create a couple of related instances.
5
+ #
6
+ p = Person.create(:first => "Chris", :last => "Scott", :email => "chris@scott.com")
7
+ u = User.create(:password => "1234", :person => p)
8
+
9
+ class BogusModel
10
+ include Sencha::Model
11
+ def additional_attribute
12
+ 'computed value'
13
+ end
14
+ class << self
15
+ def sencha_allow_blank(col)
16
+ true
17
+ end
18
+
19
+ def sencha_default(col)
20
+ nil
21
+ end
22
+
23
+ def sencha_type(col)
24
+ nil
25
+ end
26
+
27
+ def sencha_column_names
28
+ [:one, :two, :three_id]
29
+ end
30
+
31
+ def sencha_columns_hash
32
+ {
33
+ :one => {},
34
+ :two => {},
35
+ :three_id => {}
36
+ }
37
+ end
38
+
39
+ def sencha_polymorphic_type(id_column_name)
40
+ id_column_name.to_s.gsub(/_id\Z/, '_type').to_sym
41
+ end
42
+
43
+ def sencha_primary_key
44
+ :id
45
+ end
46
+
47
+ def sencha_associations
48
+ {
49
+ :three => {
50
+ :name => :tree,
51
+ :type => :belongs_to,
52
+ :class => nil,
53
+ :foreign_key => :three_id,
54
+ :is_polymorphic => false
55
+ }
56
+ }
57
+ end
58
+ end
59
+ end
60
+
61
+
62
+ class BogusModelChild < BogusModel
63
+ end
64
+
65
+ class ModelTest < Test::Unit::TestCase
66
+ context "Rendering DataReader configuration for Person and User" do
67
+
68
+ setup do
69
+ App.clean_all
70
+ end
71
+
72
+ should "Person and User should render a valid Reader config" do
73
+ reader = Person.sencha_schema
74
+ assert reader.kind_of?(Hash) && reader.has_key?(:fields) && reader.has_key?(:idProperty)
75
+ end
76
+ should "Person instance should render with to_record, a Hash containing at least a primary_key" do
77
+ rec = Person.first.to_record
78
+ assert_kind_of(Hash, rec)
79
+ assert_array_has_item(rec.keys, 'has primary key') { |i| i.to_s == Person.sencha_primary_key.to_s }
80
+ end
81
+ should "User should render a Reader config" do
82
+ reader = User.sencha_schema
83
+ assert reader.kind_of?(Hash) && reader.has_key?(:fields) && reader.has_key?(:idProperty)
84
+ end
85
+ should "User instance should render with to_record, a Hash containing at least a primary_key" do
86
+ rec = User.first.to_record
87
+ assert rec.kind_of?(Hash) && rec.keys.include?(User.sencha_primary_key)
88
+ end
89
+ should "User instance should render to_record containing foreign_key of Person" do
90
+ rec = User.first.to_record
91
+ assn = User.sencha_associations[:person]
92
+ assert rec.keys.include?(assn[:foreign_key])
93
+ end
94
+
95
+ end
96
+
97
+ context "A User with HABTM relationship with Group" do
98
+ setup do
99
+ App.clean_all
100
+ UserGroup.destroy_all
101
+
102
+ @user = User.first
103
+ UserGroup.create(:user => @user, :group => Group.create(:title => "Merb"))
104
+ UserGroup.create(:user => @user, :group => Group.create(:title => "Rails"))
105
+ end
106
+
107
+ should "Render to_record should return 2 groups" do
108
+ User.sencha_fields(:groups)
109
+ assert @user.to_record[:groups].length == 2
110
+ end
111
+ end
112
+
113
+ context "A User with Person relationship: User.sencha_fields(:password, :person => [:first, {:last => {'sortDir' => 'ASC'}}])" do
114
+ setup do
115
+ App.clean_all
116
+ User.sencha_fields(:password, {:person => [:first, {:last => {:sortDir => "ASC"}}]})
117
+ @fields = User.sencha_schema[:fields]
118
+ end
119
+
120
+ should "User should render a Reader with 4 total fields" do
121
+ assert @fields.count === 4
122
+ end
123
+ should "Reader fields should contain 'password' field" do
124
+ assert_array_has_item(@fields, 'has password field') {|f| f[:name] === "password"}
125
+ end
126
+ should "Reader fields should contain person_id" do
127
+ assns = User.sencha_associations
128
+ assn = assns[:person]
129
+ assert_array_has_item(@fields, 'has foreign key person_id') {|f| f[:name] === assns[:person][:foreign_key].to_s }
130
+ end
131
+ should "Reader fields should contain mapped field 'person.first'" do
132
+ assert_array_has_item(@fields, 'has person_first') {|f| f[:name] === "person_first" and f[:mapping] === "person.first"}
133
+ end
134
+ should "Reader fields should contain mapped field 'person.last'" do
135
+ assert_array_has_item(@fields, 'has person_last') {|f| f[:name] === "person_last" and f[:mapping] === "person.last"}
136
+ end
137
+ should "person.last should have additional configuration 'sortDir' => 'ASC'" do
138
+ assert_array_has_item(@fields, 'has person.last with sortDir') {|f| f[:name] === "person_last" and f[:sortDir] === 'ASC' }
139
+ end
140
+
141
+ should "produce a valid to_record record" do
142
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
143
+ user = User.create!(:person_id => person.id, :password => 'password')
144
+ record = user.to_record
145
+ assert_equal(user.id, record[:id])
146
+ assert_equal(person.id, record[:person_id])
147
+ assert_equal('password', record[:password])
148
+ assert_equal('last', record[:person][:last])
149
+ assert_equal('first', record[:person][:first])
150
+ end
151
+ end
152
+
153
+ context "User with standard Person association" do
154
+ setup do
155
+ App.clean_all
156
+ User.sencha_fields(:id, :password, :person)
157
+ end
158
+ should "produce a valid store config" do
159
+ fields = User.sencha_schema[:fields]
160
+ assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" }
161
+ assert_array_has_item(fields, 'has person_id') {|f| f[:name] === "person_id" }
162
+ assert_array_has_item(fields, 'has password') {|f| f[:name] === "password" }
163
+ assert_array_has_item(fields, 'has person_last') {|f| f[:name] === "person_last" and f[:mapping] == "person.last" }
164
+ assert_array_has_item(fields, 'has person_first') {|f| f[:name] === "person_first" and f[:mapping] == "person.first" }
165
+ end
166
+ should "produce a valid to_record record" do
167
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
168
+ user = User.create!(:person_id => person.id, :password => 'password')
169
+ record = user.to_record
170
+ assert_equal(user.id, record[:id])
171
+ assert_equal(person.id, record[:person_id])
172
+ assert_equal('password', record[:password])
173
+ assert_equal('last', record[:person][:last])
174
+ assert_equal('first', record[:person][:first])
175
+ end
176
+ end
177
+
178
+ context "Person with User association (has_one relationship)" do
179
+ setup do
180
+ App.clean_all
181
+ User.sencha_fields(:id, :password)
182
+ Person.sencha_fields(:id, :user)
183
+ end
184
+ should "produce a valid store config" do
185
+ fields = Person.sencha_schema[:fields]
186
+ assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" }
187
+ assert_array_has_item(fields, 'has user_id') {|f| f[:name] === "user_id" and f[:mapping] == 'user.id' }
188
+ assert_array_has_item(fields, 'has user_password') {|f| f[:name] === "user_password"and f[:mapping] == 'user.password' }
189
+ end
190
+ should "produce a valid to_record record" do
191
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
192
+ user = User.create!(:person_id => person.id, :password => 'password')
193
+ record = person.reload.to_record
194
+ assert_equal(person.id, record[:id])
195
+ assert_equal(user.id, record[:user][:id])
196
+ assert_equal('password', record[:user][:password])
197
+ end
198
+ end
199
+
200
+ context "Person with User association (has_one/belongs_to relationship) cyclic reference" do
201
+ setup do
202
+ App.clean_all
203
+ User.sencha_fields(:id, :person)
204
+ Person.sencha_fields(:id, :user)
205
+ end
206
+ should "produce a valid store config for Person" do
207
+ fields = Person.sencha_schema[:fields]
208
+ assert_array_has_item(fields, 'has id') {|f| f[:name] === "id" }
209
+ assert_array_has_item(fields, 'has user_id') {|f| f[:name] === "user_id" and f[:mapping] == 'user.id' }
210
+ end
211
+ should "produce a valid to_record record for Person" do
212
+ person = Person.create!(:first => 'first', :last => 'last', :email => 'email')
213
+ user = User.create!(:person_id => person.id, :password => 'password')
214
+ record = person.reload.to_record
215
+ assert_equal(person.id, record[:id])
216
+ assert_equal(user.id, record[:user][:id])
217
+ end
218
+ end
219
+
220
+ context "Fields should render with correct, ExtJS-compatible data-types" do
221
+ setup do
222
+ App.clean_all
223
+ @fields = DataType.sencha_schema[:fields]
224
+ end
225
+
226
+ should "Understand 'string'" do
227
+ assert_array_has_item(@fields, 'has string_column with string') {|f| f[:name] == 'string_column' and f[:type] == 'string'}
228
+ end
229
+ should "Understand 'integer' as 'int'" do
230
+ assert_array_has_item(@fields, 'has integer_column with int') {|f| f[:name] == 'integer_column' and f[:type] == 'int'}
231
+ end
232
+ should "Understand 'float'" do
233
+ assert_array_has_item(@fields, 'has float_column with float') {|f| f[:name] == 'float_column' and f[:type] == 'float'}
234
+ end
235
+ should "Understand 'decimal' as 'float'" do # Is this correct??
236
+ assert_array_has_item(@fields, 'has decimal_column with float') {|f| f[:name] == 'decimal_column' and f[:type] == 'float'}
237
+ end
238
+ should "Understand 'date'" do
239
+ assert_array_has_item(@fields, 'has date_column with date') {|f| f[:name] == 'date_column' and f[:type] == 'date'}
240
+ end
241
+ should "Understand 'datetime' as 'date'" do
242
+ assert_array_has_item(@fields, 'has datetime_column with date') {|f| f[:name] == 'datetime_column' and f[:type] == 'date'}
243
+ end
244
+ should "Understand 'time' as 'date'" do
245
+ assert_array_has_item(@fields, 'has time_column with date') {|f| f[:name] == 'time_column' and f[:type] == 'date'}
246
+ end
247
+ should "Understand 'boolean'" do
248
+ assert_array_has_item(@fields, 'has boolean_column with boolean') {|f| f[:name] == 'boolean_column' and f[:type] == 'boolean'}
249
+ end
250
+ should "Understand NOT NULL" do
251
+ assert_array_has_item(@fields, 'has notnull_column with allowBlank == false') {|f| f[:name] == 'notnull_column' and f[:allowBlank] === false}
252
+ end
253
+ should "Understand DEFAULT" do
254
+ assert_array_has_item(@fields, 'has default_column with defaultValue == true') {|f| f[:name] == 'default_column' and f[:defaultValue] === true}
255
+ end
256
+ end
257
+
258
+ context "polymorphic associations" do
259
+ setup do
260
+ App.clean_all
261
+ end
262
+
263
+ should "return nil as class for a polymorphic relation" do
264
+ assert_equal(nil, Address.sencha_associations[:addressable][:class])
265
+ end
266
+
267
+ should "create a proper default store config" do
268
+ Address.sencha_fields
269
+ fields = Address.sencha_schema[:fields]
270
+ assert_array_has_item(fields, 'has addressable_id') {|f| f[:name] === 'addressable_id' && !f[:mapping] }
271
+ assert_array_has_item(fields, 'addressable_type') {|f| f[:name] === 'addressable_type' && !f[:mapping] }
272
+ end
273
+
274
+ should "create the right store config when including members of the polymorphic association" do
275
+ Address.sencha_fields :street, :addressable => [:name]
276
+ fields = Address.sencha_schema[:fields]
277
+ assert_array_has_item(fields, "has addressable_name") {|f| f[:name] === 'addressable_name' && f[:mapping] === 'addressable.name'}
278
+ assert_array_has_item(fields, "has addressable_id") {|f| f[:name] === 'addressable_id' && !f[:mapping] }
279
+ assert_array_has_item(fields, "has addressable_type") {|f| f[:name] === 'addressable_type' && !f[:mapping] }
280
+ end
281
+
282
+ should "fill in the right values for to_record" do
283
+ Address.sencha_fields :street, :addressable => [:name]
284
+ location = Location.create!(:name => 'Home')
285
+ address = location.create_address(:street => 'Main Street 1')
286
+ record = address.to_record
287
+ assert_equal({:name => "Home", :id => location.id}, record[:addressable])
288
+ assert_equal("Location", record[:addressable_type])
289
+ assert_equal(location.id, record[:addressable_id])
290
+ assert_equal(address.id, record[:id])
291
+ assert_equal("Main Street 1", record[:street])
292
+ end
293
+ end
294
+
295
+ context "single table inheritance" do
296
+ setup do
297
+ App.clean_all
298
+ end
299
+
300
+ should "fieldsets should be accessible from descendants" do
301
+ Location.sencha_fieldset :on_location, [:street]
302
+ fields = House.sencha_schema(:on_location)[:fields]
303
+ assert_array_has_item(fields, 'has street') {|f| f[:name] === 'street' }
304
+ assert_array_has_not_item(fields, 'has name') {|f| f[:name] === 'name' }
305
+ end
306
+ should "fieldsets should be overrideable from descendants" do
307
+ Location.sencha_fieldset :override, [:street]
308
+ House.sencha_fieldset :override, [:name]
309
+ fields = House.sencha_schema(:override)[:fields]
310
+ assert_array_has_not_item(fields, 'has street') {|f| f[:name] === 'street' }
311
+ assert_array_has_item(fields, 'has name') {|f| f[:name] === 'name' }
312
+ end
313
+ end
314
+
315
+ context "Sencha::Model::Util" do
316
+ context "#extract_fieldset_and_options default" do
317
+ setup do
318
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options [:fields => [:one, :two, :three]]
319
+ @fields = @options[:fields]
320
+ end
321
+ should "return :default when no fieldset provided" do
322
+ assert_equal(:'default', @fieldset)
323
+ end
324
+ should "not alter the fields array" do
325
+ assert_equal([:one, :two, :three], @fields)
326
+ end
327
+ end
328
+
329
+ context "#extract_fieldset_and_options with explicit fieldset definition and array with fields" do
330
+ setup do
331
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options [:explicit, [:one, :two, :three]]
332
+ @fields = @options[:fields]
333
+ end
334
+ should "return :default when no fieldset provided" do
335
+ assert_equal(:'explicit', @fieldset)
336
+ end
337
+ should "not alter the fields array" do
338
+ assert_equal([:one, :two, :three], @fields)
339
+ end
340
+ end
341
+
342
+ context "#extract_fieldset_and_options with explicit fieldset definition and hash with fields" do
343
+ setup do
344
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options [:explicit, {:fields => [:one, :two, :three]}]
345
+ @fields = @options[:fields]
346
+ end
347
+ should "return :default when no fieldset provided" do
348
+ assert_equal(:'explicit', @fieldset)
349
+ end
350
+ should "not alter the fields array" do
351
+ assert_equal([:one, :two, :three], @fields)
352
+ end
353
+ end
354
+
355
+ context "#extract_fieldset_and_options with only a hash" do
356
+ setup do
357
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options [{:fieldset => :explicit, :fields => [:one, :two, :three]}]
358
+ @fields = @options[:fields]
359
+ end
360
+ should "return :default when no fieldset provided" do
361
+ assert_equal(:'explicit', @fieldset)
362
+ end
363
+ should "not alter the fields array" do
364
+ assert_equal([:one, :two, :three], @fields)
365
+ end
366
+ end
367
+
368
+ context "#extract_fieldset_and_options edge cases" do
369
+ should "called without arguments" do
370
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options []
371
+ @fields = @options[:fields]
372
+ assert_equal(:'default', @fieldset)
373
+ assert_equal([], @fields)
374
+ end
375
+ should "called with only the fieldset and no field arguments" do
376
+ @fieldset, @options = Sencha::Model::Util.extract_fieldset_and_options [:explicit]
377
+ @fields = @options[:fields]
378
+ assert_equal(:'explicit', @fieldset)
379
+ assert_equal([], @fields)
380
+ end
381
+ should "raise error when called with more than 2 arguments" do
382
+ assert_raise(ArgumentError) { Sencha::Model::Util.extract_fieldset_and_options [:explicit, :some, {}] }
383
+ end
384
+ should "raise error when called with 2 arguments and the first one is no symbol" do
385
+ assert_raise(ArgumentError) { Sencha::Model::Util.extract_fieldset_and_options [{ :fields => [] }, :explicit] }
386
+ end
387
+ end
388
+ end
389
+
390
+ context "Sencha::Model::ClassMethods" do
391
+
392
+ context "#process_fields" do
393
+ should "handle a simple Array of Symbols" do
394
+ @fields = BogusModel.process_fields :one, :two, :three
395
+ assert_equal([{:name => :one}, {:name => :two}, {:name => :three}], @fields)
396
+ end
397
+ should "handle a mixed Array where the last item is a Hash" do
398
+ @fields = BogusModel.process_fields :one, :two, :three => [:three_one, :three_two]
399
+ assert_equal([{:name => :one}, {:name => :two}, {:name => :three, :fields => [{:name => :three_one}, {:name => :three_two}]}], @fields)
400
+ end
401
+ should "handle a mixed Array where a middle item is a Hash" do
402
+ @fields = BogusModel.process_fields :one, {:two => [:two_one, :two_two]}, :three
403
+ assert_equal([
404
+ {:name => :one},
405
+ {:name => :two, :fields => [{:name => :two_one}, {:name => :two_two}]},
406
+ {:name => :three}], @fields)
407
+ end
408
+ should "handle option :only" do
409
+ @fields = BogusModel.process_fields :only => [:one, :two, :three]
410
+ assert_equal([{:name => :one}, {:name => :two}, {:name => :three}], @fields)
411
+ end
412
+ should "handle option :exclude" do
413
+ @fields = BogusModel.process_fields :exclude => [:two]
414
+ assert_equal([{:name => :one}, {:name => :three_id}], @fields)
415
+ end
416
+ should "handle option :additional" do
417
+ @fields = BogusModel.process_fields :additional => [:additional_attribute]
418
+ assert_equal([{:name => :one}, {:name => :two}, {:name => :three_id}, {:name => :additional_attribute}], @fields)
419
+
420
+ end
421
+ should "handle {:field => {:sortDir => 'ASC'}}" do
422
+ @fields = BogusModel.process_fields({:field => {:sortDir => 'ASC'}})
423
+ assert_equal([{:name => :field, :sortDir => 'ASC'}], @fields)
424
+ end
425
+ should "handle recursive definition" do
426
+ @fields = BogusModel.process_fields(:one, {:three => [{:one => [:one, :two]}, {:two => {:sortDir => "ASC"}}]})
427
+ assert_equal([{:name => :one}, {:name => :three, :fields => [{:name => :one, :fields => [{:name => :one}, {:name => :two}]}, {:name => :two, :sortDir => 'ASC'}]}], @fields)
428
+ end
429
+ should "not touch already correct fields" do
430
+ @fields = BogusModel.process_fields(:one, {:name => :field,:sortDir => 'ASC'})
431
+ assert_equal([{:name => :one},{:name => :field, :sortDir => 'ASC'}], @fields)
432
+ end
433
+ should "raise ArgumentError when pass in bogus hash" do
434
+ assert_raise(ArgumentError) { @fields = BogusModel.process_fields(:one, {:nme => :field,:sortDir => 'ASC'}) }
435
+ end
436
+ end
437
+
438
+ context "#sencha_field" do
439
+ should "type gets set to 'auto' when not present" do
440
+ @field = BogusModel.sencha_field({:name => :test})
441
+ assert_equal('auto', @field[:type])
442
+ end
443
+ should "not touch type when alredy present" do
444
+ @field = BogusModel.sencha_field({:name => :test, :type => 'untouched'})
445
+ assert_equal('untouched', @field[:type])
446
+ end
447
+ should "raise exception when bogus field config passed" do
448
+ assert_raise(ArgumentError) { BogusModel.sencha_field({:name => :test, "type" => 'untouched'}) }
449
+ end
450
+
451
+ end
452
+
453
+ context "#sencha_field with ORM config" do
454
+ should "set allowBlank" do
455
+ BogusModel.expects(:sencha_allow_blank).returns(false)
456
+ @field = BogusModel.sencha_field({:name => :test}, stub())
457
+ assert_equal(false, @field[:allowBlank])
458
+ end
459
+ should "set type" do
460
+ BogusModel.expects(:sencha_type).returns('int')
461
+ @field = BogusModel.sencha_field({:name => :test}, stub())
462
+ assert_equal('int', @field[:type])
463
+ end
464
+ should "set defaultValue" do
465
+ BogusModel.expects(:sencha_default).returns(true)
466
+ @field = BogusModel.sencha_field({:name => :test}, stub())
467
+ assert_equal(true, @field[:defaultValue])
468
+ end
469
+ should "set dateFormat to c it's a date" do
470
+ BogusModel.expects(:sencha_type).returns('date')
471
+ @field = BogusModel.sencha_field({:name => :test}, stub())
472
+ assert_equal('c', @field[:dateFormat])
473
+ end
474
+ should "not touch dateFormat if it's already set" do
475
+ BogusModel.expects(:sencha_type).returns('date')
476
+ @field = BogusModel.sencha_field({:name => :test, :dateFormat => 'not-c'}, stub())
477
+ assert_equal('not-c', @field[:dateFormat])
478
+ end
479
+ end
480
+
481
+ context "#sencha_field with Hash config" do
482
+ should "set correct name and mapping" do
483
+ @field = BogusModel.sencha_field({:name => :son}, {:mapping => 'grandfather.father', :parent_trail => 'grandfather_father'})
484
+ assert_equal('grandfather_father_son', @field[:name])
485
+ assert_equal('grandfather.father.son', @field[:mapping])
486
+ end
487
+ should "apply config to field" do
488
+ @field = BogusModel.sencha_field({:name => :son}, {:sortDir => 'ASC'})
489
+ assert_equal('ASC', @field[:sortDir])
490
+ end
491
+ end
492
+
493
+ context "#sencha_get_fields_for_fieldset" do
494
+ should "return full list of columns for fieldset that was not defined, yet" do
495
+ @fields = BogusModel.sencha_get_fields_for_fieldset :not_there
496
+ assert_equal(BogusModel.process_fields(*BogusModel.sencha_column_names), @fields)
497
+ end
498
+ should "return the right fields for a fieldset that was defined before in the same class" do
499
+ BogusModel.sencha_fieldset :fieldset_was_defined, [:one]
500
+ @fields = BogusModel.sencha_get_fields_for_fieldset :fieldset_was_defined
501
+ assert_equal(BogusModel.process_fields(:one), @fields)
502
+ end
503
+ should "return the fieldset of the ancestor when it was only defined in the ancestor" do
504
+ BogusModel.sencha_fieldset :fieldset_was_defined_in_ancestor, [:one]
505
+ @fields = BogusModelChild.sencha_get_fields_for_fieldset :fieldset_was_defined_in_ancestor
506
+ assert_equal(BogusModel.process_fields(:one), @fields)
507
+ end
508
+ should "return the fieldset of the child when it was defined in the child and the ancestor" do
509
+ BogusModel.sencha_fieldset :fieldset_was_defined_in_both, [:one]
510
+ BogusModelChild.sencha_fieldset :fieldset_was_defined_in_both, [:two]
511
+ @fields = BogusModelChild.sencha_get_fields_for_fieldset :fieldset_was_defined_in_both
512
+ assert_equal(BogusModel.process_fields(:two), @fields)
513
+ end
514
+ end
515
+ end
516
+
517
+ protected
518
+ def assert_array_has_item array, item_description, &blk
519
+ assert array.find {|i| blk.call(i) }, "The array #{array.inspect} should #{item_description} but it does not"
520
+ end
521
+ def assert_array_has_not_item array, item_description, &blk
522
+ assert !array.find {|i| blk.call(i) }, "The array #{array.inspect} should not #{item_description} but it does"
523
+ end
524
+
525
+ end
526
+