slim-attributes 0.6.0 → 0.6.4

Sign up to get free protection for your applications and to get access to all the features.
data/README CHANGED
@@ -11,6 +11,8 @@ Measuring with just ActiveRecord code - fetching stuff from the database - we se
11
11
  Installation
12
12
  ============
13
13
 
14
+ Slim attributes works with ruby 1.8.6+ and 1.9.1+.
15
+
14
16
  You're going to need the mysql headers for this to work.
15
17
 
16
18
  Try:
@@ -41,4 +43,12 @@ The field contents are then instantiated into Ruby strings on demand - ruby stri
41
43
  No warranty - this gem has been tested, but not in all environments. However, that said, we are using it in our production environment with good results.
42
44
  Please report bugs to sdsykes at symbol gmail pip com.
43
45
 
44
- Copyright (c) 2008 Stephen Sykes, released under the MIT license
46
+
47
+ Credits
48
+ =======
49
+
50
+ Thanks to IronDigital for the initial port for ruby 1.9.
51
+ Thanks to Greg Hazel for fixing compilation for windows.
52
+
53
+
54
+ Copyright (c) 2008-2009 Stephen Sykes, released under the MIT license
@@ -0,0 +1,19 @@
1
+ require 'rake'
2
+
3
+ begin
4
+ require 'jeweler'
5
+ Jeweler::Tasks.new do |s|
6
+ s.name = "slim-attributes"
7
+ s.summary = "Slim-attributes - lazy instantiation of attributes for ActiveRecord"
8
+ s.email = "sdsykes@gmail.com"
9
+ s.homepage = "http://github.com/sdsykes/slim-attributes"
10
+ s.description = "Slim attributes boosts speed in Rails/Mysql ActiveRecord Models by avoiding
11
+ instantiating Hashes for each result row, and lazily instantiating attributes as needed."
12
+ s.authors = ["Stephen Sykes"]
13
+ s.files = FileList["[A-Z]*", "{ext,lib,test}/**/*"]
14
+ s.extensions = "ext/extconf.rb"
15
+ end
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://
18
+ gems.github.com"
19
+ end
@@ -0,0 +1,4 @@
1
+ ---
2
+ :patch: 4
3
+ :major: 0
4
+ :minor: 6
@@ -2,7 +2,12 @@
2
2
  // http://pennysmalls.com
3
3
 
4
4
  #include "ruby.h"
5
+
6
+ #ifdef HAVE_RUBY_ST_H
7
+ #include "ruby/st.h"
8
+ #else
5
9
  #include "st.h"
10
+ #endif
6
11
 
7
12
  #include <mysql.h>
8
13
  #include <errmsg.h>
@@ -152,14 +157,18 @@ static VALUE set_element(VALUE obj, VALUE name, VALUE val) {
152
157
  // []= before calling dup (the original values will be seen in the dup). This works ok
153
158
  // for active record usage, but perhaps could cause unexpected behaviour if model
154
159
  // attributes are dupped by the user after changing them.
155
- static VALUE dup(VALUE obj) {
160
+ static VALUE slim_dup(VALUE obj) {
156
161
  VALUE frh, field_indexes;
157
162
  int nf, i;
158
163
  char *row_info_space;
159
164
 
160
165
  if (REAL_HASH_EXISTS) return rb_obj_dup(rb_ivar_get(obj, real_hash_id));
161
166
 
167
+ #ifdef RHASH_SIZE
168
+ nf = RHASH_SIZE(field_indexes);
169
+ #else
162
170
  nf = RHASH(field_indexes)->tbl->num_entries;
171
+ #endif
163
172
  row_info_space = ruby_xmalloc(nf); // dup needs its own set of flags
164
173
  if (!row_info_space) rb_raise(rb_eNoMemError, "out of memory");
165
174
  memcpy(row_info_space, GetCharPtr(rb_ivar_get(obj, row_info_id)), nf);
@@ -177,8 +186,13 @@ static VALUE dup(VALUE obj) {
177
186
  static VALUE has_key(VALUE obj, VALUE name) {
178
187
  VALUE field_indexes;
179
188
 
189
+ #ifdef RHASH_TBL
190
+ if (REAL_HASH_EXISTS) return (st_lookup(RHASH_TBL(rb_ivar_get(obj, real_hash_id)), name, 0) ? Qtrue : Qfalse);
191
+ else return (st_lookup(RHASH_TBL(field_indexes), name, 0) ? Qtrue : Qfalse);
192
+ #else
180
193
  if (REAL_HASH_EXISTS) return (st_lookup(RHASH(rb_ivar_get(obj, real_hash_id))->tbl, name, 0) ? Qtrue : Qfalse);
181
194
  else return (st_lookup(RHASH(field_indexes)->tbl, name, 0) ? Qtrue : Qfalse);
195
+ #endif
182
196
  }
183
197
 
184
198
  void Init_slim_attrib_ext() {
@@ -195,7 +209,7 @@ void Init_slim_attrib_ext() {
195
209
  rb_define_private_method(cRowHash, "fetch_by_index", (VALUE(*)(ANYARGS))fetch_by_index, 1);
196
210
  rb_define_method(cRowHash, "[]", (VALUE(*)(ANYARGS))slim_fetch, 1);
197
211
  rb_define_method(cRowHash, "[]=", (VALUE(*)(ANYARGS))set_element, 2);
198
- rb_define_method(cRowHash, "dup", (VALUE(*)(ANYARGS))dup, 0);
212
+ rb_define_method(cRowHash, "dup", (VALUE(*)(ANYARGS))slim_dup, 0);
199
213
  rb_define_method(cRowHash, "has_key?", (VALUE(*)(ANYARGS))has_key, 1);
200
214
  // set up some symbols that we will need
201
215
  pointers_id = rb_intern("@pointers");
@@ -0,0 +1,34 @@
1
+ # Author: Stephen Sykes
2
+ # http://pennysmalls.com
3
+
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require File.dirname(__FILE__) + "/products"
7
+ require File.dirname(__FILE__) + "/slim_db_test_utils"
8
+
9
+ SlimDbTestUtils.connect_and_create_db
10
+ Product.create_large_product_table
11
+ Product.make_some_products
12
+
13
+ def do_benchmark(name)
14
+ start_t = Time.now
15
+
16
+ 2000.times do |n|
17
+ product = Product.find(:all)[n % 100]
18
+ x = product.name
19
+ y = product.comment
20
+ z = product.created_at
21
+ end
22
+
23
+ end_t = Time.now
24
+ time_taken = end_t - start_t
25
+ puts "#{name}: #{time_taken}s"
26
+ time_taken
27
+ end
28
+
29
+ time1 = do_benchmark("Without slim-attributes")
30
+ require 'slim_attributes'
31
+ time2 = do_benchmark("With slim-attributes")
32
+ puts "Diff: #{"%.2f"%(100 - time2 / time1 * 100)}%"
33
+
34
+ SlimDbTestUtils.remove_db
@@ -0,0 +1,6 @@
1
+ slim_attributes_test:
2
+ adapter: mysql
3
+ database: slim_attributes_test
4
+ username: your_username
5
+ password: your_password
6
+ socket: /tmp/mysql.sock
@@ -0,0 +1,40 @@
1
+ # Author: Stephen Sykes
2
+ # http://pennysmalls.com
3
+
4
+ class Product < ActiveRecord::Base
5
+
6
+ def self.create_product_table
7
+ ActiveRecord::Base.connection.drop_table(:products) rescue ActiveRecord::StatementInvalid
8
+ ActiveRecord::Base.connection.create_table(:products) do |t|
9
+ t.column :name, :string, :limit => 60
10
+ t.column :created_at, :datetime
11
+ t.column :number, :integer
12
+ t.column :nil_test, :string
13
+ t.column :comment, :text
14
+ end
15
+ end
16
+
17
+ def self.create_large_product_table
18
+ ActiveRecord::Base.connection.drop_table(:products) rescue ActiveRecord::StatementInvalid
19
+ ActiveRecord::Base.connection.create_table(:products) do |t|
20
+ t.column :name, :string, :limit => 60
21
+ t.column :created_at, :datetime
22
+ t.column :number, :integer
23
+ t.column :nil_test, :string
24
+ t.column :comment, :text
25
+ 40.times {|n| t.column "test_col_#{n}", :string}
26
+ end
27
+ end
28
+
29
+
30
+ def self.make_some_products
31
+ 100.times do |n|
32
+ Product.create(:name=>"product_#{n}", :number=>n, :comment=>"Made by the test suite")
33
+ end
34
+ end
35
+
36
+ def attributes_iv
37
+ @attributes
38
+ end
39
+
40
+ end
@@ -0,0 +1,169 @@
1
+ # Author: Stephen Sykes
2
+ # http://pennysmalls.com
3
+
4
+ require 'rubygems'
5
+ require 'active_record'
6
+ require 'active_record/version'
7
+ require 'slim_attributes'
8
+ require 'test/unit'
9
+ require File.dirname(__FILE__) + "/products"
10
+ require File.dirname(__FILE__) + "/slim_db_test_utils"
11
+
12
+ class SlimAttributesTest < Test::Unit::TestCase
13
+ def setup
14
+ SlimDbTestUtils.connect_and_create_db
15
+ Product.create_product_table
16
+ Product.make_some_products
17
+ end
18
+
19
+ def test_finds_all
20
+ items = Product.find(:all)
21
+ assert items.size == 100, "must find all 100 items"
22
+ end
23
+
24
+ def test_items_have_correct_attributes
25
+ items = Product.find(:all, :order=>"id")
26
+ items.each_index do |i|
27
+ check_attributes_for(items[i], i)
28
+ end
29
+ end
30
+
31
+ def test_item_attributes_can_be_changed
32
+ item = Product.find(:first)
33
+ old_name = item.name.dup
34
+ item.name << "more"
35
+ assert_equal old_name + "more", item.name, "change must stick"
36
+ item.nil_test = "not a nil"
37
+ assert_equal "not a nil", item.nil_test, "change must stick"
38
+ item.name = "something else"
39
+ assert_equal "something else", item.name, "change must stick"
40
+ item.save
41
+ item = Product.find(:first)
42
+ assert_equal "something else", item.name, "change must persist in DB"
43
+ assert_equal "not a nil", item.nil_test, "change must persist in DB"
44
+ end
45
+
46
+ def test_item_attributes_to_a_works
47
+ item = Product.find_by_id(1)
48
+ arr = item.attributes_iv.to_a
49
+ expected = [["id","1"], ["name", "product_0"], ["number","0"], ["comment","Made by the test suite"], ["created_at", item.created_at_before_type_cast], ["nil_test", nil]]
50
+ arr.each do |a|
51
+ assert expected.include?(a), "array must match"
52
+ end
53
+ end
54
+
55
+ def test_item_can_be_marshalled
56
+ item = Product.find_by_id(1)
57
+ mi = Marshal.load(Marshal.dump(item))
58
+ check_attributes_for(mi, 0)
59
+ end
60
+
61
+ def test_has_key_and_include
62
+ item = Product.find_by_id(1)
63
+ assert item.attributes_iv.has_key?("name"), "must have key name"
64
+ assert item.attributes_iv.include?("name"), "must have key name"
65
+ assert !item.attributes_iv.has_key?("name1"), "must not have key name1"
66
+ assert !item.attributes_iv.include?("name1"), "must not have key name1"
67
+ end
68
+
69
+ def test_keys
70
+ item = Product.find_by_id(1)
71
+ assert_equal ["id", "name", "created_at", "number", "comment", "nil_test"].sort, item.attributes_iv.keys.sort, "keys must work"
72
+ end
73
+
74
+ def test_to_hash
75
+ item = Product.find_by_id(1)
76
+ expected = {"id"=>"1", "name"=>"product_0", "number"=>"0", "comment"=>"Made by the test suite", "created_at"=>item.created_at_before_type_cast, "nil_test"=>nil}
77
+ hash = item.attributes_iv.to_hash
78
+ assert_equal Hash, hash.class, "to_hash must result in a Hash"
79
+ assert_equal expected, hash, "to_hash must work"
80
+ end
81
+
82
+ def test_fake_hash_can_be_updated
83
+ item = Product.find_by_id(1)
84
+ old_name = item.name
85
+ item.attributes_iv.update("name"=>"foobar")
86
+ assert_equal "foobar", item.name, "update must work"
87
+ item.name_will_change! if ActiveRecord::VERSION::STRING >= "2.1.0"
88
+ item.save
89
+ item = Product.find_by_id(1)
90
+ assert_equal "foobar", item.name, "update must work and stick through db"
91
+ assert_equal 0, item.number, "other attributes must not be changed in db"
92
+ item.name = old_name
93
+ item.save
94
+ end
95
+
96
+ def test_can_assign_to_non_columns_in_hash
97
+ item = Product.find_by_id(1)
98
+ fh = item.attributes_iv
99
+ fh["something"] = 23
100
+ fh[:symbol] = "23"
101
+ assert_equal 23, fh["something"], "assignment to non col"
102
+ assert_equal "23", fh[:symbol], "assignment to non col"
103
+ assert fh.keys.include?("something"), "keys must include new"
104
+ assert fh.keys.include?(:symbol), "keys must include new"
105
+ assert fh.keys.include?("name"), "keys must still include old"
106
+ assert_equal 23, item.something, "non col becomes accessible with method call"
107
+ end
108
+
109
+ def test_key_can_be_deleted
110
+ item = Product.find_by_id(1)
111
+ fh = item.attributes_iv
112
+ fh.delete("name")
113
+ assert_nil fh["name"], "name must be nil now"
114
+ assert_raises(ActiveRecord::MissingAttributeError, "name must raise on method call") {item.name}
115
+ end
116
+
117
+ def test_gc
118
+ GC.start
119
+ assert true, "gc didn't crash"
120
+ end
121
+
122
+ # rails query cache uses dup
123
+ def test_dup
124
+ items = Product.find(:all)
125
+ attr_dup = items[0].attributes_iv.dup
126
+ assert_equal "product_0", attr_dup["name"], "name must be correct in dup'd attributes"
127
+ attr_dup = items[0].attributes_iv.dup
128
+ assert_equal "product_0", attr_dup["name"], "name must be correct in dup'd attributes 2nd time"
129
+ end
130
+
131
+ def test_cached_result
132
+ ActiveRecord::Base.connection.cache do
133
+ item1 = Product.find_by_id(1)
134
+ item1.name = "foo"
135
+ item2 = Product.find_by_id(1)
136
+ assert_equal "product_0", item2.name, "name must be original from cached query"
137
+ item3 = Product.find_by_id(1)
138
+ item1.name = "bar"
139
+ assert_equal "product_0", item3.name, "name must be original from cached query"
140
+ item2.name << "_test"
141
+ # unmodified rails fails this test, it's ok
142
+ # check_attributes_for(item3, 0)
143
+ end
144
+ end
145
+
146
+ def test_accessing_destroyed_object_attributes
147
+ item1 = Product.find_by_id(1)
148
+ assert_equal false, item1.frozen?
149
+ item1.destroy # object is frozen
150
+ assert_equal true, item1.frozen?
151
+ check_attributes_for(item1, 0)
152
+ assert_raises(RUBY_VERSION >= "1.9" ? RuntimeError : TypeError) {item1.name = "another product"}
153
+ end
154
+
155
+ def teardown
156
+ SlimDbTestUtils.remove_db
157
+ end
158
+
159
+ private
160
+
161
+ def check_attributes_for(item, i)
162
+ assert_equal "product_#{i}", item.name, "item name must be right"
163
+ assert_equal i, item.number, "item number must be right"
164
+ assert_equal i + 1, item.id, "item id must be right"
165
+ assert_equal "Made by the test suite", item.comment, "item comment must be right"
166
+ assert item.created_at <= Time.now && item.created_at > Time.now - 30, "item created_at must be reasonable"
167
+ assert_nil item.nil_test, "nil_test must be nil"
168
+ end
169
+ end
@@ -0,0 +1,38 @@
1
+ # Author: Stephen Sykes
2
+ # http://pennysmalls.com
3
+
4
+ module SlimDbTestUtils
5
+ DB_NAME = "slim_attributes_test"
6
+
7
+ def self.db_config
8
+ @config ||= YAML.load(File.read(File.dirname(__FILE__) + "/database.yml"))
9
+ @config[DB_NAME]
10
+ end
11
+
12
+ def self.connect_and_create_db
13
+ connect_with_config(db_config)
14
+ unless ActiveRecord::Base.connected? # database did not exist (as expected)
15
+ connect_with_config(db_config.merge({"database"=>nil}))
16
+ ActiveRecord::Base.connection.create_database(db_config["database"])
17
+ connect_with_config(db_config)
18
+ end
19
+ end
20
+
21
+ def self.connect_with_config(config)
22
+ begin
23
+ ActiveRecord::Base.establish_connection(config)
24
+ ActiveRecord::Base.connection
25
+ rescue Mysql::Error
26
+ end
27
+ end
28
+
29
+ def self.remove_db
30
+ if ActiveRecord::Base.connected?
31
+ ActiveRecord::Base.connection.execute("DROP DATABASE #{db_config["database"]}")
32
+ end
33
+ end
34
+
35
+ def self.import_sql(file)
36
+ `mysql -p#{db_config["password"]} -u #{db_config["username"]} #{db_config["database"]} < #{File.dirname(__FILE__)}/#{file}`
37
+ end
38
+ end
metadata CHANGED
@@ -1,37 +1,45 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: slim-attributes
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.6.0
4
+ version: 0.6.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stephen Sykes
8
- autorequire: slim
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-04-06 00:00:00 +03:00
12
+ date: 2009-07-12 00:00:00 +03:00
13
13
  default_executable:
14
14
  dependencies: []
15
15
 
16
- description:
16
+ description: Slim attributes boosts speed in Rails/Mysql ActiveRecord Models by avoiding instantiating Hashes for each result row, and lazily instantiating attributes as needed.
17
17
  email: sdsykes@gmail.com
18
18
  executables: []
19
19
 
20
20
  extensions:
21
21
  - ext/extconf.rb
22
- extra_rdoc_files: []
23
-
22
+ extra_rdoc_files:
23
+ - README
24
24
  files:
25
- - lib/slim_attributes.rb
25
+ - MIT_LICENCE
26
+ - Rakefile
27
+ - README
28
+ - VERSION.yml
26
29
  - ext/extconf.rb
27
30
  - ext/slim_attrib_ext.c
28
- - README
29
- - MIT_LICENCE
30
- has_rdoc: false
31
- homepage: http://slim-attributes.rubyforge.org/
31
+ - lib/slim_attributes.rb
32
+ - test/benchmark.rb
33
+ - test/database.yml
34
+ - test/products.rb
35
+ - test/slim_attributes_test.rb
36
+ - test/slim_db_test_utils.rb
37
+ has_rdoc: true
38
+ homepage: http://github.com/sdsykes/slim-attributes
32
39
  post_install_message:
33
- rdoc_options: []
34
-
40
+ rdoc_options:
41
+ - --inline-source
42
+ - --charset=UTF-8
35
43
  require_paths:
36
44
  - lib
37
45
  required_ruby_version: !ruby/object:Gem::Requirement
@@ -48,10 +56,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
48
56
  version:
49
57
  requirements: []
50
58
 
51
- rubyforge_project: slim-attributes
59
+ rubyforge_project:
52
60
  rubygems_version: 1.3.1
53
61
  signing_key:
54
62
  specification_version: 2
55
- summary: Slim attributes boosts speed in Rails/Mysql ActiveRecord Models by avoiding instantiating Hashes for each result row, and lazily instantiating attributes as needed
63
+ summary: Slim-attributes - lazy instantiation of attributes for ActiveRecord
56
64
  test_files: []
57
65