active_any 0.0.1 → 0.0.2
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.
- checksums.yaml +4 -4
- data/.travis.yml +15 -2
- data/README.md +23 -12
- data/Rakefile +1 -1
- data/lib/active_any/adapters/abstract_adapter.rb +0 -12
- data/lib/active_any/associations/association.rb +28 -1
- data/lib/active_any/associations/has_many_association.rb +5 -0
- data/lib/active_any/associations/preloader/association.rb +130 -0
- data/lib/active_any/associations/preloader/belongs_to.rb +26 -0
- data/lib/active_any/associations/preloader/has_many.rb +25 -0
- data/lib/active_any/associations/preloader.rb +111 -0
- data/lib/active_any/associations.rb +8 -7
- data/lib/active_any/attribute.rb +71 -0
- data/lib/active_any/base.rb +48 -0
- data/lib/active_any/configuration.rb +19 -0
- data/lib/active_any/core.rb +17 -1
- data/lib/active_any/reflection/association_reflection.rb +65 -0
- data/lib/active_any/reflection.rb +6 -0
- data/lib/active_any/relation/finder_methods.rb +34 -0
- data/lib/active_any/relation/query_methods.rb +145 -0
- data/lib/active_any/relation.rb +34 -150
- data/lib/active_any/subscriber.rb +20 -0
- data/lib/active_any/version.rb +1 -1
- data/lib/active_any.rb +8 -40
- metadata +12 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7c70ba8be173563a70f75ae70628ddddb8dc66b9
|
4
|
+
data.tar.gz: 3b21c6608095ba05c84b2ab1256771434a567a75
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7e7c7311ed6633a140b529ce3acdadd7858e728322f15707c45a93a2980d14f271b3fd23e8f385604a1f9a3512dea1451ff00c93037d31c172762e3912c9951a
|
7
|
+
data.tar.gz: d3eccc65ad03a3b637bfd49ceb52f87702a36ffc58e5b74b3c11cadf65085f2ee381b671b6fba517e01f582589b185d5367f61449aabbd20ba9d350c942a4992
|
data/.travis.yml
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
sudo: false
|
2
2
|
language: ruby
|
3
3
|
rvm:
|
4
|
-
|
5
|
-
|
4
|
+
- 2.2.7
|
5
|
+
- 2.3.4
|
6
|
+
- 2.4.1
|
7
|
+
before_install:
|
8
|
+
- gem update --system
|
9
|
+
- gem install bundler -v 1.15.3
|
10
|
+
cache: bundler
|
11
|
+
script:
|
12
|
+
- bundle exec rubocop
|
13
|
+
- bundle exec rake test
|
14
|
+
notifications:
|
15
|
+
slack:
|
16
|
+
secure: S1W/Lw+dH3wb8FfkMIWPZmr6M4Q6S2WMkSlanpKva1HM7K5QL7hXdmUl2yBUxJE26BHSsb1ScozMEzadyda2+i/W34UvZ7LXgKeHkUKEdjy/AmsSJPK1ZjMfgnv10tVgbEIusNb4bF/sSuChdZKK3ILwOlqIPDlQNdMwF1xRA2xt5J7tb26UgyIzoCI4P3bJYMULWsEkk+UwHiJH0YO9ulkTZI/j0N+hLXQLJTZPjmKtMk/tE0NbBmFVL4md89hUcR5gKTGGrNzEMJ58K+zqeDG/DubkcIbA5ZuqKv+oE5m0pDODZExxnC+oeENTvq/VfYwOfD0pTDrBNYjj+Bm3YiyGDzQAgov9XPDG8g/fKEs/LNAT79UZXkZlFO99Yn/vrYH9o5DKpOE9smENUXylb55MgLTUiYe17CTp7pB3trbJl3wwIbLjSmTjAdSUNgPv8qDP4uk3K4U32mknXCDDkU9EI7f6F731ocdoxsGarEBcPcgjs73Y84iwDteQp847Gigtgo4Y4TCWH657uzLolR2O8NSw+vWT0VNI9qtR5PZD7iVYtSp1qHtPAKowCztodewY2Nu+Ds9Z95udf4GPUkFg/SNEJPTPrQFLiiJZ8UYP8NJEuA+IP1tc2zG3zU/ADrjenRC1ZiupQG7OMH82y11408U6PcHFSlF+7NuDkac=
|
17
|
+
on_success: change
|
18
|
+
on_failure: always
|
data/README.md
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# ActiveAny
|
2
2
|
|
3
|
+
[](https://travis-ci.org/yuemori/active_any)
|
4
|
+
|
3
5
|
A utility for quering interface to any objects like ActiveRecord. This gem support for querying, association and relation.
|
4
6
|
|
5
|
-
This gem is not stable version. Be careful
|
7
|
+
**This gem is not stable version. Be careful.**
|
6
8
|
|
7
9
|
## Installation
|
8
10
|
|
@@ -46,8 +48,8 @@ end
|
|
46
48
|
|
47
49
|
User.all #=> Relation
|
48
50
|
User.all.to_a #=> Array of User
|
49
|
-
User.where(name: 'alice') #=> WHERE name
|
50
|
-
User.where(name: /li/) #=> WHERE name LIKE li (alice, charlie)
|
51
|
+
User.where(name: 'alice') #=> WHERE name = alice
|
52
|
+
User.where(name: /li/) #=> WHERE name LIKE %li% (alice, charlie)
|
51
53
|
User.group(:name) #=> GROUP BY name
|
52
54
|
User.order(:age) #=> ORDER BY age
|
53
55
|
User.order(age: :desc) #=> ORDER BY age DESC
|
@@ -61,15 +63,24 @@ Clone repository and run `bin/console`, if you want to try it!
|
|
61
63
|
|
62
64
|
## Features
|
63
65
|
|
64
|
-
-
|
65
|
-
- [] `joins`
|
66
|
-
- [] `
|
67
|
-
- [] `
|
68
|
-
- []
|
69
|
-
- []
|
70
|
-
- []
|
71
|
-
-
|
72
|
-
- []
|
66
|
+
- More powerful interface for select query
|
67
|
+
- [ ] `joins` (interface only)
|
68
|
+
- [ ] `preload` (interface only)
|
69
|
+
- [ ] `eager_load` (interface only)
|
70
|
+
- [x] `includes`
|
71
|
+
- [ ] `having`
|
72
|
+
- [ ] `offset`
|
73
|
+
- Adapters
|
74
|
+
- [ ] YAML
|
75
|
+
- [ ] JSON
|
76
|
+
- [ ] CSV
|
77
|
+
- ActiveRecord like interfaces
|
78
|
+
- [ ] Interface for other than select query (create, destroy, update)
|
79
|
+
- [ ] enum support
|
80
|
+
- [x] subscriber
|
81
|
+
- [ ] scoping
|
82
|
+
- Integration
|
83
|
+
- [ ] ActiveRecord
|
73
84
|
|
74
85
|
## Testing
|
75
86
|
|
data/Rakefile
CHANGED
@@ -11,17 +11,5 @@ module ActiveAny
|
|
11
11
|
def query(_where_clause, _limit_value = nil, _group_values = [], _order_values = [])
|
12
12
|
raise NotImplementedError.new, "#{self.class.name} can not handle for #{value.class}"
|
13
13
|
end
|
14
|
-
|
15
|
-
def limit_handler(_records, _limit_value)
|
16
|
-
raise NotImplementedError.new, "#{self.class.name} can not handle for limit"
|
17
|
-
end
|
18
|
-
|
19
|
-
def group_handler(_records, _group_values)
|
20
|
-
raise NotImplementedError.new, "#{self.class.name} can not handle for group"
|
21
|
-
end
|
22
|
-
|
23
|
-
def order_handler(_records, _order_values)
|
24
|
-
raise NotImplementedError.new, "#{self.class.name} can not handle for order"
|
25
|
-
end
|
26
14
|
end
|
27
15
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
module ActiveAny
|
4
4
|
module Associations
|
5
5
|
class Association
|
6
|
-
attr_reader :loaded, :reflection, :target, :owner
|
6
|
+
attr_reader :loaded, :reflection, :target, :owner, :inversed
|
7
7
|
|
8
8
|
def initialize(owner, reflection)
|
9
9
|
@owner = owner
|
@@ -61,6 +61,7 @@ module ActiveAny
|
|
61
61
|
def reset
|
62
62
|
@loaded = false
|
63
63
|
@stale_target = nil
|
64
|
+
@inversed = false
|
64
65
|
end
|
65
66
|
|
66
67
|
def reset_scope
|
@@ -71,12 +72,17 @@ module ActiveAny
|
|
71
72
|
@loaded
|
72
73
|
end
|
73
74
|
|
75
|
+
def find_from_target?
|
76
|
+
loaded?
|
77
|
+
end
|
78
|
+
|
74
79
|
def find_target
|
75
80
|
raise NotImplementedError
|
76
81
|
end
|
77
82
|
|
78
83
|
def loaded!
|
79
84
|
@loaded = true
|
85
|
+
@inversed = false
|
80
86
|
end
|
81
87
|
|
82
88
|
def target=(target)
|
@@ -84,11 +90,32 @@ module ActiveAny
|
|
84
90
|
loaded!
|
85
91
|
end
|
86
92
|
|
93
|
+
def set_inverse_instance(record)
|
94
|
+
if invertible_for?(record)
|
95
|
+
inverse = record.association(inverse_reflection_for(record).name)
|
96
|
+
inverse.target = owner
|
97
|
+
inverse.inversed = true
|
98
|
+
end
|
99
|
+
record
|
100
|
+
end
|
101
|
+
|
87
102
|
private
|
88
103
|
|
104
|
+
def invertible_for?(record)
|
105
|
+
foreign_key_for?(record) && inverse_reflection_for(record)
|
106
|
+
end
|
107
|
+
|
108
|
+
def foreign_key_for?(record)
|
109
|
+
record.has_attribute?(reflection.foreign_key)
|
110
|
+
end
|
111
|
+
|
89
112
|
def find_target?
|
90
113
|
!loaded? && klass
|
91
114
|
end
|
115
|
+
|
116
|
+
def inverse_reflection_for(_record)
|
117
|
+
reflection.inverse_of
|
118
|
+
end
|
92
119
|
end
|
93
120
|
end
|
94
121
|
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_any/associations/preloader/association'
|
4
|
+
|
5
|
+
module ActiveAny
|
6
|
+
module Associations
|
7
|
+
class Preloader
|
8
|
+
class Association
|
9
|
+
attr_reader :reflection, :preload_scope, :owners, :klass, :preloaded_records
|
10
|
+
|
11
|
+
def initialize(klass, owner_records, reflection, preload_scope)
|
12
|
+
@klass = klass
|
13
|
+
@owners = owner_records
|
14
|
+
@reflection = reflection
|
15
|
+
@preload_scope = preload_scope
|
16
|
+
@preloaded_records = []
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(preloader)
|
20
|
+
preload(preloader)
|
21
|
+
end
|
22
|
+
|
23
|
+
def preload(_preloader)
|
24
|
+
raise NotImplementedError
|
25
|
+
end
|
26
|
+
|
27
|
+
def scope
|
28
|
+
@scope ||= build_scope
|
29
|
+
end
|
30
|
+
|
31
|
+
def records_for(ids)
|
32
|
+
scope.where(association_key_name => ids)
|
33
|
+
end
|
34
|
+
|
35
|
+
# The name of the key on the associated records
|
36
|
+
def association_key_name
|
37
|
+
raise NotImplementedError
|
38
|
+
end
|
39
|
+
|
40
|
+
def owner_key_name
|
41
|
+
raise NotImplementedError
|
42
|
+
end
|
43
|
+
|
44
|
+
def options
|
45
|
+
reflection.options
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def associated_records_by_owner(_preloader)
|
51
|
+
records = load_records do |record|
|
52
|
+
owner = owners_by_key[record[association_key_name]]
|
53
|
+
association = owner.association(reflection.name)
|
54
|
+
association.set_inverse_instance(record)
|
55
|
+
end
|
56
|
+
|
57
|
+
owners.each_with_object({}) do |owner, result|
|
58
|
+
result[owner] = records[owner[owner_key_name]] || []
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def owners_by_key
|
63
|
+
@owners_by_key ||= owners.each_with_object({}) do |owner, h|
|
64
|
+
h[owner[owner_key_name]] = owner
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def owner_keys
|
69
|
+
@owner_keys ||= owners.map do |owner|
|
70
|
+
owner[owner_key_name]
|
71
|
+
end.uniq.compact
|
72
|
+
end
|
73
|
+
|
74
|
+
def load_records
|
75
|
+
return {} if owner_keys.empty?
|
76
|
+
@preloaded_records = records_for(owner_keys).load
|
77
|
+
@preloaded_records.each do |record|
|
78
|
+
yield record if block_given?
|
79
|
+
end
|
80
|
+
@preloaded_records.group_by do |record|
|
81
|
+
record[association_key_name]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
def reflection_scope
|
86
|
+
@reflection_scope ||= reflection.scope_for(klass)
|
87
|
+
end
|
88
|
+
|
89
|
+
def build_scope # rubocop:disable all
|
90
|
+
scope = klass.unscoped
|
91
|
+
|
92
|
+
values = reflection_scope.values
|
93
|
+
preload_values = preload_scope.values
|
94
|
+
|
95
|
+
scope.where_clause = reflection_scope.where_clause + preload_scope.where_clause
|
96
|
+
# scope.references_values = Array(values[:references]) + Array(preload_values[:references])
|
97
|
+
|
98
|
+
if preload_values[:select] || values[:select]
|
99
|
+
scope._select!(preload_values[:select] || values[:select])
|
100
|
+
end
|
101
|
+
scope.includes! preload_values[:includes] || values[:includes]
|
102
|
+
# if preload_scope.joins_values.any?
|
103
|
+
# scope.joins!(preload_scope.joins_values)
|
104
|
+
# else
|
105
|
+
# scope.joins!(reflection_scope.joins_values)
|
106
|
+
# end
|
107
|
+
order_values = preload_values[:order] || values[:order]
|
108
|
+
if order_values
|
109
|
+
scope.order!(order_values)
|
110
|
+
end
|
111
|
+
|
112
|
+
# if preload_values[:reordering] || values[:reordering]
|
113
|
+
# scope.reordering_value = true
|
114
|
+
# end
|
115
|
+
|
116
|
+
# if preload_values[:readonly] || values[:readonly]
|
117
|
+
# scope.readonly!
|
118
|
+
# end
|
119
|
+
|
120
|
+
# if options[:as]
|
121
|
+
# scope.where!(klass.table_name => { reflection.type => model.base_class.sti_name })
|
122
|
+
# end
|
123
|
+
|
124
|
+
# scope.unscope_values = Array(values[:unscope]) + Array(preload_values[:unscope])
|
125
|
+
klass.default_scoped.merge(scope)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAny
|
4
|
+
module Associations
|
5
|
+
class Preloader
|
6
|
+
class BelongsTo < Association
|
7
|
+
def preload(preloader)
|
8
|
+
associated_records_by_owner(preloader).each do |owner, associated_records|
|
9
|
+
record = associated_records.first
|
10
|
+
|
11
|
+
association = owner.association(reflection.name)
|
12
|
+
association.target = record
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def association_key_name
|
17
|
+
reflection.options[:primary_key] || klass && klass.primary_key
|
18
|
+
end
|
19
|
+
|
20
|
+
def owner_key_name
|
21
|
+
reflection.foreign_key
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAny
|
4
|
+
module Associations
|
5
|
+
class Preloader
|
6
|
+
class HasMany < Association
|
7
|
+
def preload(preloader)
|
8
|
+
associated_records_by_owner(preloader).each do |owner, records|
|
9
|
+
association = owner.association(reflection.name)
|
10
|
+
association.loaded!
|
11
|
+
association.target.concat(records)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def association_key_name
|
16
|
+
reflection.foreign_key
|
17
|
+
end
|
18
|
+
|
19
|
+
def owner_key_name
|
20
|
+
reflection.primary_key
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,111 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_any/associations/preloader/association'
|
4
|
+
require 'active_any/associations/preloader/has_many'
|
5
|
+
require 'active_any/associations/preloader/belongs_to'
|
6
|
+
|
7
|
+
module ActiveAny
|
8
|
+
module Associations
|
9
|
+
class Preloader
|
10
|
+
NULL_RELATION = Struct.new(:values, :where_clause, :joins_values).new({}, Relation::WhereClause.empty, [])
|
11
|
+
|
12
|
+
def preload(records, associations, scope = NULL_RELATION)
|
13
|
+
records = Array.wrap(records).compact.uniq
|
14
|
+
associations = Array.wrap(associations)
|
15
|
+
|
16
|
+
if records.empty?
|
17
|
+
[]
|
18
|
+
else
|
19
|
+
associations.flat_map do |association|
|
20
|
+
preloaders_on association, records, scope
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def preloaders_on(association, records, scope)
|
28
|
+
case association
|
29
|
+
when Symbol
|
30
|
+
preloaders_for_one(association, records, scope)
|
31
|
+
when String
|
32
|
+
preloaders_for_one(association.to_sym, records, scope)
|
33
|
+
else
|
34
|
+
raise ArgumentError, "#{association.inspect} was not recognized for preload"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def preloaders_for_one(association, records, scope)
|
39
|
+
grouped_records(association, records).flat_map do |reflection, klasses|
|
40
|
+
klasses.map do |rhs_klass, rs|
|
41
|
+
loader = preloader_for(reflection, rs, rhs_klass).new(rhs_klass, rs, reflection, scope)
|
42
|
+
loader.run self
|
43
|
+
loader
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def grouped_records(association, records)
|
49
|
+
h = {}
|
50
|
+
records.each do |record|
|
51
|
+
next unless record
|
52
|
+
assoc = record.association(association)
|
53
|
+
klasses = h[assoc.reflection] ||= {}
|
54
|
+
(klasses[assoc.klass] ||= []) << record
|
55
|
+
end
|
56
|
+
h
|
57
|
+
end
|
58
|
+
|
59
|
+
class AlreadyLoaded
|
60
|
+
attr_reader :owners, :reflection
|
61
|
+
|
62
|
+
def initialize(_klass, owners, reflection, _preload_scope)
|
63
|
+
@owners = owners
|
64
|
+
@reflection = reflection
|
65
|
+
end
|
66
|
+
|
67
|
+
def run(preloader); end
|
68
|
+
|
69
|
+
def preloaded_records
|
70
|
+
owners.flat_map { |owner| owner.association(reflection.name).target }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class NullPreloader
|
75
|
+
def self.new(_klass, _owners, _reflection, _preload_scope)
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.run(_preloader); end
|
80
|
+
|
81
|
+
def self.preloaded_records
|
82
|
+
[]
|
83
|
+
end
|
84
|
+
|
85
|
+
def self.owners
|
86
|
+
[]
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def preloader_for(reflection, owners, rhs_klass) # rubocop:disable Metrics/MethodLength
|
91
|
+
return NullPreloader unless rhs_klass
|
92
|
+
|
93
|
+
if owners.first.association(reflection.name).loaded?
|
94
|
+
return AlreadyLoaded
|
95
|
+
end
|
96
|
+
reflection.check_preloadable!
|
97
|
+
|
98
|
+
case reflection.macro
|
99
|
+
when :has_many
|
100
|
+
# reflection.options[:through] ? HasManyThrough : HasMany
|
101
|
+
HasMany
|
102
|
+
when :has_one
|
103
|
+
# reflection.options[:through] ? HasOneThrough : HasOne
|
104
|
+
HasOne
|
105
|
+
when :belongs_to
|
106
|
+
BelongsTo
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'active_any/associations/preloader'
|
3
4
|
require 'active_any/associations/collection_proxy'
|
4
5
|
require 'active_any/associations/association_scope'
|
5
6
|
require 'active_any/associations/builder/association'
|
@@ -40,13 +41,6 @@ module ActiveAny
|
|
40
41
|
super
|
41
42
|
end
|
42
43
|
|
43
|
-
private
|
44
|
-
|
45
|
-
def init_internals(*)
|
46
|
-
@association_cache = {}
|
47
|
-
super
|
48
|
-
end
|
49
|
-
|
50
44
|
def association(name)
|
51
45
|
association = association_instance_get(name)
|
52
46
|
|
@@ -61,6 +55,13 @@ module ActiveAny
|
|
61
55
|
association
|
62
56
|
end
|
63
57
|
|
58
|
+
private
|
59
|
+
|
60
|
+
def init_internals(*)
|
61
|
+
@association_cache = {}
|
62
|
+
super
|
63
|
+
end
|
64
|
+
|
64
65
|
def association_instance_get(name)
|
65
66
|
@association_cache[name]
|
66
67
|
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAny
|
4
|
+
module Attribute
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
def attributes
|
8
|
+
@attributes ||= {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def has_attribute?(attribute)
|
12
|
+
attributes.key?(attribute)
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_attribute(name)
|
16
|
+
attributes.fetch(name)
|
17
|
+
end
|
18
|
+
|
19
|
+
def attribute_for_inspect(attr_name)
|
20
|
+
value = read_attribute(attr_name)
|
21
|
+
|
22
|
+
if value.is_a?(String) && value.length > 50
|
23
|
+
"#{value[0, 50]}...".inspect
|
24
|
+
elsif value.is_a?(Date) || value.is_a?(Time)
|
25
|
+
%("#{value.to_s(:db)}")
|
26
|
+
else
|
27
|
+
value.inspect
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def inspect
|
32
|
+
inspection = self.class.attribute_names.collect do |name|
|
33
|
+
"#{name}: #{attribute_for_inspect(name)}" if has_attribute?(name)
|
34
|
+
end.compact.join(', ')
|
35
|
+
|
36
|
+
"#<#{self.class} #{inspection}>"
|
37
|
+
end
|
38
|
+
|
39
|
+
module ClassMethods
|
40
|
+
def attributes(*names)
|
41
|
+
names.each { |name| attribute name }
|
42
|
+
end
|
43
|
+
|
44
|
+
def attribute(name)
|
45
|
+
attribute_names << name.to_sym
|
46
|
+
define_writer_method name
|
47
|
+
define_reader_method name
|
48
|
+
end
|
49
|
+
|
50
|
+
def attribute_names
|
51
|
+
@attribute_names ||= []
|
52
|
+
end
|
53
|
+
|
54
|
+
def define_writer_method(name)
|
55
|
+
define_method "#{name}=" do |value|
|
56
|
+
attributes[name] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def define_reader_method(name)
|
61
|
+
define_method name do
|
62
|
+
attributes.fetch(name)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def has_attribute?(attribute)
|
67
|
+
attribute_names.key?(attribute)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAny
|
4
|
+
class Base
|
5
|
+
include Core
|
6
|
+
include Attribute
|
7
|
+
include Associations
|
8
|
+
include AttributeAssignment
|
9
|
+
include Finders
|
10
|
+
include Reflection
|
11
|
+
end
|
12
|
+
|
13
|
+
class Object < Base
|
14
|
+
def self.inherited(child)
|
15
|
+
child.abstract_class = false
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
attr_accessor :data
|
20
|
+
|
21
|
+
def adapter
|
22
|
+
@adapter ||= ObjectAdapter.new(self)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class Hash < Base
|
28
|
+
def self.inherited(child)
|
29
|
+
child.abstract_class = false
|
30
|
+
end
|
31
|
+
|
32
|
+
class << self
|
33
|
+
attr_reader :data
|
34
|
+
|
35
|
+
def data=(data)
|
36
|
+
data.map(&:keys).flatten.uniq.each do |method|
|
37
|
+
attribute method
|
38
|
+
end
|
39
|
+
|
40
|
+
@data = data.map { |d| new(d) }
|
41
|
+
end
|
42
|
+
|
43
|
+
def adapter
|
44
|
+
@adapter ||= ObjectAdapter.new(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ActiveAny
|
4
|
+
class Configuration
|
5
|
+
attr_accessor :logger, :log_level
|
6
|
+
|
7
|
+
def logger
|
8
|
+
@logger ||= begin
|
9
|
+
logger = Logger.new($stdout)
|
10
|
+
logger.level = "::Logger::#{log_level.to_s.upcase}".constantize
|
11
|
+
logger
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def log_level
|
16
|
+
@log_level ||= :debug
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/active_any/core.rb
CHANGED
@@ -12,13 +12,29 @@ module ActiveAny
|
|
12
12
|
end
|
13
13
|
|
14
14
|
class_methods do
|
15
|
-
delegate :take, :take!, :first, :last, to: :all
|
15
|
+
delegate :includes, :take, :take!, :first, :last, to: :all
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
def unscoped
|
20
|
+
# TODO: implement
|
21
|
+
all
|
22
|
+
end
|
23
|
+
|
24
|
+
def default_scoped
|
25
|
+
# TODO: implement
|
26
|
+
all
|
27
|
+
end
|
16
28
|
end
|
17
29
|
|
18
30
|
def initialize(*args)
|
19
31
|
init_internals(*args)
|
20
32
|
end
|
21
33
|
|
34
|
+
def [](key)
|
35
|
+
send(key)
|
36
|
+
end
|
37
|
+
|
22
38
|
private
|
23
39
|
|
24
40
|
def init_internals(*); end
|