ardm 0.0.1

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 (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