active_any 0.0.1 → 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://travis-ci.org/yuemori/active_any.svg?branch=master)](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
|