ardm 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +35 -0
- data/Gemfile +13 -0
- data/LICENSE +21 -0
- data/README.md +70 -0
- data/Rakefile +4 -0
- data/ardm.gemspec +29 -0
- data/db/.gitignore +1 -0
- data/lib/ardm/active_record/associations.rb +80 -0
- data/lib/ardm/active_record/base.rb +49 -0
- data/lib/ardm/active_record/dirty.rb +25 -0
- data/lib/ardm/active_record/hooks.rb +31 -0
- data/lib/ardm/active_record/inheritance.rb +37 -0
- data/lib/ardm/active_record/is/state_machine.rb +21 -0
- data/lib/ardm/active_record/is.rb +22 -0
- data/lib/ardm/active_record/not_found.rb +7 -0
- data/lib/ardm/active_record/predicate_builder/array_handler.rb +31 -0
- data/lib/ardm/active_record/predicate_builder/rails3.rb +147 -0
- data/lib/ardm/active_record/predicate_builder/rails4.rb +139 -0
- data/lib/ardm/active_record/predicate_builder/relation_handler.rb +15 -0
- data/lib/ardm/active_record/predicate_builder.rb +19 -0
- data/lib/ardm/active_record/property.rb +357 -0
- data/lib/ardm/active_record/query.rb +108 -0
- data/lib/ardm/active_record/record.rb +70 -0
- data/lib/ardm/active_record/relation.rb +83 -0
- data/lib/ardm/active_record/repository.rb +38 -0
- data/lib/ardm/active_record/serialization.rb +164 -0
- data/lib/ardm/active_record/storage_names.rb +28 -0
- data/lib/ardm/active_record/validations.rb +111 -0
- data/lib/ardm/active_record.rb +43 -0
- data/lib/ardm/data_mapper/not_found.rb +5 -0
- data/lib/ardm/data_mapper/record.rb +41 -0
- data/lib/ardm/data_mapper.rb +5 -0
- data/lib/ardm/env.rb +5 -0
- data/lib/ardm/property/api_key.rb +30 -0
- data/lib/ardm/property/bcrypt_hash.rb +31 -0
- data/lib/ardm/property/binary.rb +23 -0
- data/lib/ardm/property/boolean.rb +29 -0
- data/lib/ardm/property/class.rb +19 -0
- data/lib/ardm/property/comma_separated_list.rb +28 -0
- data/lib/ardm/property/csv.rb +35 -0
- data/lib/ardm/property/date.rb +12 -0
- data/lib/ardm/property/datetime.rb +12 -0
- data/lib/ardm/property/decimal.rb +38 -0
- data/lib/ardm/property/discriminator.rb +65 -0
- data/lib/ardm/property/enum.rb +51 -0
- data/lib/ardm/property/epoch_time.rb +38 -0
- data/lib/ardm/property/file_path.rb +25 -0
- data/lib/ardm/property/flag.rb +65 -0
- data/lib/ardm/property/float.rb +18 -0
- data/lib/ardm/property/integer.rb +24 -0
- data/lib/ardm/property/invalid_value_error.rb +22 -0
- data/lib/ardm/property/ip_address.rb +35 -0
- data/lib/ardm/property/json.rb +49 -0
- data/lib/ardm/property/lookup.rb +29 -0
- data/lib/ardm/property/numeric.rb +40 -0
- data/lib/ardm/property/object.rb +36 -0
- data/lib/ardm/property/paranoid_boolean.rb +18 -0
- data/lib/ardm/property/paranoid_datetime.rb +17 -0
- data/lib/ardm/property/regexp.rb +22 -0
- data/lib/ardm/property/serial.rb +16 -0
- data/lib/ardm/property/slug.rb +29 -0
- data/lib/ardm/property/string.rb +40 -0
- data/lib/ardm/property/support/dirty_minder.rb +169 -0
- data/lib/ardm/property/support/flags.rb +41 -0
- data/lib/ardm/property/support/paranoid_base.rb +78 -0
- data/lib/ardm/property/text.rb +11 -0
- data/lib/ardm/property/time.rb +12 -0
- data/lib/ardm/property/uri.rb +27 -0
- data/lib/ardm/property/uuid.rb +65 -0
- data/lib/ardm/property/validation.rb +208 -0
- data/lib/ardm/property/yaml.rb +38 -0
- data/lib/ardm/property.rb +891 -0
- data/lib/ardm/property_set.rb +152 -0
- data/lib/ardm/query/expression.rb +85 -0
- data/lib/ardm/query/ext/symbol.rb +37 -0
- data/lib/ardm/query/operator.rb +64 -0
- data/lib/ardm/record.rb +1 -0
- data/lib/ardm/support/assertions.rb +8 -0
- data/lib/ardm/support/deprecate.rb +12 -0
- data/lib/ardm/support/descendant_set.rb +89 -0
- data/lib/ardm/support/equalizer.rb +48 -0
- data/lib/ardm/support/ext/array.rb +22 -0
- data/lib/ardm/support/ext/blank.rb +25 -0
- data/lib/ardm/support/ext/hash.rb +67 -0
- data/lib/ardm/support/ext/module.rb +47 -0
- data/lib/ardm/support/ext/object.rb +57 -0
- data/lib/ardm/support/ext/string.rb +24 -0
- data/lib/ardm/support/ext/try_dup.rb +12 -0
- data/lib/ardm/support/hook.rb +405 -0
- data/lib/ardm/support/lazy_array.rb +451 -0
- data/lib/ardm/support/local_object_space.rb +13 -0
- data/lib/ardm/support/logger.rb +201 -0
- data/lib/ardm/support/mash.rb +176 -0
- data/lib/ardm/support/naming_conventions.rb +90 -0
- data/lib/ardm/support/ordered_set.rb +380 -0
- data/lib/ardm/support/subject.rb +33 -0
- data/lib/ardm/support/subject_set.rb +250 -0
- data/lib/ardm/version.rb +3 -0
- data/lib/ardm.rb +56 -0
- data/spec/fixtures/api_user.rb +11 -0
- data/spec/fixtures/article.rb +22 -0
- data/spec/fixtures/bookmark.rb +14 -0
- data/spec/fixtures/invention.rb +5 -0
- data/spec/fixtures/network_node.rb +23 -0
- data/spec/fixtures/person.rb +17 -0
- data/spec/fixtures/software_package.rb +22 -0
- data/spec/fixtures/ticket.rb +12 -0
- data/spec/fixtures/tshirt.rb +15 -0
- data/spec/integration/api_key_spec.rb +25 -0
- data/spec/integration/bcrypt_hash_spec.rb +45 -0
- data/spec/integration/comma_separated_list_spec.rb +85 -0
- data/spec/integration/dirty_minder_spec.rb +197 -0
- data/spec/integration/enum_spec.rb +79 -0
- data/spec/integration/epoch_time_spec.rb +59 -0
- data/spec/integration/file_path_spec.rb +158 -0
- data/spec/integration/flag_spec.rb +72 -0
- data/spec/integration/ip_address_spec.rb +151 -0
- data/spec/integration/json_spec.rb +70 -0
- data/spec/integration/slug_spec.rb +65 -0
- data/spec/integration/uri_spec.rb +136 -0
- data/spec/integration/uuid_spec.rb +102 -0
- data/spec/integration/yaml_spec.rb +85 -0
- data/spec/public/property/binary_spec.rb +41 -0
- data/spec/public/property/boolean_spec.rb +30 -0
- data/spec/public/property/class_spec.rb +28 -0
- data/spec/public/property/date_spec.rb +22 -0
- data/spec/public/property/date_time_spec.rb +22 -0
- data/spec/public/property/decimal_spec.rb +23 -0
- data/spec/public/property/discriminator_spec.rb +133 -0
- data/spec/public/property/float_spec.rb +22 -0
- data/spec/public/property/integer_spec.rb +22 -0
- data/spec/public/property/object_spec.rb +103 -0
- data/spec/public/property/serial_spec.rb +22 -0
- data/spec/public/property/string_spec.rb +22 -0
- data/spec/public/property/text_spec.rb +23 -0
- data/spec/public/property/time_spec.rb +22 -0
- data/spec/public/property_spec.rb +316 -0
- data/spec/rcov.opts +6 -0
- data/spec/schema.rb +86 -0
- data/spec/semipublic/property/binary_spec.rb +14 -0
- data/spec/semipublic/property/boolean_spec.rb +48 -0
- data/spec/semipublic/property/class_spec.rb +36 -0
- data/spec/semipublic/property/date_spec.rb +44 -0
- data/spec/semipublic/property/date_time_spec.rb +47 -0
- data/spec/semipublic/property/decimal_spec.rb +83 -0
- data/spec/semipublic/property/discriminator_spec.rb +22 -0
- data/spec/semipublic/property/float_spec.rb +83 -0
- data/spec/semipublic/property/integer_spec.rb +83 -0
- data/spec/semipublic/property/lookup_spec.rb +27 -0
- data/spec/semipublic/property/serial_spec.rb +14 -0
- data/spec/semipublic/property/string_spec.rb +14 -0
- data/spec/semipublic/property/text_spec.rb +30 -0
- data/spec/semipublic/property/time_spec.rb +49 -0
- data/spec/semipublic/property_spec.rb +51 -0
- data/spec/shared/flags_shared_spec.rb +36 -0
- data/spec/shared/identity_function_group.rb +5 -0
- data/spec/shared/public_property_spec.rb +229 -0
- data/spec/shared/semipublic_property_spec.rb +159 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +58 -0
- data/spec/unit/bcrypt_hash_spec.rb +154 -0
- data/spec/unit/csv_spec.rb +139 -0
- data/spec/unit/dirty_minder_spec.rb +64 -0
- data/spec/unit/enum_spec.rb +125 -0
- data/spec/unit/epoch_time_spec.rb +72 -0
- data/spec/unit/file_path_spec.rb +75 -0
- data/spec/unit/flag_spec.rb +114 -0
- data/spec/unit/ip_address_spec.rb +109 -0
- data/spec/unit/json_spec.rb +127 -0
- data/spec/unit/paranoid_boolean_spec.rb +142 -0
- data/spec/unit/paranoid_datetime_spec.rb +149 -0
- data/spec/unit/regexp_spec.rb +62 -0
- data/spec/unit/uri_spec.rb +64 -0
- data/spec/unit/yaml_spec.rb +111 -0
- data/tasks/spec.rake +40 -0
- data/tasks/yard.rake +9 -0
- data/tasks/yardstick.rake +19 -0
- 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
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
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,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
|