has-many-with-set 1.0.0 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,6 @@
1
+ has-many-with-set 1.1.0
2
+
3
+ - Children can now see their parents.
4
+ - The README now uses Ruby 1.9 features.
5
+ - Fixed a bug with the relationships table. By default, Rails name has_and_belongs_to_many relationship tables in alphabetical order. This breaks the save callback if your parent > child.
6
+ - Added CHANGELOG.
data/README.textile CHANGED
@@ -16,11 +16,11 @@ The _join_ table model is a very redundant way of storing these relationships if
16
16
 
17
17
  For example:
18
18
 
19
- bc.. Tag.new(:name => 'programming').save
20
- Tag.new(:name => 'open source').save
21
- Tag.new(:name => 'startups').save
22
- Tag.new(:name => 'ruby').save
23
- Tag.new(:name => 'development').save
19
+ bc.. Tag.create(:name => 'programming')
20
+ Tag.create(:name => 'open source')
21
+ Tag.create(:name => 'startups')
22
+ Tag.create(:name => 'ruby')
23
+ Tag.create(:name => 'development')
24
24
 
25
25
  tags = Tag.all
26
26
 
@@ -28,12 +28,8 @@ tags = Tag.all
28
28
  a = Article.new(:title => "Buzzword about buzzwords!",
29
29
  :body => "Lorem ipsum")
30
30
 
31
- rand(tags.size + 1).times do
32
- t = tags[rand(tags.size)]
33
- a.tags << t
34
- end
31
+ a.tags = tags.sample(rand(tags.size + 1))
35
32
 
36
- a.tags.uniq!
37
33
  a.save
38
34
  end
39
35
 
@@ -87,11 +83,11 @@ bc.. class Article < ActiveRecord::Base
87
83
  has_many_with_set :tags # <--- key part!
88
84
  end
89
85
 
90
- Tag.new(:name => 'programming').save
91
- Tag.new(:name => 'open source').save
92
- Tag.new(:name => 'startups').save
93
- Tag.new(:name => 'ruby').save
94
- Tag.new(:name => 'development').save
86
+ Tag.create(:name => 'programming')
87
+ Tag.create(:name => 'open source')
88
+ Tag.create(:name => 'startups')
89
+ Tag.create(:name => 'ruby')
90
+ Tag.create(:name => 'development')
95
91
 
96
92
  tags = Tag.all
97
93
 
@@ -99,12 +95,8 @@ tags = Tag.all
99
95
  a = Article.new(:title => "Buzzword about buzzwords!",
100
96
  :body => "Lorem ipsum")
101
97
 
102
- rand(tags.size + 1).times do
103
- t = tags[rand(tags.size)]
104
- a.tags << t
105
- end
98
+ a.tags = tags.sample(rand(tags.size + 1))
106
99
 
107
- a.tags.uniq!
108
100
  a.save
109
101
  end
110
102
 
@@ -119,6 +111,14 @@ Article.first.tags
119
111
  Article.last.tags
120
112
  => [#<Tag id: 1, name: "programming", ...>, #<Tag id: 5, name: "development", ...]
121
113
 
114
+ # The child model can also see to which parent models it relates to
115
+
116
+ Tag.first.articles.size
117
+ => 503
118
+
119
+ Tag.first.article.first
120
+ => #<Article id: 2, title: "Buzzword about buzzwords!", ..>
121
+
122
122
  p. Same example as before, just now using *_has_many_with_set_*. We get the impressive number of 80 rows to represent the same information that we had before with thousands of rows (roughly the same, since we use random combinations is not _exactly_ the same article/tag layout).
123
123
 
124
124
  The funny thing in this particular example, is that since we have only five tags, there are only 32 possible ways to combine five tags together, these 32 combinations amount to 80 rows in our relationship table.... that is, even if we had a million articles we would still have the same 80 rows to represent our relationships, we don't need to create any more rows!!
@@ -41,5 +41,17 @@ module HasManyWithSet
41
41
  instance_variable_set(instance_var_name, elements)
42
42
  }
43
43
  end
44
+
45
+ def self.build_parent_loader_method (parent_table_name, child_table_name, set_table_name, set_items_table_name)
46
+ find_query = Queries.build_find_parents_query(parent_table_name, child_table_name, set_table_name, set_items_table_name)
47
+
48
+ parent_klass = Object.const_get(parent_table_name.classify)
49
+
50
+ Proc.new {
51
+ values = []
52
+ values = parent_klass.find_by_sql([ find_query, self ]).to_a
53
+ values
54
+ }
55
+ end
44
56
  end
45
57
  end
@@ -13,14 +13,18 @@ module HasManyWithSet
13
13
  set_model_name = set_table_name.classify
14
14
  set_items_table_name = "#{ set_table_name }_#{ child_table_name }"
15
15
  instance_var_name = "@#{ child_table_name }"
16
+ child_instance_var_name = "@#{ parent_table_name }"
16
17
  setter_method_name = "#{ child_table_name }="
17
18
  loader_method_name = "#{ set_items_table_name }_loader"
19
+ parent_loader_method_name = "#{ parent_table_name }_loader"
18
20
  save_callback_method_name = "#{ set_items_table_name }_save_callback"
21
+ child_klass = Object.const_get(child_model_name)
19
22
 
20
23
  Relationships.create_set_model(set_model_name)
21
24
  Relationships.relate_child_to_set(set_model_name, child_model_name)
22
25
  Relationships.relate_parent_to_set(set_model_name, parent_model_name)
23
26
 
27
+ # Parent methods
24
28
  define_method(loader_method_name,
25
29
  Accessors.build_loader_method(child_table_name, set_table_name))
26
30
 
@@ -34,6 +38,16 @@ module HasManyWithSet
34
38
  Callbacks.build_saver_callback(set_table_name, set_items_table_name,
35
39
  child_table_name, instance_var_name))
36
40
 
41
+ # Child methods
42
+ child_klass.send(:define_method, parent_loader_method_name,
43
+ Accessors.build_parent_loader_method(parent_table_name, child_table_name,
44
+ set_table_name, set_items_table_name))
45
+
46
+ child_klass.send(:define_method, parent_table_name,
47
+ Accessors.build_getter_method(child_instance_var_name, parent_loader_method_name))
48
+
49
+ child_klass.send(:private, parent_loader_method_name)
50
+
37
51
  class_eval do
38
52
  private loader_method_name
39
53
  private save_callback_method_name
@@ -3,7 +3,7 @@ module HasManyWithSet
3
3
  def self.build_find_empty_set_query(set_table_name, set_items_table_name)
4
4
  "select #{ set_table_name }.id from #{ set_table_name }
5
5
  where not exists (select null from #{ set_items_table_name }
6
- where #{ set_items_table_name }.#{ set_table_name.singularize }_id = #{ set_table_name }.id)"
6
+ where #{ set_items_table_name }.#{ set_table_name.singularize }_id = #{ set_table_name }.id);"
7
7
  end
8
8
 
9
9
  def self.build_find_set_query(set_table_name, set_items_table_name, child_table_name)
@@ -16,7 +16,15 @@ module HasManyWithSet
16
16
  where c.#{ set_table_name.singularize }_id =
17
17
  #{ set_items_table_name }.#{ set_table_name.singularize }_id) = ?
18
18
  group by #{ set_table_name }.id
19
- having count(*) = ?"
19
+ having count(*) = ?;"
20
+ end
21
+
22
+ def self.build_find_parents_query (parent_table_name, child_table_name, set_table_name, set_items_table_name)
23
+ "select #{ parent_table_name }.* from #{ child_table_name }
24
+ join #{ set_items_table_name } on #{ child_table_name }.id = #{ set_items_table_name }.#{ child_table_name.singularize }_id
25
+ join #{ parent_table_name } on #{ parent_table_name }.#{ set_table_name.singularize }_id =
26
+ #{ set_items_table_name }.#{ set_table_name.singularize}_id
27
+ where #{ child_table_name }.id = ?;"
20
28
  end
21
29
  end
22
30
  end
@@ -8,12 +8,12 @@ module HasManyWithSet
8
8
  def self.relate_child_to_set (set_model_name, child_model_name)
9
9
  # Take the child model and add a regular many-to-many relationship to the Set model...
10
10
  Object.const_get(child_model_name).class_eval do
11
- has_and_belongs_to_many set_model_name.tableize.to_sym
11
+ has_and_belongs_to_many set_model_name.tableize.to_sym, :join_table => "#{ set_model_name.tableize }_#{ child_model_name.tableize }"
12
12
  end
13
13
 
14
14
  # ... and take the Set model and finish the many-to-many relationship.
15
15
  Object.const_get(set_model_name).class_eval do
16
- has_and_belongs_to_many child_model_name.tableize.to_sym
16
+ has_and_belongs_to_many child_model_name.tableize.to_sym, :join_table => "#{ set_model_name.tableize }_#{ child_model_name.tableize }"
17
17
  end
18
18
  end
19
19
 
@@ -1,3 +1,3 @@
1
1
  module HasManyWithSet
2
- VERSION = "1.0.0"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -32,6 +32,10 @@ class HasManyWithSetTest < ActiveSupport::TestCase
32
32
  assert_respond_to ModelOne.new, "model_twos="
33
33
  end
34
34
 
35
+ test "child class has the getter" do
36
+ assert_respond_to ModelTwo.new, "model_ones"
37
+ end
38
+
35
39
  test "getter type" do
36
40
  assert_kind_of Array, ModelOne.new.model_twos
37
41
  end
@@ -111,6 +115,20 @@ class HasManyWithSetTest < ActiveSupport::TestCase
111
115
  assert master_record.model_twos.size == items.size
112
116
  end
113
117
 
118
+ test "children can see parents" do
119
+ item = ModelTwo.create
120
+
121
+ how_many = rand(50)
122
+
123
+ how_many.times do
124
+ record = ModelOne.new(:num => 1)
125
+ record.model_twos << item
126
+ record.save
127
+ end
128
+
129
+ assert item.model_ones.size == how_many, "#{ item.model_ones.size } != #{ how_many }"
130
+ end
131
+
114
132
  test "items count match" do
115
133
  ModelOne.all.each do |m|
116
134
  assert m.num == m.model_twos.size
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: has-many-with-set
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-13 00:00:00.000000000 Z
12
+ date: 2012-11-24 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -62,11 +62,12 @@ files:
62
62
  - MIT-LICENSE
63
63
  - Rakefile
64
64
  - README.textile
65
+ - CHANGELOG
65
66
  - test/has-many-with-set_test.rb
66
67
  - test/support/models.rb
67
68
  - test/support/prepare_activerecord.rb
68
69
  - test/test_helper.rb
69
- - test/tmp/db/migrate/20121113022938_create_model_ones_model_twos_set.rb
70
+ - test/tmp/db/migrate/20121124222345_create_model_ones_model_twos_set.rb
70
71
  homepage: https://github.com/ebobby/has-many-with-set
71
72
  licenses: []
72
73
  post_install_message:
@@ -81,7 +82,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
81
82
  version: '0'
82
83
  segments:
83
84
  - 0
84
- hash: 2377775004769566141
85
+ hash: 3966952451817173381
85
86
  required_rubygems_version: !ruby/object:Gem::Requirement
86
87
  none: false
87
88
  requirements:
@@ -90,7 +91,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
90
91
  version: '0'
91
92
  segments:
92
93
  - 0
93
- hash: 2377775004769566141
94
+ hash: 3966952451817173381
94
95
  requirements: []
95
96
  rubyforge_project:
96
97
  rubygems_version: 1.8.24
@@ -102,4 +103,4 @@ test_files:
102
103
  - test/support/models.rb
103
104
  - test/support/prepare_activerecord.rb
104
105
  - test/test_helper.rb
105
- - test/tmp/db/migrate/20121113022938_create_model_ones_model_twos_set.rb
106
+ - test/tmp/db/migrate/20121124222345_create_model_ones_model_twos_set.rb