sbf-dm-core 1.3.0.beta

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.
Files changed (259) hide show
  1. checksums.yaml +7 -0
  2. data/.autotest +29 -0
  3. data/.document +5 -0
  4. data/.gitignore +44 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +468 -0
  7. data/.travis.yml +57 -0
  8. data/.yardopts +1 -0
  9. data/Gemfile +70 -0
  10. data/LICENSE +20 -0
  11. data/README.md +269 -0
  12. data/Rakefile +4 -0
  13. data/dm-core.gemspec +21 -0
  14. data/lib/dm-core/adapters/abstract_adapter.rb +233 -0
  15. data/lib/dm-core/adapters/in_memory_adapter.rb +110 -0
  16. data/lib/dm-core/adapters.rb +249 -0
  17. data/lib/dm-core/associations/many_to_many.rb +477 -0
  18. data/lib/dm-core/associations/many_to_one.rb +282 -0
  19. data/lib/dm-core/associations/one_to_many.rb +332 -0
  20. data/lib/dm-core/associations/one_to_one.rb +84 -0
  21. data/lib/dm-core/associations/relationship.rb +650 -0
  22. data/lib/dm-core/backwards.rb +11 -0
  23. data/lib/dm-core/collection.rb +1486 -0
  24. data/lib/dm-core/core_ext/kernel.rb +21 -0
  25. data/lib/dm-core/core_ext/pathname.rb +4 -0
  26. data/lib/dm-core/core_ext/symbol.rb +10 -0
  27. data/lib/dm-core/identity_map.rb +6 -0
  28. data/lib/dm-core/model/hook.rb +99 -0
  29. data/lib/dm-core/model/is.rb +30 -0
  30. data/lib/dm-core/model/property.rb +244 -0
  31. data/lib/dm-core/model/relationship.rb +366 -0
  32. data/lib/dm-core/model/scope.rb +87 -0
  33. data/lib/dm-core/model.rb +876 -0
  34. data/lib/dm-core/property/binary.rb +19 -0
  35. data/lib/dm-core/property/boolean.rb +35 -0
  36. data/lib/dm-core/property/class.rb +23 -0
  37. data/lib/dm-core/property/date.rb +45 -0
  38. data/lib/dm-core/property/date_time.rb +44 -0
  39. data/lib/dm-core/property/decimal.rb +47 -0
  40. data/lib/dm-core/property/discriminator.rb +40 -0
  41. data/lib/dm-core/property/float.rb +27 -0
  42. data/lib/dm-core/property/integer.rb +32 -0
  43. data/lib/dm-core/property/invalid_value_error.rb +17 -0
  44. data/lib/dm-core/property/lookup.rb +26 -0
  45. data/lib/dm-core/property/numeric.rb +35 -0
  46. data/lib/dm-core/property/object.rb +33 -0
  47. data/lib/dm-core/property/serial.rb +13 -0
  48. data/lib/dm-core/property/string.rb +47 -0
  49. data/lib/dm-core/property/text.rb +12 -0
  50. data/lib/dm-core/property/time.rb +46 -0
  51. data/lib/dm-core/property/typecast/numeric.rb +32 -0
  52. data/lib/dm-core/property/typecast/time.rb +33 -0
  53. data/lib/dm-core/property.rb +856 -0
  54. data/lib/dm-core/property_set.rb +177 -0
  55. data/lib/dm-core/query/conditions/comparison.rb +886 -0
  56. data/lib/dm-core/query/conditions/operation.rb +710 -0
  57. data/lib/dm-core/query/direction.rb +33 -0
  58. data/lib/dm-core/query/operator.rb +34 -0
  59. data/lib/dm-core/query/path.rb +113 -0
  60. data/lib/dm-core/query/sort.rb +38 -0
  61. data/lib/dm-core/query.rb +1352 -0
  62. data/lib/dm-core/relationship_set.rb +69 -0
  63. data/lib/dm-core/repository.rb +226 -0
  64. data/lib/dm-core/resource/persistence_state/clean.rb +36 -0
  65. data/lib/dm-core/resource/persistence_state/deleted.rb +26 -0
  66. data/lib/dm-core/resource/persistence_state/dirty.rb +91 -0
  67. data/lib/dm-core/resource/persistence_state/immutable.rb +32 -0
  68. data/lib/dm-core/resource/persistence_state/persisted.rb +25 -0
  69. data/lib/dm-core/resource/persistence_state/transient.rb +87 -0
  70. data/lib/dm-core/resource/persistence_state.rb +70 -0
  71. data/lib/dm-core/resource.rb +1220 -0
  72. data/lib/dm-core/spec/lib/adapter_helpers.rb +63 -0
  73. data/lib/dm-core/spec/lib/collection_helpers.rb +21 -0
  74. data/lib/dm-core/spec/lib/counter_adapter.rb +38 -0
  75. data/lib/dm-core/spec/lib/pending_helpers.rb +50 -0
  76. data/lib/dm-core/spec/lib/spec_helper.rb +74 -0
  77. data/lib/dm-core/spec/setup.rb +164 -0
  78. data/lib/dm-core/spec/shared/adapter_spec.rb +366 -0
  79. data/lib/dm-core/spec/shared/public/property_spec.rb +229 -0
  80. data/lib/dm-core/spec/shared/resource_spec.rb +1221 -0
  81. data/lib/dm-core/spec/shared/sel_spec.rb +111 -0
  82. data/lib/dm-core/spec/shared/semipublic/property_spec.rb +184 -0
  83. data/lib/dm-core/spec/shared/semipublic/query/conditions/abstract_comparison_spec.rb +261 -0
  84. data/lib/dm-core/support/assertions.rb +8 -0
  85. data/lib/dm-core/support/chainable.rb +18 -0
  86. data/lib/dm-core/support/deprecate.rb +12 -0
  87. data/lib/dm-core/support/descendant_set.rb +89 -0
  88. data/lib/dm-core/support/equalizer.rb +48 -0
  89. data/lib/dm-core/support/ext/array.rb +22 -0
  90. data/lib/dm-core/support/ext/blank.rb +25 -0
  91. data/lib/dm-core/support/ext/hash.rb +67 -0
  92. data/lib/dm-core/support/ext/module.rb +47 -0
  93. data/lib/dm-core/support/ext/object.rb +57 -0
  94. data/lib/dm-core/support/ext/string.rb +24 -0
  95. data/lib/dm-core/support/ext/try_dup.rb +12 -0
  96. data/lib/dm-core/support/hook.rb +388 -0
  97. data/lib/dm-core/support/inflections.rb +60 -0
  98. data/lib/dm-core/support/inflector/inflections.rb +211 -0
  99. data/lib/dm-core/support/inflector/methods.rb +151 -0
  100. data/lib/dm-core/support/lazy_array.rb +451 -0
  101. data/lib/dm-core/support/local_object_space.rb +13 -0
  102. data/lib/dm-core/support/logger.rb +201 -0
  103. data/lib/dm-core/support/mash.rb +176 -0
  104. data/lib/dm-core/support/naming_conventions.rb +109 -0
  105. data/lib/dm-core/support/ordered_set.rb +381 -0
  106. data/lib/dm-core/support/subject.rb +33 -0
  107. data/lib/dm-core/support/subject_set.rb +251 -0
  108. data/lib/dm-core/version.rb +3 -0
  109. data/lib/dm-core.rb +274 -0
  110. data/script/performance.rb +275 -0
  111. data/script/profile.rb +218 -0
  112. data/spec/lib/rspec_immediate_feedback_formatter.rb +54 -0
  113. data/spec/public/associations/many_to_many/read_multiple_join_spec.rb +69 -0
  114. data/spec/public/associations/many_to_many_spec.rb +197 -0
  115. data/spec/public/associations/many_to_one_spec.rb +83 -0
  116. data/spec/public/associations/many_to_one_with_boolean_cpk_spec.rb +40 -0
  117. data/spec/public/associations/many_to_one_with_custom_fk_spec.rb +49 -0
  118. data/spec/public/associations/one_to_many_spec.rb +81 -0
  119. data/spec/public/associations/one_to_one_spec.rb +176 -0
  120. data/spec/public/associations/one_to_one_with_boolean_cpk_spec.rb +46 -0
  121. data/spec/public/collection_spec.rb +69 -0
  122. data/spec/public/finalize_spec.rb +77 -0
  123. data/spec/public/model/hook_spec.rb +245 -0
  124. data/spec/public/model/property_spec.rb +91 -0
  125. data/spec/public/model/relationship_spec.rb +1040 -0
  126. data/spec/public/model_spec.rb +456 -0
  127. data/spec/public/property/binary_spec.rb +43 -0
  128. data/spec/public/property/boolean_spec.rb +21 -0
  129. data/spec/public/property/class_spec.rb +27 -0
  130. data/spec/public/property/date_spec.rb +21 -0
  131. data/spec/public/property/date_time_spec.rb +21 -0
  132. data/spec/public/property/decimal_spec.rb +23 -0
  133. data/spec/public/property/discriminator_spec.rb +134 -0
  134. data/spec/public/property/float_spec.rb +22 -0
  135. data/spec/public/property/integer_spec.rb +22 -0
  136. data/spec/public/property/object_spec.rb +117 -0
  137. data/spec/public/property/serial_spec.rb +22 -0
  138. data/spec/public/property/string_spec.rb +21 -0
  139. data/spec/public/property/text_spec.rb +62 -0
  140. data/spec/public/property/time_spec.rb +21 -0
  141. data/spec/public/property_spec.rb +333 -0
  142. data/spec/public/resource/state_spec.rb +72 -0
  143. data/spec/public/resource_spec.rb +289 -0
  144. data/spec/public/sel_spec.rb +53 -0
  145. data/spec/public/setup_spec.rb +145 -0
  146. data/spec/public/shared/association_collection_shared_spec.rb +309 -0
  147. data/spec/public/shared/collection_finder_shared_spec.rb +267 -0
  148. data/spec/public/shared/collection_shared_spec.rb +1637 -0
  149. data/spec/public/shared/finder_shared_spec.rb +1647 -0
  150. data/spec/semipublic/adapters/abstract_adapter_spec.rb +30 -0
  151. data/spec/semipublic/adapters/in_memory_adapter_spec.rb +13 -0
  152. data/spec/semipublic/associations/many_to_many_spec.rb +94 -0
  153. data/spec/semipublic/associations/many_to_one_spec.rb +63 -0
  154. data/spec/semipublic/associations/one_to_many_spec.rb +55 -0
  155. data/spec/semipublic/associations/one_to_one_spec.rb +53 -0
  156. data/spec/semipublic/associations/relationship_spec.rb +200 -0
  157. data/spec/semipublic/associations_spec.rb +177 -0
  158. data/spec/semipublic/collection_spec.rb +110 -0
  159. data/spec/semipublic/model_spec.rb +96 -0
  160. data/spec/semipublic/property/binary_spec.rb +13 -0
  161. data/spec/semipublic/property/boolean_spec.rb +47 -0
  162. data/spec/semipublic/property/class_spec.rb +33 -0
  163. data/spec/semipublic/property/date_spec.rb +43 -0
  164. data/spec/semipublic/property/date_time_spec.rb +46 -0
  165. data/spec/semipublic/property/decimal_spec.rb +83 -0
  166. data/spec/semipublic/property/discriminator_spec.rb +19 -0
  167. data/spec/semipublic/property/float_spec.rb +82 -0
  168. data/spec/semipublic/property/integer_spec.rb +82 -0
  169. data/spec/semipublic/property/lookup_spec.rb +29 -0
  170. data/spec/semipublic/property/serial_spec.rb +13 -0
  171. data/spec/semipublic/property/string_spec.rb +13 -0
  172. data/spec/semipublic/property/text_spec.rb +31 -0
  173. data/spec/semipublic/property/time_spec.rb +50 -0
  174. data/spec/semipublic/property_spec.rb +114 -0
  175. data/spec/semipublic/query/conditions/comparison_spec.rb +1502 -0
  176. data/spec/semipublic/query/conditions/operation_spec.rb +1296 -0
  177. data/spec/semipublic/query/path_spec.rb +471 -0
  178. data/spec/semipublic/query_spec.rb +3665 -0
  179. data/spec/semipublic/resource/state/clean_spec.rb +89 -0
  180. data/spec/semipublic/resource/state/deleted_spec.rb +79 -0
  181. data/spec/semipublic/resource/state/dirty_spec.rb +163 -0
  182. data/spec/semipublic/resource/state/immutable_spec.rb +107 -0
  183. data/spec/semipublic/resource/state/transient_spec.rb +163 -0
  184. data/spec/semipublic/resource/state_spec.rb +230 -0
  185. data/spec/semipublic/resource_spec.rb +23 -0
  186. data/spec/semipublic/shared/condition_shared_spec.rb +9 -0
  187. data/spec/semipublic/shared/resource_shared_spec.rb +198 -0
  188. data/spec/semipublic/shared/resource_state_shared_spec.rb +91 -0
  189. data/spec/semipublic/shared/subject_shared_spec.rb +79 -0
  190. data/spec/spec_helper.rb +34 -0
  191. data/spec/support/core_ext/hash.rb +10 -0
  192. data/spec/support/core_ext/inheritable_attributes.rb +46 -0
  193. data/spec/support/properties/huge_integer.rb +17 -0
  194. data/spec/unit/array_spec.rb +23 -0
  195. data/spec/unit/blank_spec.rb +73 -0
  196. data/spec/unit/data_mapper/ordered_set/append_spec.rb +26 -0
  197. data/spec/unit/data_mapper/ordered_set/clear_spec.rb +24 -0
  198. data/spec/unit/data_mapper/ordered_set/delete_spec.rb +28 -0
  199. data/spec/unit/data_mapper/ordered_set/each_spec.rb +19 -0
  200. data/spec/unit/data_mapper/ordered_set/empty_spec.rb +20 -0
  201. data/spec/unit/data_mapper/ordered_set/entries_spec.rb +22 -0
  202. data/spec/unit/data_mapper/ordered_set/eql_spec.rb +51 -0
  203. data/spec/unit/data_mapper/ordered_set/equal_value_spec.rb +84 -0
  204. data/spec/unit/data_mapper/ordered_set/hash_spec.rb +12 -0
  205. data/spec/unit/data_mapper/ordered_set/include_spec.rb +23 -0
  206. data/spec/unit/data_mapper/ordered_set/index_spec.rb +28 -0
  207. data/spec/unit/data_mapper/ordered_set/initialize_spec.rb +32 -0
  208. data/spec/unit/data_mapper/ordered_set/merge_spec.rb +36 -0
  209. data/spec/unit/data_mapper/ordered_set/shared/append_spec.rb +24 -0
  210. data/spec/unit/data_mapper/ordered_set/shared/clear_spec.rb +9 -0
  211. data/spec/unit/data_mapper/ordered_set/shared/delete_spec.rb +25 -0
  212. data/spec/unit/data_mapper/ordered_set/shared/each_spec.rb +17 -0
  213. data/spec/unit/data_mapper/ordered_set/shared/empty_spec.rb +9 -0
  214. data/spec/unit/data_mapper/ordered_set/shared/entries_spec.rb +9 -0
  215. data/spec/unit/data_mapper/ordered_set/shared/include_spec.rb +9 -0
  216. data/spec/unit/data_mapper/ordered_set/shared/index_spec.rb +13 -0
  217. data/spec/unit/data_mapper/ordered_set/shared/initialize_spec.rb +28 -0
  218. data/spec/unit/data_mapper/ordered_set/shared/merge_spec.rb +28 -0
  219. data/spec/unit/data_mapper/ordered_set/shared/size_spec.rb +13 -0
  220. data/spec/unit/data_mapper/ordered_set/shared/to_ary_spec.rb +11 -0
  221. data/spec/unit/data_mapper/ordered_set/size_spec.rb +27 -0
  222. data/spec/unit/data_mapper/ordered_set/to_ary_spec.rb +23 -0
  223. data/spec/unit/data_mapper/subject_set/append_spec.rb +47 -0
  224. data/spec/unit/data_mapper/subject_set/clear_spec.rb +34 -0
  225. data/spec/unit/data_mapper/subject_set/delete_spec.rb +40 -0
  226. data/spec/unit/data_mapper/subject_set/each_spec.rb +30 -0
  227. data/spec/unit/data_mapper/subject_set/empty_spec.rb +31 -0
  228. data/spec/unit/data_mapper/subject_set/entries_spec.rb +31 -0
  229. data/spec/unit/data_mapper/subject_set/get_spec.rb +34 -0
  230. data/spec/unit/data_mapper/subject_set/include_spec.rb +32 -0
  231. data/spec/unit/data_mapper/subject_set/named_spec.rb +33 -0
  232. data/spec/unit/data_mapper/subject_set/shared/append_spec.rb +18 -0
  233. data/spec/unit/data_mapper/subject_set/shared/clear_spec.rb +9 -0
  234. data/spec/unit/data_mapper/subject_set/shared/delete_spec.rb +9 -0
  235. data/spec/unit/data_mapper/subject_set/shared/each_spec.rb +9 -0
  236. data/spec/unit/data_mapper/subject_set/shared/empty_spec.rb +9 -0
  237. data/spec/unit/data_mapper/subject_set/shared/entries_spec.rb +9 -0
  238. data/spec/unit/data_mapper/subject_set/shared/get_spec.rb +9 -0
  239. data/spec/unit/data_mapper/subject_set/shared/include_spec.rb +9 -0
  240. data/spec/unit/data_mapper/subject_set/shared/named_spec.rb +9 -0
  241. data/spec/unit/data_mapper/subject_set/shared/size_spec.rb +13 -0
  242. data/spec/unit/data_mapper/subject_set/shared/to_ary_spec.rb +9 -0
  243. data/spec/unit/data_mapper/subject_set/shared/values_at_spec.rb +44 -0
  244. data/spec/unit/data_mapper/subject_set/size_spec.rb +42 -0
  245. data/spec/unit/data_mapper/subject_set/to_ary_spec.rb +34 -0
  246. data/spec/unit/data_mapper/subject_set/values_at_spec.rb +57 -0
  247. data/spec/unit/hash_spec.rb +27 -0
  248. data/spec/unit/hook_spec.rb +1216 -0
  249. data/spec/unit/inflections_spec.rb +14 -0
  250. data/spec/unit/lazy_array_spec.rb +1949 -0
  251. data/spec/unit/mash_spec.rb +289 -0
  252. data/spec/unit/module_spec.rb +70 -0
  253. data/spec/unit/object_spec.rb +38 -0
  254. data/spec/unit/try_dup_spec.rb +46 -0
  255. data/tasks/ci.rake +1 -0
  256. data/tasks/spec.rake +18 -0
  257. data/tasks/yard.rake +9 -0
  258. data/tasks/yardstick.rake +19 -0
  259. metadata +323 -0
@@ -0,0 +1,275 @@
1
+ #!/usr/bin/env ruby -Ku
2
+
3
+ require 'ftools'
4
+ require 'rubygems'
5
+
6
+ gem 'activerecord', '~> 2.3.4'
7
+ gem 'addressable', '~> 2.1'
8
+ gem 'faker', '~> 0.3.1'
9
+ gem 'rbench', '~> 0.2.3'
10
+
11
+ require 'active_record'
12
+ require 'addressable/uri'
13
+ require 'faker'
14
+ require 'rbench'
15
+
16
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'dm-core'))
17
+
18
+ socket_file = Pathname.glob(%w[
19
+ /opt/local/var/run/mysql5/mysqld.sock
20
+ tmp/mysqld.sock
21
+ /tmp/mysqld.sock
22
+ tmp/mysql.sock
23
+ /tmp/mysql.sock
24
+ /var/mysql/mysql.sock
25
+ /var/run/mysqld/mysqld.sock
26
+ ]).find { |path| path.socket? }
27
+
28
+ configuration_options = {
29
+ :adapter => 'mysql',
30
+ :username => 'root',
31
+ :password => '',
32
+ :database => 'dm_core_test',
33
+ }
34
+
35
+ configuration_options[:socket] = socket_file unless socket_file.nil?
36
+
37
+ log_dir = DataMapper.root / 'log'
38
+ log_dir.mkdir unless log_dir.directory?
39
+
40
+ DataMapper::Logger.new(log_dir / 'dm.log', :off)
41
+ adapter = DataMapper.setup(:default, "mysql://root@127.0.0.1/dm_core_test?socket=#{socket_file}")
42
+
43
+ if configuration_options[:adapter]
44
+ sqlfile = File.join(File.dirname(__FILE__), '..', 'tmp', 'performance.sql')
45
+ mysql_bin = %w[ mysql mysql5 ].select { |bin| `which #{bin}`.length > 0 }
46
+ mysqldump_bin = %w[ mysqldump mysqldump5 ].select { |bin| `which #{bin}`.length > 0 }
47
+ end
48
+
49
+ ActiveRecord::Base.logger = Logger.new(log_dir / 'ar.log')
50
+ ActiveRecord::Base.logger.level = 0
51
+
52
+ ActiveRecord::Base.establish_connection(configuration_options)
53
+
54
+ class ARExhibit < ActiveRecord::Base #:nodoc:
55
+ set_table_name 'exhibits'
56
+
57
+ belongs_to :user, :class_name => 'ARUser', :foreign_key => 'user_id'
58
+ end
59
+
60
+ class ARUser < ActiveRecord::Base #:nodoc:
61
+ set_table_name 'users'
62
+
63
+ has_many :exhibits, :foreign_key => 'user_id'
64
+ end
65
+
66
+ ARExhibit.find_by_sql('SELECT 1')
67
+
68
+ class User
69
+ include DataMapper::Resource
70
+
71
+ property :id, Serial
72
+ property :name, String
73
+ property :email, String
74
+ property :about, Text, :lazy => false
75
+ property :created_on, Date
76
+ end
77
+
78
+ class Exhibit
79
+ include DataMapper::Resource
80
+
81
+ property :id, Serial
82
+ property :name, String
83
+ property :zoo_id, Integer
84
+ property :user_id, Integer
85
+ property :notes, Text, :lazy => false
86
+ property :created_on, Date
87
+
88
+ belongs_to :user
89
+ end
90
+
91
+ DataMapper.auto_migrate!
92
+
93
+ def touch_attributes(*exhibits)
94
+ exhibits.flatten.each do |exhibit|
95
+ exhibit.id
96
+ exhibit.name
97
+ exhibit.created_on
98
+ end
99
+ end
100
+
101
+ def touch_relationships(*exhibits)
102
+ exhibits.flatten.each do |exhibit|
103
+ exhibit.id
104
+ exhibit.name
105
+ exhibit.created_on
106
+ exhibit.user
107
+ end
108
+ end
109
+
110
+ c = configuration_options
111
+
112
+ if sqlfile && File.exists?(sqlfile)
113
+ puts "Found data-file. Importing from #{sqlfile}"
114
+ #adapter.execute("LOAD DATA LOCAL INFILE '#{sqlfile}' INTO TABLE exhibits")
115
+ `#{mysql_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} < #{sqlfile}`
116
+ else
117
+ puts 'Generating data for benchmarking...'
118
+
119
+ # pre-compute the insert statements and fake data compilation,
120
+ # so the benchmarks below show the actual runtime for the execute
121
+ # method, minus the setup steps
122
+
123
+ # Using the same paragraph for all exhibits because it is very slow
124
+ # to generate unique paragraphs for all exhibits.
125
+ notes = Faker::Lorem.paragraphs.join($/)
126
+ today = Date.today
127
+
128
+ puts 'Inserting 10,000 users and exhibits...'
129
+ 10_000.times do
130
+ user = User.create(
131
+ :created_on => today,
132
+ :name => Faker::Name.name,
133
+ :email => Faker::Internet.email
134
+ )
135
+
136
+ Exhibit.create(
137
+ :created_on => today,
138
+ :name => Faker::Company.name,
139
+ :user => user,
140
+ :notes => notes,
141
+ :zoo_id => rand(10).ceil
142
+ )
143
+ end
144
+
145
+ if sqlfile
146
+ answer = nil
147
+ until answer && answer[/\A(?:y(?:es)?|no?)\b/i]
148
+ print('Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]');
149
+ STDOUT.flush
150
+ answer = gets
151
+ end
152
+
153
+ if %w[ y yes ].include?(answer.downcase)
154
+ File.makedirs(File.dirname(sqlfile))
155
+ #adapter.execute("SELECT * INTO OUTFILE '#{sqlfile}' FROM exhibits;")
156
+ `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
157
+ puts "File saved\n"
158
+ end
159
+ end
160
+ end
161
+
162
+ TIMES = ENV.key?('x') ? ENV['x'].to_i : 10_000
163
+
164
+ puts 'You can specify how many times you want to run the benchmarks with rake:perf x=(number)'
165
+ puts 'Some tasks will be run 10 and 1000 times less than (number)'
166
+ puts "Benchmarks will now run #{TIMES} times"
167
+ # Inform about slow benchmark
168
+ # answer = nil
169
+ # until answer && answer[/^$|y|yes|n|no/]
170
+ # print("A slow benchmark exposing problems with SEL is newly added. It takes approx. 20s\n");
171
+ # print("you have scheduled it to run #{TIMES / 100} times.\nWould you still include the particular benchmark? [Yn]")
172
+ # STDOUT.flush
173
+ # answer = gets
174
+ # end
175
+ # run_rel_bench = answer[/^$|y|yes/] ? true : false
176
+
177
+
178
+ RBench.run(TIMES) do
179
+
180
+ column :times
181
+ column :ar, :title => 'AR 2.3.2'
182
+ column :dm, :title => "DM #{DataMapper::VERSION}"
183
+ column :diff, :compare => [:ar, :dm]
184
+
185
+ report 'Model#id', (TIMES * 100).ceil do
186
+ ar_obj = ARExhibit.find(1)
187
+ dm_obj = Exhibit.get(1)
188
+
189
+ ar { ar_obj.id }
190
+ dm { dm_obj.id }
191
+ end
192
+
193
+ report 'Model.new (instantiation)' do
194
+ ar { ARExhibit.new }
195
+ dm { Exhibit.new }
196
+ end
197
+
198
+ report 'Model.new (setting attributes)' do
199
+ attrs = { :name => 'sam', :zoo_id => 1 }
200
+ ar { ARExhibit.new(attrs) }
201
+ dm { Exhibit.new(attrs) }
202
+ end
203
+
204
+ report 'Model.get specific (not cached)' do
205
+ ActiveRecord::Base.uncached { ar { touch_attributes(ARExhibit.find(1)) } }
206
+ dm { touch_attributes(Exhibit.get(1)) }
207
+ end
208
+
209
+ report 'Model.get specific (cached)' do
210
+ ActiveRecord::Base.cache { ar { touch_attributes(ARExhibit.find(1)) } }
211
+ Exhibit.repository(:default) { dm { touch_attributes(Exhibit.get(1)) } }
212
+ end
213
+
214
+ report 'Model.first' do
215
+ ar { touch_attributes(ARExhibit.first) }
216
+ dm { touch_attributes(Exhibit.first) }
217
+ end
218
+
219
+ report 'Model.all limit(100)', (TIMES / 10).ceil do
220
+ ar { touch_attributes(ARExhibit.find(:all, :limit => 100)) }
221
+ dm { touch_attributes(Exhibit.all(:limit => 100)) }
222
+ end
223
+
224
+ report 'Model.all limit(100) with relationship', (TIMES / 10).ceil do
225
+ ar { touch_relationships(ARExhibit.all(:limit => 100, :include => [ :user ])) }
226
+ dm { touch_relationships(Exhibit.all(:limit => 100)) }
227
+ end
228
+
229
+ report 'Model.all limit(10,000)', (TIMES / 1000).ceil do
230
+ ar { touch_attributes(ARExhibit.find(:all, :limit => 10_000)) }
231
+ dm { touch_attributes(Exhibit.all(:limit => 10_000)) }
232
+ end
233
+
234
+ exhibit = {
235
+ :name => Faker::Company.name,
236
+ :zoo_id => rand(10).ceil,
237
+ :notes => Faker::Lorem.paragraphs.join($/),
238
+ :created_on => Date.today
239
+ }
240
+
241
+ report 'Model.create' do
242
+ ar { ARExhibit.create(exhibit) }
243
+ dm { Exhibit.create(exhibit) }
244
+ end
245
+
246
+ report 'Resource#attributes=' do
247
+ attrs_first = { :name => 'sam', :zoo_id => 1 }
248
+ attrs_second = { :name => 'tom', :zoo_id => 1 }
249
+ ar { exhibit = ARExhibit.new(attrs_first); exhibit.attributes = attrs_second }
250
+ dm { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
251
+ end
252
+
253
+ report 'Resource#update' do
254
+ ar { ARExhibit.find(1).update_attributes(:name => 'bob') }
255
+ dm { Exhibit.get(1).update(:name => 'bob') }
256
+ end
257
+
258
+ report 'Resource#destroy' do
259
+ ar { ARExhibit.first.destroy }
260
+ dm { Exhibit.first.destroy }
261
+ end
262
+
263
+ report 'Model.transaction' do
264
+ ar { ARExhibit.transaction { ARExhibit.new } }
265
+ dm { Exhibit.transaction { Exhibit.new } }
266
+ end
267
+
268
+ summary 'Total'
269
+ end
270
+
271
+ connection = adapter.send(:open_connection)
272
+ command = connection.create_command('DROP TABLE exhibits')
273
+ command = connection.create_command('DROP TABLE users')
274
+ command.execute_non_query rescue nil
275
+ connection.close
data/script/profile.rb ADDED
@@ -0,0 +1,218 @@
1
+ #!/usr/bin/env ruby -Ku
2
+
3
+ require 'ftools'
4
+ require 'rubygems'
5
+
6
+ gem 'addressable', '~> 2.1'
7
+ gem 'faker', '~> 0.3.1'
8
+ gem 'ruby-prof', '~> 0.7.3'
9
+
10
+ require 'addressable/uri'
11
+ require 'faker'
12
+ require 'ruby-prof'
13
+
14
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'dm-core'))
15
+
16
+ TEXT_OUTPUT = DataMapper.root / 'profile_results.txt'
17
+ HTML_OUTPUT = DataMapper.root / 'profile_results.html'
18
+ CALL_OUTPUT = DataMapper.root / 'profile_results.prof'
19
+
20
+ SOCKET_FILE = Pathname.glob(%w[
21
+ /opt/local/var/run/mysql5/mysqld.sock
22
+ /tmp/mysqld.sock
23
+ /tmp/mysql.sock
24
+ /var/mysql/mysql.sock
25
+ /var/run/mysqld/mysqld.sock
26
+ ]).find { |path| path.socket? }
27
+
28
+ configuration_options = {
29
+ :adapter => 'mysql',
30
+ :database => 'dm_core_test',
31
+ :host => '127.0.0.1',
32
+ :username => 'root',
33
+ :password => '',
34
+ :socket => SOCKET_FILE,
35
+ }
36
+
37
+ DataMapper::Logger.new(DataMapper.root / 'log' / 'dm.log', :debug)
38
+ adapter = DataMapper.setup(:default, configuration_options)
39
+
40
+ if configuration_options[:adapter]
41
+ sqlfile = File.join(File.dirname(__FILE__), '..', 'tmp', 'performance.sql')
42
+ mysql_bin = %w[ mysql mysql5 ].select { |bin| `which #{bin}`.length > 0 }
43
+ mysqldump_bin = %w[ mysqldump mysqldump5 ].select { |bin| `which #{bin}`.length > 0 }
44
+ end
45
+
46
+ class User
47
+ include DataMapper::Resource
48
+
49
+ property :id, Serial
50
+ property :name, String
51
+ property :email, String
52
+ property :about, Text, :lazy => false
53
+ property :created_on, Date
54
+ end
55
+
56
+ class Exhibit
57
+ include DataMapper::Resource
58
+
59
+ property :id, Serial
60
+ property :name, String
61
+ property :zoo_id, Integer
62
+ property :user_id, Integer
63
+ property :notes, Text, :lazy => false
64
+ property :created_on, Date
65
+
66
+ belongs_to :user
67
+ end
68
+
69
+ DataMapper.auto_migrate!
70
+
71
+ def touch_attributes(*exhibits)
72
+ exhibits.flatten.each do |exhibit|
73
+ exhibit.id
74
+ exhibit.name
75
+ exhibit.created_on
76
+ end
77
+ end
78
+
79
+ def touch_relationships(*exhibits)
80
+ exhibits.flatten.each do |exhibit|
81
+ exhibit.id
82
+ exhibit.name
83
+ exhibit.created_on
84
+ exhibit.user
85
+ end
86
+ end
87
+
88
+ # RubyProf, making profiling Ruby pretty since 1899!
89
+ def profile(&b)
90
+ results = RubyProf.profile(&b)
91
+
92
+ TEXT_OUTPUT.open('w+') do |file|
93
+ RubyProf::FlatPrinter.new(results).print(file)
94
+ end
95
+
96
+ HTML_OUTPUT.open('w+') do |file|
97
+ RubyProf::GraphHtmlPrinter.new(results).print(file)
98
+ end
99
+
100
+ CALL_OUTPUT.open('w+') do |file|
101
+ RubyProf::CallTreePrinter.new(results).print(file)
102
+ end
103
+ end
104
+
105
+ c = configuration_options
106
+
107
+ if sqlfile && File.exists?(sqlfile)
108
+ puts "Found data-file. Importing from #{sqlfile}"
109
+ #adapter.execute("LOAD DATA LOCAL INFILE '#{sqlfile}' INTO TABLE exhibits")
110
+ `#{mysql_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} < #{sqlfile}`
111
+ else
112
+ puts 'Generating data for benchmarking...'
113
+
114
+ # pre-compute the insert statements and fake data compilation,
115
+ # so the benchmarks below show the actual runtime for the execute
116
+ # method, minus the setup steps
117
+
118
+ # Using the same paragraph for all exhibits because it is very slow
119
+ # to generate unique paragraphs for all exhibits.
120
+ notes = Faker::Lorem.paragraphs.join($/)
121
+ today = Date.today
122
+
123
+ puts 'Inserting 10,000 users and exhibits...'
124
+ 10_000.times do
125
+ user = User.create(
126
+ :created_on => today,
127
+ :name => Faker::Name.name,
128
+ :email => Faker::Internet.email
129
+ )
130
+
131
+ Exhibit.create(
132
+ :created_on => today,
133
+ :name => Faker::Company.name,
134
+ :user => user,
135
+ :notes => notes,
136
+ :zoo_id => rand(10).ceil
137
+ )
138
+ end
139
+
140
+ if sqlfile
141
+ answer = nil
142
+ until answer && answer[/\A(?:y(?:es)?|no?)\b/i]
143
+ print('Would you like to dump data into tmp/performance.sql (for faster setup)? [Yn]');
144
+ STDOUT.flush
145
+ answer = gets
146
+ end
147
+
148
+ if %w[ y yes ].include?(answer.downcase)
149
+ File.makedirs(File.dirname(sqlfile))
150
+ #adapter.execute("SELECT * INTO OUTFILE '#{sqlfile}' FROM exhibits;")
151
+ `#{mysqldump_bin} -u #{c[:username]} #{"-p#{c[:password]}" unless c[:password].blank?} #{c[:database]} exhibits users > #{sqlfile}`
152
+ puts "File saved\n"
153
+ end
154
+ end
155
+ end
156
+
157
+ TIMES = 10_000
158
+
159
+ exhibits = Exhibit.all.to_a
160
+
161
+ profile do
162
+ # dm_obj = Exhibit.get(1)
163
+ # puts 'Model#id'
164
+ # (TIMES * 100).times { dm_obj.id }
165
+ #
166
+ # puts 'Model.new (instantiation)'
167
+ # TIMES.times { Exhibit.new }
168
+ #
169
+ # puts 'Model.new (setting attributes)'
170
+ # TIMES.times { Exhibit.new(:name => 'sam', :zoo_id => 1) }
171
+ #
172
+ # puts 'Model.get specific (not cached)'
173
+ # TIMES.times { touch_attributes(Exhibit.get(1)) }
174
+ #
175
+ # puts 'Model.get specific (cached)'
176
+ # repository(:default) do
177
+ # TIMES.times { touch_attributes(Exhibit.get(1)) }
178
+ # end
179
+
180
+ puts 'Model.first'
181
+ TIMES.times { touch_attributes(Exhibit.first) }
182
+
183
+ # puts 'Model.all limit(100)'
184
+ # (TIMES / 10).ceil.times { touch_attributes(Exhibit.all(:limit => 100)) }
185
+ #
186
+ # puts 'Model.all limit(100) with relationship'
187
+ # (TIMES / 10).ceil.times { touch_relationships(Exhibit.all(:limit => 100)) }
188
+ #
189
+ # puts 'Model.all limit(10,000)'
190
+ # (TIMES / 1000).ceil { touch_attributes(Exhibit.all(:limit => 10_000)) }
191
+ #
192
+ # exhibit = {
193
+ # :name => Faker::Company.name,
194
+ # :zoo_id => rand(10).ceil,
195
+ # :notes => Faker::Lorem.paragraphs.join($/),
196
+ # :created_on => Date.today
197
+ # }
198
+ #
199
+ # puts 'Model.create'
200
+ # TIMES.times { Exhibit.create(exhibit) }
201
+ #
202
+ # attrs_first = { :name => 'sam', :zoo_id => 1 }
203
+ # attrs_second = { :name => 'tom', :zoo_id => 1 }
204
+ #
205
+ # puts 'Resource#attributes='
206
+ # TIMES.times { exhibit = Exhibit.new(attrs_first); exhibit.attributes = attrs_second }
207
+ #
208
+ # puts 'Resource#update'
209
+ # TIMES.times { |index| exhibit = exhibits[index]; exhibit.name = 'bob'; exhibit.save }
210
+ #
211
+ # puts 'Resource#destroy'
212
+ # TIMES.times { |index| exhibits[index].destroy }
213
+ #
214
+ # puts 'Model.transaction'
215
+ # TIMES.times { Exhibit.transaction { Exhibit.new } }
216
+ end
217
+
218
+ puts "Done!"
@@ -0,0 +1,54 @@
1
+ require 'rspec/core/formatters/base_text_formatter'
2
+
3
+ # Code is based on standard SpecdocFormatter, but will print full error details as soon as they are found.
4
+ # Successful or pending examples are written only as a dot in the output. Header is only printed if errors occur.
5
+ #
6
+ # To use it, add the following to your spec/spec.opts:
7
+ # --require
8
+ # lib/rspec_immediate_feedback_formatter.rb
9
+ # --format
10
+ # RSpec::Core::Runner::Formatter::ImmediateFeedbackFormatter
11
+
12
+ module RSpec
13
+ module Core
14
+ module Formatters
15
+ class ImmediateFeedbackFormatter < BaseTextFormatter
16
+
17
+ def add_example_group(example_group)
18
+ super
19
+ @current_group = example_group.description
20
+ end
21
+
22
+ def example_failed(example, counter, failure)
23
+ if @current_group
24
+ output.puts
25
+ output.puts @current_group
26
+ @current_group = nil # only print the group name once
27
+ end
28
+
29
+ message = if failure.expectation_not_met?
30
+ "- #{example.description} (FAILED - #{counter})"
31
+ else
32
+ "- #{example.description} (ERROR - #{counter})"
33
+ end
34
+
35
+ output.puts(red(message))
36
+ # output.puts(failure.expectation_not_met? ? red(message) : message)
37
+ dump_failure(counter, failure) # dump stacktrace immediately
38
+ output.flush
39
+ end
40
+
41
+ def example_passed(*)
42
+ output.print green('.')
43
+ output.flush
44
+ end
45
+
46
+ def example_pending(*)
47
+ super
48
+ output.print yellow('*')
49
+ output.flush
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,69 @@
1
+ require_relative '../../../spec_helper'
2
+
3
+ describe 'Many to Many Associations read across multiple join associations' do
4
+ before :all do
5
+ class ::User
6
+ include DataMapper::Resource
7
+
8
+ property :id, Serial
9
+
10
+ has n, :sales
11
+ has n, :sale_items, :through => :sales
12
+ has n, :items, :through => :sale_items
13
+ end
14
+
15
+ class ::Sale
16
+ include DataMapper::Resource
17
+
18
+ property :id, Serial
19
+
20
+ belongs_to :user
21
+ has n, :sale_items
22
+ has n, :items, :through => :sale_items
23
+ end
24
+
25
+ class ::SaleItem
26
+ include DataMapper::Resource
27
+
28
+ property :id, Serial
29
+
30
+ belongs_to :sale
31
+ belongs_to :item
32
+ end
33
+
34
+ class ::Item
35
+ include DataMapper::Resource
36
+
37
+ property :id, Serial
38
+
39
+ has n, :sale_items
40
+ end
41
+
42
+ DataMapper.finalize
43
+ end
44
+
45
+ supported_by :all do
46
+ before :all do
47
+ @user = User.create
48
+ @sale = @user.sales.create
49
+
50
+ 5.times { @sale.items.create }
51
+ end
52
+
53
+ before :all do
54
+ @no_join = (defined?(DataMapper::Adapters::InMemoryAdapter) && @adapter.is_a?(DataMapper::Adapters::InMemoryAdapter)) ||
55
+ (defined?(DataMapper::Adapters::YamlAdapter) && @adapter.is_a?(DataMapper::Adapters::YamlAdapter))
56
+
57
+ @skip = @no_join
58
+ end
59
+
60
+ before do
61
+ pending if @skip
62
+ end
63
+
64
+ it 'returns all the created entries' do
65
+ expect(@user.items.to_a).to eq Item.all.to_a
66
+ expect(@sale.items.to_a).to eq Item.all.to_a
67
+ end
68
+ end
69
+ end