ardm 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (179) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +35 -0
  3. data/Gemfile +13 -0
  4. data/LICENSE +21 -0
  5. data/README.md +70 -0
  6. data/Rakefile +4 -0
  7. data/ardm.gemspec +29 -0
  8. data/db/.gitignore +1 -0
  9. data/lib/ardm/active_record/associations.rb +80 -0
  10. data/lib/ardm/active_record/base.rb +49 -0
  11. data/lib/ardm/active_record/dirty.rb +25 -0
  12. data/lib/ardm/active_record/hooks.rb +31 -0
  13. data/lib/ardm/active_record/inheritance.rb +37 -0
  14. data/lib/ardm/active_record/is/state_machine.rb +21 -0
  15. data/lib/ardm/active_record/is.rb +22 -0
  16. data/lib/ardm/active_record/not_found.rb +7 -0
  17. data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
  18. data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
  19. data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
  20. data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
  21. data/lib/ardm/active_record/predicate_builder.rb +19 -0
  22. data/lib/ardm/active_record/property.rb +357 -0
  23. data/lib/ardm/active_record/query.rb +108 -0
  24. data/lib/ardm/active_record/record.rb +70 -0
  25. data/lib/ardm/active_record/relation.rb +83 -0
  26. data/lib/ardm/active_record/repository.rb +38 -0
  27. data/lib/ardm/active_record/serialization.rb +164 -0
  28. data/lib/ardm/active_record/storage_names.rb +28 -0
  29. data/lib/ardm/active_record/validations.rb +111 -0
  30. data/lib/ardm/active_record.rb +43 -0
  31. data/lib/ardm/data_mapper/not_found.rb +5 -0
  32. data/lib/ardm/data_mapper/record.rb +41 -0
  33. data/lib/ardm/data_mapper.rb +5 -0
  34. data/lib/ardm/env.rb +5 -0
  35. data/lib/ardm/property/api_key.rb +30 -0
  36. data/lib/ardm/property/bcrypt_hash.rb +31 -0
  37. data/lib/ardm/property/binary.rb +23 -0
  38. data/lib/ardm/property/boolean.rb +29 -0
  39. data/lib/ardm/property/class.rb +19 -0
  40. data/lib/ardm/property/comma_separated_list.rb +28 -0
  41. data/lib/ardm/property/csv.rb +35 -0
  42. data/lib/ardm/property/date.rb +12 -0
  43. data/lib/ardm/property/datetime.rb +12 -0
  44. data/lib/ardm/property/decimal.rb +38 -0
  45. data/lib/ardm/property/discriminator.rb +65 -0
  46. data/lib/ardm/property/enum.rb +51 -0
  47. data/lib/ardm/property/epoch_time.rb +38 -0
  48. data/lib/ardm/property/file_path.rb +25 -0
  49. data/lib/ardm/property/flag.rb +65 -0
  50. data/lib/ardm/property/float.rb +18 -0
  51. data/lib/ardm/property/integer.rb +24 -0
  52. data/lib/ardm/property/invalid_value_error.rb +22 -0
  53. data/lib/ardm/property/ip_address.rb +35 -0
  54. data/lib/ardm/property/json.rb +49 -0
  55. data/lib/ardm/property/lookup.rb +29 -0
  56. data/lib/ardm/property/numeric.rb +40 -0
  57. data/lib/ardm/property/object.rb +36 -0
  58. data/lib/ardm/property/paranoid_boolean.rb +18 -0
  59. data/lib/ardm/property/paranoid_datetime.rb +17 -0
  60. data/lib/ardm/property/regexp.rb +22 -0
  61. data/lib/ardm/property/serial.rb +16 -0
  62. data/lib/ardm/property/slug.rb +29 -0
  63. data/lib/ardm/property/string.rb +40 -0
  64. data/lib/ardm/property/support/dirty_minder.rb +169 -0
  65. data/lib/ardm/property/support/flags.rb +41 -0
  66. data/lib/ardm/property/support/paranoid_base.rb +78 -0
  67. data/lib/ardm/property/text.rb +11 -0
  68. data/lib/ardm/property/time.rb +12 -0
  69. data/lib/ardm/property/uri.rb +27 -0
  70. data/lib/ardm/property/uuid.rb +65 -0
  71. data/lib/ardm/property/validation.rb +208 -0
  72. data/lib/ardm/property/yaml.rb +38 -0
  73. data/lib/ardm/property.rb +891 -0
  74. data/lib/ardm/property_set.rb +152 -0
  75. data/lib/ardm/query/expression.rb +85 -0
  76. data/lib/ardm/query/ext/symbol.rb +37 -0
  77. data/lib/ardm/query/operator.rb +64 -0
  78. data/lib/ardm/record.rb +1 -0
  79. data/lib/ardm/support/assertions.rb +8 -0
  80. data/lib/ardm/support/deprecate.rb +12 -0
  81. data/lib/ardm/support/descendant_set.rb +89 -0
  82. data/lib/ardm/support/equalizer.rb +48 -0
  83. data/lib/ardm/support/ext/array.rb +22 -0
  84. data/lib/ardm/support/ext/blank.rb +25 -0
  85. data/lib/ardm/support/ext/hash.rb +67 -0
  86. data/lib/ardm/support/ext/module.rb +47 -0
  87. data/lib/ardm/support/ext/object.rb +57 -0
  88. data/lib/ardm/support/ext/string.rb +24 -0
  89. data/lib/ardm/support/ext/try_dup.rb +12 -0
  90. data/lib/ardm/support/hook.rb +405 -0
  91. data/lib/ardm/support/lazy_array.rb +451 -0
  92. data/lib/ardm/support/local_object_space.rb +13 -0
  93. data/lib/ardm/support/logger.rb +201 -0
  94. data/lib/ardm/support/mash.rb +176 -0
  95. data/lib/ardm/support/naming_conventions.rb +90 -0
  96. data/lib/ardm/support/ordered_set.rb +380 -0
  97. data/lib/ardm/support/subject.rb +33 -0
  98. data/lib/ardm/support/subject_set.rb +250 -0
  99. data/lib/ardm/version.rb +3 -0
  100. data/lib/ardm.rb +56 -0
  101. data/spec/fixtures/api_user.rb +11 -0
  102. data/spec/fixtures/article.rb +22 -0
  103. data/spec/fixtures/bookmark.rb +14 -0
  104. data/spec/fixtures/invention.rb +5 -0
  105. data/spec/fixtures/network_node.rb +23 -0
  106. data/spec/fixtures/person.rb +17 -0
  107. data/spec/fixtures/software_package.rb +22 -0
  108. data/spec/fixtures/ticket.rb +12 -0
  109. data/spec/fixtures/tshirt.rb +15 -0
  110. data/spec/integration/api_key_spec.rb +25 -0
  111. data/spec/integration/bcrypt_hash_spec.rb +45 -0
  112. data/spec/integration/comma_separated_list_spec.rb +85 -0
  113. data/spec/integration/dirty_minder_spec.rb +197 -0
  114. data/spec/integration/enum_spec.rb +79 -0
  115. data/spec/integration/epoch_time_spec.rb +59 -0
  116. data/spec/integration/file_path_spec.rb +158 -0
  117. data/spec/integration/flag_spec.rb +72 -0
  118. data/spec/integration/ip_address_spec.rb +151 -0
  119. data/spec/integration/json_spec.rb +70 -0
  120. data/spec/integration/slug_spec.rb +65 -0
  121. data/spec/integration/uri_spec.rb +136 -0
  122. data/spec/integration/uuid_spec.rb +102 -0
  123. data/spec/integration/yaml_spec.rb +85 -0
  124. data/spec/public/property/binary_spec.rb +41 -0
  125. data/spec/public/property/boolean_spec.rb +30 -0
  126. data/spec/public/property/class_spec.rb +28 -0
  127. data/spec/public/property/date_spec.rb +22 -0
  128. data/spec/public/property/date_time_spec.rb +22 -0
  129. data/spec/public/property/decimal_spec.rb +23 -0
  130. data/spec/public/property/discriminator_spec.rb +133 -0
  131. data/spec/public/property/float_spec.rb +22 -0
  132. data/spec/public/property/integer_spec.rb +22 -0
  133. data/spec/public/property/object_spec.rb +103 -0
  134. data/spec/public/property/serial_spec.rb +22 -0
  135. data/spec/public/property/string_spec.rb +22 -0
  136. data/spec/public/property/text_spec.rb +23 -0
  137. data/spec/public/property/time_spec.rb +22 -0
  138. data/spec/public/property_spec.rb +316 -0
  139. data/spec/rcov.opts +6 -0
  140. data/spec/schema.rb +86 -0
  141. data/spec/semipublic/property/binary_spec.rb +14 -0
  142. data/spec/semipublic/property/boolean_spec.rb +48 -0
  143. data/spec/semipublic/property/class_spec.rb +36 -0
  144. data/spec/semipublic/property/date_spec.rb +44 -0
  145. data/spec/semipublic/property/date_time_spec.rb +47 -0
  146. data/spec/semipublic/property/decimal_spec.rb +83 -0
  147. data/spec/semipublic/property/discriminator_spec.rb +22 -0
  148. data/spec/semipublic/property/float_spec.rb +83 -0
  149. data/spec/semipublic/property/integer_spec.rb +83 -0
  150. data/spec/semipublic/property/lookup_spec.rb +27 -0
  151. data/spec/semipublic/property/serial_spec.rb +14 -0
  152. data/spec/semipublic/property/string_spec.rb +14 -0
  153. data/spec/semipublic/property/text_spec.rb +30 -0
  154. data/spec/semipublic/property/time_spec.rb +49 -0
  155. data/spec/semipublic/property_spec.rb +51 -0
  156. data/spec/shared/flags_shared_spec.rb +36 -0
  157. data/spec/shared/identity_function_group.rb +5 -0
  158. data/spec/shared/public_property_spec.rb +229 -0
  159. data/spec/shared/semipublic_property_spec.rb +159 -0
  160. data/spec/spec.opts +4 -0
  161. data/spec/spec_helper.rb +58 -0
  162. data/spec/unit/bcrypt_hash_spec.rb +154 -0
  163. data/spec/unit/csv_spec.rb +139 -0
  164. data/spec/unit/dirty_minder_spec.rb +64 -0
  165. data/spec/unit/enum_spec.rb +125 -0
  166. data/spec/unit/epoch_time_spec.rb +72 -0
  167. data/spec/unit/file_path_spec.rb +75 -0
  168. data/spec/unit/flag_spec.rb +114 -0
  169. data/spec/unit/ip_address_spec.rb +109 -0
  170. data/spec/unit/json_spec.rb +127 -0
  171. data/spec/unit/paranoid_boolean_spec.rb +142 -0
  172. data/spec/unit/paranoid_datetime_spec.rb +149 -0
  173. data/spec/unit/regexp_spec.rb +62 -0
  174. data/spec/unit/uri_spec.rb +64 -0
  175. data/spec/unit/yaml_spec.rb +111 -0
  176. data/tasks/spec.rake +40 -0
  177. data/tasks/yard.rake +9 -0
  178. data/tasks/yardstick.rake +19 -0
  179. metadata +350 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Mzk3NGRlZjc5N2E0Nzc4MjFkZmI5ZDA3MWNiNDVjNTE3NzQ1ZTM5Mg==
5
+ data.tar.gz: !binary |-
6
+ YmZlNzI5MzMzMjdlZDQwYjExZjcxYjZiNjA5YTkyZTBmM2QxOGZlMQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ZmViYjY2MmFiMjRmOGZiM2U1M2ZhY2MyN2I4YzJhZDc4NDcyYTcwZWE3ODQy
10
+ MTA5OWQ2ODUzMmRhN2M3ZWM2MWUyODkxOWU0ZmU2OTFjZGIwN2Y1ZmVkZGNh
11
+ Nzk3M2I3NzhmOTNiZDcxNjVkY2E2ZWE2MTY1MmQ1NGIwMjc3OTI=
12
+ data.tar.gz: !binary |-
13
+ NzcxYzU2NDRiMjU5NjVkYTVhNjNiZGEyMGRhYWVlYmYxOTc2ZDBhN2Q0YjE2
14
+ NzI3MTYzNDg5ODRjZTY1Y2U1N2JiYmMzZDdmNDlhYTBkYmZiYTJiMmFjMjk1
15
+ MWUzMmExYjQ5NGQxN2IxZjc2ZTIwZGU0ZGIzNzE4ZGM5Y2JiYTg=
data/.gitignore ADDED
@@ -0,0 +1,35 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+ ._*
4
+
5
+ ## TEXTMATE
6
+ *.tmproj
7
+ tmtags
8
+
9
+ ## EMACS
10
+ *~
11
+ \#*
12
+ .\#*
13
+
14
+ ## VIM
15
+ *.swp
16
+
17
+ ## Rubinius
18
+ *.rbc
19
+
20
+ ## PROJECT::GENERAL
21
+ *.gem
22
+ coverage
23
+ rdoc
24
+ pkg
25
+ tmp
26
+ doc
27
+ log
28
+ .yardoc
29
+ measurements
30
+
31
+ ## BUNDLER
32
+ .bundle
33
+ Gemfile.*
34
+
35
+ ## PROJECT::SPECIFIC
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ gem 'pry'
6
+ gem 'pry-debugger'
7
+
8
+ group :test do
9
+ gem 'sqlite3'
10
+ gem 'activerecord', '~> 4.0.0'
11
+ gem 'addressable'
12
+ gem 'database_cleaner'
13
+ end
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Code to adapt dm-types and dm-core properties to Active Record copyright (c) 2013 Martin Emde
2
+ Code from dm-types and dm-core originally Copyright (c) 2011 Sam Smoot, Dan Kubb
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,70 @@
1
+ # Ardm
2
+
3
+ ActiveRecord plugin to provide a smooth migration from DataMapper to ActiveRecord.
4
+
5
+ ## Why
6
+
7
+ Ardm is intended for applications running Rails with DataMapper 1.2 who need
8
+ to migrate to ActiveRecord. Lets examine some of the reasons why you might
9
+ move to ActiveRecord.
10
+
11
+ * DataMapper is no longer under development. Ruby Object Mapper (ROM) is the
12
+ imlicit replacement for DataMapper, but it's not a supported migration. ROM
13
+ is a completely new codebase and very few of the idioms transfer.
14
+ * DataMapper produces inefficient queries. Includes, joins, and subqueries are
15
+ either not supported or incorrect. Enabling subqueries in DM speeds up queries
16
+ but causes other subtle query problems than can produce bad SQL that may
17
+ select incorrect records.
18
+ * DataMapper cannot currently run on Rails4. Someone may take the initiative to
19
+ upgrade DataMapper to work on Rails4, but I think you're much better off
20
+ moving to ActiveRecord.
21
+ * Arel is awesome. ActiveRecord and Arel together is quite nice. With the added
22
+ support properties and the advances in Rails4, I think this upgrade is a must.
23
+ * ActiveRecord is used in more applications, better tested, and more performant.
24
+
25
+ ## Installation
26
+
27
+ Incremental migration from DataMapper to ActiveRecord.
28
+
29
+ ActiveRecord requires your models to inherit from ActiveRecord::Base, but makes
30
+ it difficult to approach the migration incrementally. All or nothing is a scary
31
+ way to switch ORMs. To solve this, Ardm supplies Ardm::Record.
32
+
33
+ Ardm::Record will be the new base class. You'll need to search and replace
34
+ all models that include DataMapper::Resource, remove it, and add Ardm::Record
35
+ as the base class. If your model is STI, add it to the base model and remove
36
+ DataMapper::Resource from all models.
37
+
38
+ Example:
39
+
40
+ class MyModel
41
+ include DataMapper::Resource
42
+ # ...
43
+ end
44
+
45
+ # The model above changes to:
46
+
47
+ class MyModel < Ardm::Record
48
+ # ...
49
+ end
50
+
51
+ With this new base clase you can switch between ActiveRecord and DataMapper
52
+ by flipping a swith in your application. This approach allows you to continue
53
+ developing your application in DataMapper while you work on teasing out all
54
+ the "datamapper-isms" from your code. This library attempts to take care of
55
+ most DataMapper features, but there are probably tons of small variations
56
+ that are not accounted for.
57
+
58
+ ## Usage
59
+
60
+ require 'ardm'
61
+
62
+
63
+
64
+ ## Copyright
65
+
66
+ This is an adaptation of the original DataMapper source code to allow
67
+ users of the now defunct DataMapper to migrate to ActiveRecord.
68
+ Much of this code was originally written by Sam Smoot and Dan Kubb as
69
+ [dm-types](https://github.com/datamapper/dm-types) and
70
+ [dm-core](https://github.com/datamapper/dm-core). See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ FileList['tasks/**/*.rake'].each { |task| import task }
data/ardm.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/ardm/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = [ 'Martin Emde', 'Dan Kubb' ]
6
+ gem.email = [ "me@martinemde.com" ]
7
+ gem.summary = "ActiveRecord plugin to provide a smooth migration from DataMapper to ActiveRecord"
8
+ gem.description = gem.summary
9
+ gem.homepage = "http://github.com/engineyard/ardm"
10
+
11
+ gem.files = `git ls-files`.split("\n")
12
+ gem.test_files = `git ls-files -- {spec}/*`.split("\n")
13
+ gem.extra_rdoc_files = %w[LICENSE README.md]
14
+
15
+ gem.name = "ardm"
16
+ gem.require_paths = [ "lib" ]
17
+ gem.version = Ardm::VERSION
18
+
19
+ gem.add_runtime_dependency('activesupport', '>= 3.2')
20
+ gem.add_runtime_dependency('bcrypt-ruby', '~> 3.0.0')
21
+ gem.add_runtime_dependency('fastercsv', '~> 1.5.4')
22
+ gem.add_runtime_dependency('multi_json', '> 1.3.2')
23
+ gem.add_runtime_dependency('stringex', '~> 1.3.3')
24
+ gem.add_runtime_dependency('uuidtools', '~> 2.1.2')
25
+ gem.add_runtime_dependency('coercible')
26
+
27
+ gem.add_development_dependency('rake', '~> 0.9.2')
28
+ gem.add_development_dependency('rspec', '~> 2.0')
29
+ end
data/db/.gitignore ADDED
@@ -0,0 +1 @@
1
+ *.sqlite
@@ -0,0 +1,80 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ardm
4
+ module ActiveRecord
5
+ module Associations
6
+ extend ActiveSupport::Concern
7
+
8
+ def assign_attributes(attrs, *a)
9
+ new_attrs = attrs.inject({}) do |memo,(k,v)|
10
+ if assoc = self.class.reflect_on_association(k)
11
+ memo[assoc.foreign_key] = v && v.send(v.class.primary_key)
12
+ else
13
+ memo[k] = v
14
+ end
15
+ memo
16
+ end
17
+ super new_attrs, *a
18
+ end
19
+
20
+
21
+ # Convert options from DM style to AR style.
22
+ #
23
+ # Keep any unknown keys to use as conditions.
24
+ def self.convert_options(klass, options, *keep)
25
+ keep += [:class_name, :foreign_key]
26
+
27
+ ar = options.dup
28
+ ar[:class_name] = ar.delete(:model) if ar[:model]
29
+ ar[:foreign_key] = ar.delete(:child_key) if ar[:child_key]
30
+ ar[:foreign_key] = ar[:foreign_key].first if ar[:foreign_key].respond_to?(:to_ary)
31
+
32
+ if ar[:foreign_key] && property = klass.properties[ar[:foreign_key]]
33
+ ar[:foreign_key] = property.field
34
+ end
35
+
36
+ if (conditions = ar.slice!(*keep)).any?
37
+ ar[:conditions] = conditions
38
+ end
39
+ ar
40
+ end
41
+
42
+ module ClassMethods
43
+ def belongs_to(field, options={})
44
+ options.delete(:default)
45
+ options.delete(:required)
46
+ opts = Ardm::ActiveRecord::Associations.convert_options(self, options)
47
+ super field, opts
48
+ assoc = reflect_on_association(field)
49
+ property assoc.foreign_key, Ardm::Property::Integer
50
+ nil
51
+ end
52
+
53
+ def n
54
+ "many"
55
+ end
56
+
57
+ def has(count, name, *args)
58
+ options = args.shift || {}
59
+
60
+ if String === options # has n, :name, 'Class', options: 'here'
61
+ options = (args.last || {}).merge(:model => options)
62
+ end
63
+
64
+ unless Hash === options
65
+ raise ArgumentError, "bad has #{count} options format #{options.inspect}"
66
+ end
67
+
68
+ options[:order] = Ardm::ActiveRecord::Query.order(self, options[:order]) if options[:order]
69
+ opts = Ardm::ActiveRecord::Associations.convert_options(self, options, :through, :order)
70
+
71
+ case count
72
+ when 1 then has_one name, opts
73
+ when "many" then has_many name, opts
74
+ end
75
+ end
76
+
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ require 'active_support/concern'
2
+
3
+ require 'ardm/active_record/associations'
4
+ require 'ardm/active_record/dirty'
5
+ require 'ardm/active_record/hooks'
6
+ require 'ardm/active_record/is'
7
+ require 'ardm/active_record/inheritance'
8
+ require 'ardm/active_record/property'
9
+ require 'ardm/active_record/query'
10
+ require 'ardm/active_record/repository'
11
+ require 'ardm/active_record/storage_names'
12
+ require 'ardm/active_record/validations'
13
+
14
+ module Ardm
15
+ module ActiveRecord
16
+ # Include all the Ardm modules.
17
+ #
18
+ # You can use this directly if you want your own abstract base class.
19
+ #
20
+ # require 'ardm/active_record/base'
21
+ #
22
+ # class MyRecord < ActiveRecord::Base
23
+ # include Ardm::ActiveRecord::Base
24
+ # end
25
+ #
26
+ # Or Ardm::ActiveRecord::Base is built in to Ardm::Record
27
+ #
28
+ # require 'ardm/active_record/record'
29
+ #
30
+ # class MyRecord < Ardm::Record
31
+ # # already included
32
+ # end
33
+ #
34
+ module Base
35
+ extend ActiveSupport::Concern
36
+
37
+ include Ardm::ActiveRecord::Associations
38
+ include Ardm::ActiveRecord::Hooks
39
+ include Ardm::ActiveRecord::Dirty
40
+ include Ardm::ActiveRecord::Is
41
+ include Ardm::ActiveRecord::Inheritance
42
+ include Ardm::ActiveRecord::Property
43
+ include Ardm::ActiveRecord::Query
44
+ include Ardm::ActiveRecord::Repository
45
+ include Ardm::ActiveRecord::StorageNames
46
+ include Ardm::ActiveRecord::Validations
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ module Ardm
2
+ module ActiveRecord
3
+ module Dirty
4
+ def dirty?
5
+ changed?
6
+ end
7
+
8
+ def dirty_attributes
9
+ changes.inject({}) do |memo, (attr, val)|
10
+ property = properties[attr]
11
+ memo[property] = val
12
+ memo
13
+ end
14
+ end
15
+
16
+ def method_missing(meth, *args, &block)
17
+ if meth.to_s =~ /^([\w_]+)_dirty\?$/
18
+ send("#{$1}_changed?", *args, &block)
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,31 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ardm
4
+ module ActiveRecord
5
+ module Hooks
6
+ extend ActiveSupport::Concern
7
+
8
+ module ClassMethods
9
+ def before(event, meth=nil, &block)
10
+ _ardm_hook(:before, event, meth, &block)
11
+ end
12
+
13
+ def after(event, meth=nil, &block)
14
+ _ardm_hook(:after, event, meth, &block)
15
+ end
16
+
17
+ def _ardm_hook(order, event, meth=nil, &block)
18
+ if event.to_sym == :valid?
19
+ event = "validation"
20
+ end
21
+
22
+ if meth.nil?
23
+ send "#{order}_#{event}", &block
24
+ else
25
+ send "#{order}_#{event}", meth
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,37 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ardm
4
+ module ActiveRecord
5
+ module Inheritance
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # ActiveRecord would prefer that you not use the column "type"
10
+ # for anything other than single table inheritance.
11
+ # The solution is to point ActiveRecord elsewhere.
12
+ unless respond_to?(:inheritance_column=)
13
+ class_attribute :inheritance_column
14
+ end
15
+ self.inheritance_column = "override-active-record-default-sti-column-type"
16
+ end
17
+
18
+ module ClassMethods
19
+ def new(attrs={}, *a, &b)
20
+ type = attrs && attrs.stringify_keys[inheritance_column.to_s]
21
+ if type && type != name && type != self
22
+ #puts "STI found for #{type} #{self}"
23
+ con = type.is_a?(Class) ? type : type.constantize
24
+ if con < self
25
+ con.new(attrs, *a, &b)
26
+ else
27
+ raise "Tried to create subclass from #{type} (from key #{inheritance_column}) that is not a subclass of #{name}."
28
+ end
29
+ else
30
+ #puts "No STI found for #{self} (#{attrs.inspect})"
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,21 @@
1
+ module Ardm
2
+ module ActiveRecord
3
+ module Is
4
+ module StateMachine
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ include AASM
9
+ end
10
+
11
+ module ClassMethods
12
+ def is_state_machine(options, &block)
13
+ STDERR.puts "TODO: dm state machine on #{self}"
14
+ property options[:column], Ardm::Property::String, default: options[:initial]
15
+ aasm column: options[:column], &block
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,22 @@
1
+ require 'active_support/concern'
2
+ require 'ardm/active_record/is/state_machine'
3
+
4
+ module Ardm
5
+ module ActiveRecord
6
+ module Is
7
+ extend ActiveSupport::Concern
8
+
9
+ module ClassMethods
10
+ def is(target, options={}, &block)
11
+ case target
12
+ when :state_machine
13
+ include Ardm::ActiveRecord::Is::StateMachine
14
+ is_state_machine(options, &block)
15
+ else
16
+ STDERR.puts "TODO: #{self} is #{target.inspect}, #{options.inspect}"
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,7 @@
1
+ require 'ardm/active_record/record'
2
+
3
+ module Ardm
4
+ class Record < ::ActiveRecord::Base
5
+ NotFound = ::ActiveRecord::RecordNotFound
6
+ end
7
+ end
@@ -0,0 +1,31 @@
1
+ module Ardm
2
+ module ActiveRecord
3
+ module PredicateBuilder
4
+ class ArrayHandler # :nodoc:
5
+ def call(attribute, value)
6
+ values = value.map { |x| x.is_a?(::ActiveRecord::Base) ? x.id : x }
7
+ ranges, values = values.partition { |v| v.is_a?(::Range) }
8
+
9
+ values_predicate = if values.include?(nil)
10
+ values = values.compact
11
+
12
+ case values.length
13
+ when 0
14
+ attribute.eq(nil)
15
+ when 1
16
+ attribute.eq(values.first).or(attribute.eq(nil))
17
+ else
18
+ attribute.in(values).or(attribute.eq(nil))
19
+ end
20
+ else
21
+ attribute.in(values)
22
+ end
23
+
24
+ array_predicates = ranges.map { |range| attribute.in(range) }
25
+ array_predicates << values_predicate
26
+ array_predicates.inject { |composite, predicate| composite.or(predicate) }
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,147 @@
1
+ require 'active_support/concern'
2
+
3
+ module Ardm
4
+ module ActiveRecord
5
+ module PredicateBuilder
6
+ module Rails3
7
+ extend ActiveSupport::Concern
8
+
9
+ Base = ::ActiveRecord::Base
10
+ Relation = ::ActiveRecord::Relation
11
+ Collection = ::ActiveRecord::Associations::CollectionProxy
12
+
13
+ included do
14
+ class << self
15
+ alias_method :original_build_from_hash, :build_from_hash
16
+ end
17
+
18
+ class_attribute :handlers
19
+ self.handlers = []
20
+
21
+ register_handler(BasicObject, ->(attribute, value) { attribute.eq(value) })
22
+ register_handler(Class, ->(attribute, value) { attribute.eq(value.name) })
23
+ register_handler(Base, ->(attribute, value) { attribute.eq(value.id) })
24
+ register_handler(Range, ->(attribute, value) { attribute.in(value) })
25
+ register_handler(Relation, RelationHandler.new)
26
+ register_handler(Collection, ArrayHandler.new)
27
+ register_handler(Array, ArrayHandler.new)
28
+ end
29
+
30
+ module ClassMethods
31
+ def resolve_column_aliases(klass, hash)
32
+ hash = hash.dup
33
+ hash.keys.grep(Symbol) do |key|
34
+ if klass.attribute_alias? key
35
+ hash[klass.attribute_alias(key)] = hash.delete key
36
+ end
37
+ end
38
+ hash
39
+ end
40
+
41
+ def build_from_hash(klass, attributes, default_table)
42
+ queries = []
43
+ klass = attributes.klass # HAX (this method is added to the attributes hash by expand_hash_conditions_for_aggregates
44
+
45
+ attributes.each do |column, value|
46
+ table = default_table
47
+
48
+ if value.is_a?(Hash)
49
+ if value.empty?
50
+ queries << '1=0'
51
+ else
52
+ table = Arel::Table.new(column, default_table.engine)
53
+ association = klass.reflect_on_association(column.to_sym)
54
+
55
+ value.each do |k, v|
56
+ queries.concat expand(association && association.klass, table, k, v)
57
+ end
58
+ end
59
+ else
60
+ if Ardm::Query::Operator === column
61
+ original = column
62
+ operator = column.operator
63
+ column = column.target.to_s
64
+ else
65
+ column = column.to_s
66
+ end
67
+
68
+ if column.include?('.')
69
+ table_name, column = column.split('.', 2)
70
+ table = Arel::Table.new(table_name, default_table.engine)
71
+ end
72
+
73
+ query = expand(klass, table, column, value)
74
+ # TODO make nicer
75
+ if operator == :not
76
+ # Logical not factorization !(a && b) == (!a || !b)
77
+ query.map! &:not
78
+ query = [query.inject { |composite, predicate| composite.or(predicate) }]
79
+ end
80
+ queries.concat query
81
+ end
82
+ end
83
+
84
+ queries
85
+ end
86
+
87
+ def expand(klass, table, column, value)
88
+ queries = []
89
+
90
+ # Find the foreign key when using queries such as:
91
+ # Post.where(author: author)
92
+ #
93
+ # For polymorphic relationships, find the foreign key and type:
94
+ # PriceEstimate.where(estimate_of: treasure)
95
+ if klass && reflection = klass.reflect_on_association(column.to_sym)
96
+ column = reflection.foreign_key
97
+ if value.is_a?(Base) && reflection.respond_to?(:polymorphic?) && reflection.polymorphic?
98
+ queries << build(table[reflection.foreign_type], value.class.base_class)
99
+ end
100
+ end
101
+
102
+ #puts "expand(#{klass.name}, #{column.inspect}, #{value.inspect})"
103
+
104
+ queries << build(table[column], value)
105
+ queries
106
+ end
107
+
108
+ def references(attributes)
109
+ attributes.map do |key, value|
110
+ if value.is_a?(Hash)
111
+ key
112
+ else
113
+ key = key.to_s
114
+ key.split('.').first if key.include?('.')
115
+ end
116
+ end.compact
117
+ end
118
+
119
+ # Define how a class is converted to Arel nodes when passed to +where+.
120
+ # The handler can be any object that responds to +call+, and will be used
121
+ # for any value that +===+ the class given. For example:
122
+ #
123
+ # MyCustomDateRange = Struct.new(:start, :end)
124
+ # handler = proc do |column, range|
125
+ # Arel::Nodes::Between.new(column,
126
+ # Arel::Nodes::And.new([range.start, range.end])
127
+ # )
128
+ # end
129
+ # ActiveRecord::PredicateBuilder.register_handler(MyCustomDateRange, handler)
130
+ def register_handler(klass, handler)
131
+ handlers.unshift([klass, handler])
132
+ end
133
+
134
+ private
135
+
136
+ def build(attribute, value)
137
+ handler_for(value).call(attribute, value)
138
+ end
139
+
140
+ def handler_for(object)
141
+ handlers.detect { |klass, _| klass === object }.last
142
+ end
143
+ end
144
+ end
145
+ end
146
+ end
147
+ end