acts_as_tableless 0.0.3 → 0.1.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.
@@ -2,10 +2,87 @@
2
2
 
3
3
  Create tableless activerecord objects that support associations and validations.
4
4
 
5
- 1. Install gem
6
- 2. Add <tt>acts_as_tableless</tt> to a activerecord model
5
+ == Install:
7
6
 
8
- Note:
7
+ 1. add <tt>gem 'acts_as_tableless'</tt> to your gemfile
8
+ 2. run <tt>bundle install</tt>
9
+ 3. add <tt>acts_as_tableless</tt> to a activerecord model
10
+
11
+ == Usage:
12
+
13
+ class User < ActiveRecord::Base
14
+ has_many :shapes
15
+ has_many :user_colors
16
+ has_many :colors, :through => :user_colors
17
+ has_one :size
18
+ belongs_to :number
19
+ attr_accessible :name, :number_id, :letter_id
20
+ end
21
+
22
+ class Color < ActiveRecord::Base
23
+ acts_as_tableless
24
+ column :id, :integer
25
+ column :name, :string
26
+ attr_accessible :id, :name
27
+ belongs_to :user_colors
28
+ end
29
+
30
+ class Letter < ActiveRecord::Base
31
+ acts_as_tableless
32
+ column :id, :integer
33
+ column :name, :string
34
+ column :user_id, :integer
35
+ attr_accessible :id, :name, :user_id
36
+ has_and_belongs_to_many :users
37
+ end
38
+
39
+ class Number < ActiveRecord::Base
40
+ acts_as_tableless
41
+ column :id, :integer
42
+ column :name, :string
43
+ attr_accessible :id, :name
44
+ has_many :users
45
+ end
46
+
47
+ class Shape < ActiveRecord::Base
48
+ acts_as_tableless
49
+ column :id, :integer
50
+ column :name, :string
51
+ column :user_id, :integer
52
+ attr_accessible :id, :name, :user_id
53
+ belongs_to :user
54
+ end
55
+
56
+ class Size < ActiveRecord::Base
57
+ acts_as_tableless
58
+ column :id, :integer
59
+ column :name, :string
60
+ column :user_id, :integer
61
+ attr_accessible :id, :name, :user_id
62
+ belongs_to :user
63
+ end
64
+
65
+ class UserColor < ActiveRecord::Base
66
+ belongs_to :user
67
+ belongs_to :color
68
+ attr_accessible :user_id, :color_id
69
+ end
70
+
71
+ == Examples:
72
+
73
+ @user = User.create(:name => "User")
74
+
75
+ @user.shapes.create(:name => "Octagon") #=> #<Shape id: 1, name: "Octagon", user_id: 1>
76
+
77
+ @user.colors << Color.create([{:name => "Green"},{:name => "Blue"}]) #=> [#<Color id: 1, name: "Green">, #<Color id: 2, name: "Blue">]
78
+
79
+ @user.size.create(:name => "Large") #=> #<Size id: 1, name: "Large", user_id: 1>
80
+
81
+ @user.number = Number.create(name: "One") #=> #<Number id: 1, name: "One">
82
+
83
+ More stuff in the test file.
84
+
85
+ == Notes:
9
86
 
10
87
  This gem relies on String#singularize which is known to have some problems. To correct errors such as:
11
88
 
@@ -18,4 +95,4 @@ please add something similar to:
18
95
  inflect.singular("status", "status")
19
96
  end
20
97
 
21
- to your environment.rb file before the <tt>Application.initialize!</tt> line
98
+ to your environment.rb file before the <tt>Application.initialize!</tt> line
@@ -37,8 +37,25 @@ module ActsAsTableless
37
37
  def all
38
38
  ActsAsTableless.class_variable_get(:"@@#{self.name.underscore}")
39
39
  end
40
+
41
+ def find(id)
42
+ ActsAsTableless.class_variable_get(:"@@#{self.name.underscore}").select{|record| record.id == id}.first
43
+ end
44
+
45
+ def delete(ids)
46
+ ids = [ids] unless ids.is_a?(Array)
47
+ # this coule be improved
48
+ ids.each do |id|
49
+ find(id).delete
50
+ end
51
+ end
52
+
53
+ def exists?(id)
54
+ find(id).nil? ? false : true
55
+ end
40
56
  end
41
57
  ActsAsTableless.class_variable_set(:"@@#{self.name.underscore}", [])
58
+ ActsAsTableless.class_variable_set(:"@@#{self.name.underscore}_increment", 1)
42
59
  include InstanceMethods
43
60
  end
44
61
  end
@@ -47,18 +64,31 @@ module ActsAsTableless
47
64
  def persisted?
48
65
  false
49
66
  end
50
-
67
+
51
68
  def readonly?
52
- false
69
+ true
53
70
  end
54
71
 
55
72
  def save(validate = true)
73
+ unless self.id
74
+ id = ActsAsTableless.class_variable_get(:"@@#{self.class.name.underscore}_increment")
75
+ id += 1 while self.class.send(:find, id)
76
+ self.id = ActsAsTableless.class_variable_set(:"@@#{self.class.name.underscore}_increment", id)
77
+ end
78
+ raise "Duplicate ID" if self.class.send(:find, id) # these are read only
56
79
  if validate ? valid? : true
57
- ActsAsTableless.class.class_variable_set(:"@@#{self.class.name.underscore}", ActsAsTableless.class_variable_get(:"@@#{self.class.name.underscore}").push(self))
80
+ ActsAsTableless.class_variable_get(:"@@#{self.class.name.underscore}").push(self)
81
+ return self
58
82
  end
59
83
  end
60
84
 
85
+ def delete
86
+ ActsAsTableless.class_variable_get(:"@@#{self.class.name.underscore}").delete(self)
87
+ return true
88
+ end
89
+
61
90
  alias :save! :save
91
+ alias :delete! :delete
62
92
  end
63
93
  end
64
94
 
@@ -84,7 +114,8 @@ module ActiveRecord
84
114
  []
85
115
  else
86
116
  through_objects = [through_objects] unless through_objects.is_a?(Array)
87
- through_objects.collect{|object| association_class.find(object.send("#{association_class.name.underscore}_id")) }
117
+ through_association_id = [:has_one, :belongs_to].include?( options[:through].to_s.singularize.camelize.constantize.reflect_on_all_associations.select{ |associations| associations.name.to_s == class_variable_name }.first.macro ) ? association_id.to_s.singularize.to_sym : association_id
118
+ through_objects.collect{|object| object.send(through_association_id) }.flatten
88
119
  end
89
120
 
90
121
  records.instance_variable_set(:@parent, self)
@@ -96,7 +127,7 @@ module ActiveRecord
96
127
  when :has_many
97
128
  associated_records.each do |associated_record|
98
129
  raise ActiveRecord::AssociationTypeMismatch, "#{@association_class.name} expected, got #{associated_record.inspect}" unless @association_class.name == associated_record.class.name
99
- @parent.send(@options[:through]).send(:new, "#{@association_class.name.underscore}_id".to_sym => associated_record.id)
130
+ @options[:through].to_s.singularize.camelize.constantize.create("#{@parent.class.name.underscore}_id".to_sym => @parent.id, "#{@association_class.name.underscore}_id".to_sym => associated_record.id)
100
131
  end
101
132
  when :has_one
102
133
  # not yet implemented
@@ -108,23 +139,94 @@ module ActiveRecord
108
139
  # not yet implemented
109
140
  []
110
141
  end
111
- @parent.roles
142
+ self
112
143
  end
113
- # def records.create(attributes = nil, options = {})
114
- # if attributes.is_a?(Array)
115
- # attributes.collect { |record_attributes| create(record_attributes, options) }
116
- # else
117
- # record = new(attributes, options)
118
- # record.save
119
- # record
120
- # end
121
- # end
122
-
144
+ def records.create(new_records_attributes = nil, options = {})
145
+ new_records = new_records_attributes.is_a?(Array) ? [] : nil
146
+ new_records_attributes = [new_records_attributes] unless new_records_attributes.is_a?(Array)
147
+ case @parent.class.reflect_on_all_associations.select{|association| association.name == @options[:through]}.first.macro
148
+ when :has_many
149
+ new_records_attributes = [new_records_attributes] unless new_records_attributes.is_a?(Array)
150
+ new_records = []
151
+ new_records_attributes.each do |attributes|
152
+ new_record = @association_class.create(attributes)
153
+ @options[:through].to_s.singularize.camelize.constantize.create("#{@parent.class.name.underscore}_id".to_sym => @parent.id, "#{@association_class.name.underscore}_id".to_sym => new_record.id)
154
+ if new_records.is_a?(Array)
155
+ new_records << new_record
156
+ else
157
+ return new_record
158
+ end
159
+ end
160
+ when :has_one
161
+ # not yet implemented
162
+ []
163
+ when :belongs_to
164
+ # not yet implemented
165
+ []
166
+ when :has_and_belongs_to_many
167
+ # not yet implemented
168
+ []
169
+ end
170
+ return new_records
171
+ end
172
+
123
173
  return records
124
174
  end
175
+
176
+ define_method("#{association_id.to_s}=") do |associated_records|
177
+ new_associated_records = associated_records.is_a?(Array) ? [] : nil
178
+ associated_records = [associated_records] unless associated_records.is_a?(Array)
179
+ through_class = options[:through].to_s.singularize.camelize.constantize
180
+ through_class.all.select{|r|r.send("#{self.class.name.underscore}_id") == self.id}.each{|r|r.delete} # this line could be made more efficient
181
+ case self.class.reflect_on_all_associations.select{|association| association.name == options[:through]}.first.macro
182
+ when :has_many
183
+ associated_records.each do |associated_record|
184
+ new_associated_record = through_class.new
185
+ new_associated_record.send("#{self.class.name.underscore}_id=", self.id)
186
+ new_associated_record.send("#{association_class.name.underscore}_id=", associated_record.id)
187
+ new_associated_record.save
188
+ if new_associated_records.is_a?(Array)
189
+ new_associated_records << new_associated_record
190
+ else
191
+ return new_associated_record
192
+ end
193
+ end
194
+ when :has_one
195
+ # not yet implemented
196
+ []
197
+ when :belongs_to
198
+ # not yet implemented
199
+ []
200
+ when :has_and_belongs_to_many
201
+ # not yet implemented
202
+ []
203
+ end
204
+ return new_associated_records
205
+ end
206
+
125
207
  else
126
208
  define_method(association_id.to_s) do
127
- association_class.all.select{|record| record.send("#{self.class.name.underscore}_id") == self.id}
209
+ records = association_class.all.select{|record| record.send("#{self.class.name.underscore}_id") == self.id}
210
+
211
+ records.instance_variable_set(:@parent, self)
212
+ records.instance_variable_set(:@association_class, association_class)
213
+ def records.create(new_records_attributes = nil)
214
+ new_records = new_records_attributes.is_a?(Array) ? [] : nil
215
+ new_records_attributes = [new_records_attributes] unless new_records_attributes.is_a?(Array)
216
+ new_records_attributes.each do |attributes|
217
+ new_record = @association_class.new(attributes)
218
+ new_record.send("#{@parent.class.name.underscore}_id=", @parent.id)
219
+ new_record.save
220
+ if new_records.is_a?(Array)
221
+ new_records << new_record
222
+ else
223
+ return new_record
224
+ end
225
+ end
226
+ return new_records
227
+ end
228
+
229
+ return records
128
230
  end
129
231
  end
130
232
  end
@@ -143,7 +245,18 @@ module ActiveRecord
143
245
  end
144
246
  else
145
247
  define_method(association_id.to_s) do
146
- association_class.all.select{|record| record.send("#{self.class.name.underscore}_id") == self.id}.first
248
+ record = association_class.all.select{|r|r.send("#{self.class.name.underscore}_id") == self.id}.first
249
+ record.instance_variable_set(:@parent, self)
250
+ record.instance_variable_set(:@association_class, association_class)
251
+ def record.create(attributes)
252
+ old_record = @parent.send(@association_class.name.underscore)
253
+ new_record = @association_class.new(attributes)
254
+ new_record.send("#{@parent.class.name.underscore}_id=", @parent.id)
255
+ new_record.save
256
+ old_record.delete unless old_record.nil?
257
+ return new_record
258
+ end
259
+ return record
147
260
  end
148
261
  end
149
262
  end
@@ -155,7 +268,7 @@ module ActiveRecord
155
268
  if class_variable_name.camelize.constantize.send(:included_modules).include?(ActsAsTableless) # || ActsAsTableless.class_variables.include?(:"@@#{class_variable_name}")
156
269
  association_class = class_variable_name.camelize.constantize rescue nil
157
270
  define_method(association_id.to_s) do
158
- association_class.find(self.send("#{association_class.name.underscore}_id"))
271
+ association_class.all.select{|record| record.id == self.send("#{association_class.name.underscore}_id")}.first
159
272
  end
160
273
  end
161
274
  end
@@ -165,8 +278,10 @@ module ActiveRecord
165
278
  class_variable_name = association_id.to_s.singularize
166
279
  if class_variable_name.camelize.constantize.send(:included_modules).include?(ActsAsTableless) # || ActsAsTableless.class_variables.include?(:"@@#{class_variable_name}")
167
280
  association_class = class_variable_name.camelize.constantize rescue nil
168
- # not yet implemented
169
- []
281
+ # not yet implemented, and may never be; use has_many
282
+ define_method(association_id.to_s) do
283
+ []
284
+ end
170
285
  end
171
286
  end
172
287
  end
@@ -1,3 +1,3 @@
1
1
  module ActsAsTableless
2
- VERSION = "0.0.3"
2
+ VERSION = "0.1.0"
3
3
  end
@@ -2,11 +2,14 @@ require 'test_helper'
2
2
 
3
3
  class ActsAsTablelessTest < ActiveSupport::TestCase
4
4
  def setup
5
- build_tableless_objects
5
+ build_users
6
+
7
+ @user = @user_one
6
8
  end
7
9
 
8
- test "acts_as_tableless_active_record_methods" do
9
- tabless_models = [HasManyTablelessObject]
10
+ test "active_record_methods" do
11
+ build_all
12
+ tabless_models = [Color, Letter, Number, Shape, Size]
10
13
 
11
14
  tabless_models.each do |tabless_model|
12
15
  assert tabless_model.table_name
@@ -19,47 +22,172 @@ class ActsAsTablelessTest < ActiveSupport::TestCase
19
22
 
20
23
  tableless_object = tabless_model.all.select{|object|object.id == 1}.first
21
24
  assert !tableless_object.persisted?
22
- assert !tableless_object.readonly?
23
- assert_equal "default", tableless_object.test
24
- tableless_object.test = "update"
25
- tableless_object.save!
26
- assert_equal "update", tableless_object.test
25
+ assert tableless_object.readonly?
26
+ end
27
+ end
28
+
29
+ test "has_many" do
30
+ # has_many :shapes
31
+ build_shapes
32
+ assert @user.present?
33
+ assert @user.shapes.present?
34
+ assert_equal 2, @user.shapes.count
35
+ assert ["Circle", "Square"].sort.eql?(@user.shapes.collect(&:name).sort)
36
+
37
+ @user.shapes.create(:name => "Octagon")
38
+ assert_equal 3, @user.shapes.count
39
+ assert ["Circle", "Octagon", "Square"].sort.eql?(@user.shapes.collect(&:name).sort)
40
+ end
41
+
42
+ test "has_many_through" do
43
+ # has_many :user_colors
44
+ # has_many :colors, :through => :user_colors
45
+ build_colors
46
+ build_user_colors
47
+ assert @user
48
+ assert @user.user_colors
49
+ assert @user.colors
50
+ assert_equal 3, @user.colors.count
51
+ assert ["Blue", "Purple", "Red"].sort.eql?(@user.colors.collect(&:name).sort)
52
+
53
+ @user.colors << Color.all.select{|c|c.name == "Green"}
54
+ assert ["Blue", "Green", "Purple", "Red"].sort.eql?(@user.colors.collect(&:name).sort)
55
+
56
+ @user.colors.create(:name => "Orange")
57
+ assert ["Blue", "Green", "Orange", "Purple", "Red"].sort.eql?(@user.colors.collect(&:name).sort)
58
+
59
+ @user.colors = Color.all.select{|c| ["Yellow", "Blue"].include?(c.name)}
60
+ assert ["Blue", "Yellow"].sort.eql?(@user.colors.collect(&:name).sort)
61
+ end
62
+
63
+ test "has_one_association" do
64
+ # has_one :size
65
+ build_sizes
66
+ assert @user
67
+ assert @user.size
68
+ assert_equal "Small", @user.size.name
69
+ assert Size.all.collect(&:name).include?("Small")
70
+ assert !Size.all.collect(&:name).include?("X-Large")
71
+
72
+ @user.size.create(:name => "X-Large")
73
+ assert_equal "X-Large", @user.size.name
74
+ assert Size.all.collect(&:name).include?("X-Large")
75
+ assert !Size.all.collect(&:name).include?("Small")
76
+ end
77
+
78
+ test "belongs_to_association" do
79
+ # belongs_to :number
80
+ build_numbers
81
+ assert @user
82
+ assert @user.number
83
+ assert_equal "One", @user.number.name
84
+
85
+ @user.number = @@two
86
+ @user.save
87
+ assert_equal "Two", @user.number.name
88
+ end
89
+
90
+ # this is not yet implemented, and may never be; use has_many
91
+ test "has_and_belongs_to_many_association" do
92
+ # has_and_belongs_to_many :letters
93
+ build_letters
94
+ assert @user
95
+ assert @user.letters
96
+ # assert_equal 2, @user.letters.count
97
+ # assert ["A", "O"].sort.eql? @user.letters.collect(&:name).sort
98
+
99
+ # @user.letters.create(:name => "Z")
100
+ # assert_equal 3, @user.letters.count
101
+ # assert ["A", "O", "Z"].sort.eql?(@user.letters.collect(&:name).sort)
102
+ end
103
+
104
+ def build_all
105
+ build_colors
106
+ build_letters
107
+ build_numbers
108
+ build_shapes
109
+ build_sizes
110
+ build_users
111
+ build_user_colors
112
+ end
113
+
114
+ def build_colors
115
+ if Color.all.none?
116
+ @@red = Color.create(name: "Red")
117
+ @@yellow = Color.create(name: "Yellow")
118
+ @@blue = Color.create(name: "Blue")
119
+ @@green = Color.create(name: "Green")
120
+ @@purple = Color.create(name: "Purple")
27
121
  end
28
122
  end
29
123
 
30
- test "acts_as_tableless_active_record_association_has_many" do
31
- assert_equal HasManyTablelessObject.all.select{|o|o.active_record_modle_with_association_id = @active_record_modle_with_association.id}.count, @active_record_modle_with_association.has_many_tableless_objects.count
124
+ def build_letters
125
+ if Letter.all.none?
126
+ build_users
127
+ @@a = Letter.create(name: "A", user_id: @user_one.id)
128
+ @@e = Letter.create(name: "E", user_id: @user_two.id)
129
+ @@i = Letter.create(name: "I", user_id: @user_three.id)
130
+ @@o = Letter.create(name: "O", user_id: @user_one.id)
131
+ @@u = Letter.create(name: "U", user_id: @user_two.id)
132
+ @@y = Letter.create(name: "Y", user_id: @user_three.id)
133
+ end
32
134
  end
33
135
 
34
- test "acts_as_tableless_active_record_association_has_one" do
35
- # not yet tested
36
- assert true
136
+ def build_numbers
137
+ if Number.all.none?
138
+ @@one = Number.create(name: "One")
139
+ @@two = Number.create(name: "Two")
140
+ @@three = Number.create(name: "Three")
141
+ @@four = Number.create(name: "Four")
142
+ @@five = Number.create(name: "Five")
143
+ @@six = Number.create(name: "Six")
144
+ @@seven = Number.create(name: "Seven")
145
+ @@eight = Number.create(name: "Eight")
146
+ @@nine = Number.create(name: "Nine")
147
+ @@zero = Number.create(name: "Zero")
148
+ end
37
149
  end
38
150
 
39
- test "acts_as_tableless_active_record_association_belongs_to" do
40
- # not yet tested
41
- assert true
151
+ def build_shapes
152
+ if Shape.all.none?
153
+ build_users
154
+ @@square = Shape.create(name: "Square", user_id: @user_one.id)
155
+ @@rectangle = Shape.create(name: "Rectangle", user_id: @user_two.id)
156
+ @@triangle = Shape.create(name: "Triangle", user_id: @user_three.id)
157
+ @@circle = Shape.create(name: "Circle", user_id: @user_one.id)
158
+ @@hexagon = Shape.create(name: "Hexagon", user_id: @user_two.id)
159
+ end
42
160
  end
43
161
 
44
- test "acts_as_tableless_active_record_association_has_and_belongs_to_many" do
45
- # not yet tested
46
- assert true
162
+ def build_sizes
163
+ if Size.all.none?
164
+ build_users
165
+ @@small = Size.create(name: "Small", user_id: @user_one.id)
166
+ @@medium = Size.create(name: "Meduim", user_id: @user_two.id)
167
+ @@large = Size.create(name: "Large", user_id: @user_three.id)
168
+ end
47
169
  end
48
170
 
49
- test "acts_as_tableless_active_record_validations" do
50
- # not yet tested
51
- assert true
171
+ def build_users
172
+ if User.all.none?
173
+ build_numbers
174
+ @user_one = User.create(name: "User One", number_id: @@one.id)
175
+ @user_two = User.create(name: "User Two", number_id: @@two.id)
176
+ @user_three = User.create(name: "User Three", number_id: @@three.id)
177
+ end
52
178
  end
53
179
 
54
- def build_tableless_objects
55
- @active_record_modle_with_association = ActiveRecordModleWithAssociation.create(id: 1, name: 'default')
56
-
57
- HasManyTablelessObject.create([
58
- {id: 1, test: 'default', active_record_modle_with_association_id: @active_record_modle_with_association.id},
59
- {id: 2, test: 'default', active_record_modle_with_association_id: @active_record_modle_with_association.id},
60
- {id: 3, test: 'default', active_record_modle_with_association_id: @active_record_modle_with_association.id},
61
- {id: 4, test: 'default', active_record_modle_with_association_id: @active_record_modle_with_association.id},
62
- {id: 5, test: 'default', active_record_modle_with_association_id: @active_record_modle_with_association.id},
63
- ])
180
+ def build_user_colors
181
+ if UserColor.all.none?
182
+ build_users
183
+ build_colors
184
+ UserColor.create([
185
+ {user_id: @user_one.id, color_id: @@red.id},
186
+ {user_id: @user_two.id, color_id: @@yellow.id},
187
+ {user_id: @user_one.id, color_id: @@blue.id},
188
+ {user_id: @user_two.id, color_id: @@green.id},
189
+ {user_id: @user_one.id, color_id: @@purple.id},
190
+ ])
191
+ end
64
192
  end
65
193
  end