ymodel 0.0.1 → 0.1.0
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/Gemfile.lock +34 -0
- data/README.md +57 -5
- data/lib/ymodel/base.rb +63 -0
- data/lib/ymodel/errors.rb +35 -0
- data/lib/ymodel/helper.rb +15 -0
- data/lib/ymodel/loadable.rb +127 -0
- data/lib/ymodel/queryable.rb +87 -0
- data/lib/ymodel/relatable.rb +69 -0
- data/lib/ymodel/schema.rb +28 -0
- data/lib/ymodel/triggerable.rb +21 -0
- data/lib/ymodel/version.rb +1 -1
- data/lib/ymodel.rb +2 -0
- metadata +10 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3e76b055a94aca7267be60ff8727343c1cacdbee176df49d0b757c63678c3703
|
4
|
+
data.tar.gz: 279c251a3f1bc448b4ca013ead8bee6eb2a3356f197b415360d099ea16a33762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9065024111d3d97779cc30566e08d14d118f797ace4c9bfc869255ed5558f7078601fe95bfaaeb1b7a1c3079fefab5b64db48f3c593ddddaee7ecc942a44663f
|
7
|
+
data.tar.gz: 45228ad8ac261538e73b064544780cf4b96036a95e75f1860af99d91e61226c6eea0806079e3ac9b79740552ba9993862952beba52740bc58fafcf324dbef7ba
|
data/Gemfile.lock
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
ymodel (0.0.1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
diff-lcs (1.5.0)
|
10
|
+
rake (13.0.6)
|
11
|
+
rspec (3.11.0)
|
12
|
+
rspec-core (~> 3.11.0)
|
13
|
+
rspec-expectations (~> 3.11.0)
|
14
|
+
rspec-mocks (~> 3.11.0)
|
15
|
+
rspec-core (3.11.0)
|
16
|
+
rspec-support (~> 3.11.0)
|
17
|
+
rspec-expectations (3.11.0)
|
18
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
19
|
+
rspec-support (~> 3.11.0)
|
20
|
+
rspec-mocks (3.11.1)
|
21
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
22
|
+
rspec-support (~> 3.11.0)
|
23
|
+
rspec-support (3.11.0)
|
24
|
+
|
25
|
+
PLATFORMS
|
26
|
+
arm64-darwin-20
|
27
|
+
|
28
|
+
DEPENDENCIES
|
29
|
+
rake (~> 13.0)
|
30
|
+
rspec (~> 3.0)
|
31
|
+
ymodel!
|
32
|
+
|
33
|
+
BUNDLED WITH
|
34
|
+
2.3.14
|
data/README.md
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
# Ymodel
|
2
2
|
|
3
|
-
Welcome to
|
4
|
-
|
5
|
-
TODO: Delete this and the text above, and describe your gem
|
3
|
+
Welcome to ymodel. An active-record-like interface to wrap yaml files.
|
6
4
|
|
7
5
|
## Installation
|
8
6
|
|
@@ -16,7 +14,61 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
16
14
|
|
17
15
|
## Usage
|
18
16
|
|
19
|
-
|
17
|
+
Create a model with data from Yaml files:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class GridCode < YModel::Base
|
21
|
+
index_on :name
|
22
|
+
|
23
|
+
default_attribute :successful_outcomes, with: []
|
24
|
+
default_attribute :loading_outcomes, with: []
|
25
|
+
default_attribute :loading_maximum, with: 0
|
26
|
+
default_attribute :required_documents, with: []
|
27
|
+
end
|
28
|
+
```
|
29
|
+
|
30
|
+
```yaml
|
31
|
+
---
|
32
|
+
data:
|
33
|
+
- name: FM1
|
34
|
+
successful_questionnaire_outcomes:
|
35
|
+
- standard
|
36
|
+
- accord
|
37
|
+
loading_outcomes:
|
38
|
+
- majoration
|
39
|
+
loading_maximum: 50
|
40
|
+
questionnaire_type: QSS
|
41
|
+
priority: 1
|
42
|
+
|
43
|
+
- name: FM2
|
44
|
+
successful_questionnaire_outcomes:
|
45
|
+
- standard
|
46
|
+
- accord
|
47
|
+
loading_outcomes:
|
48
|
+
- majoration
|
49
|
+
loading_maximum: 50
|
50
|
+
questionnaire_type: QM
|
51
|
+
priority: 1
|
52
|
+
|
53
|
+
- name: FM3
|
54
|
+
questionnaire_type: QM
|
55
|
+
priority: 2
|
56
|
+
required_documents:
|
57
|
+
- BIO 1
|
58
|
+
```
|
59
|
+
|
60
|
+
```irb
|
61
|
+
3.0.1 :001 > GridCode.all.first
|
62
|
+
=>
|
63
|
+
#<GridCode:0x000000011a0b7640
|
64
|
+
@loading_maximum=50,
|
65
|
+
@loading_outcomes=["majoration"],
|
66
|
+
@name="FM1",
|
67
|
+
@priority=1,
|
68
|
+
@questionnaire_type="QSS",
|
69
|
+
@required_documents=[],
|
70
|
+
@successful_questionnaire_outcomes=["standard", "accord"]>
|
71
|
+
```
|
20
72
|
|
21
73
|
## Development
|
22
74
|
|
@@ -26,7 +78,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
26
78
|
|
27
79
|
## Contributing
|
28
80
|
|
29
|
-
|
81
|
+
Suggestions, ideas or contribution can be discussed with steven@remarkgroup.com.
|
30
82
|
|
31
83
|
## License
|
32
84
|
|
data/lib/ymodel/base.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'relatable'
|
4
|
+
require_relative 'loadable'
|
5
|
+
require_relative 'triggerable'
|
6
|
+
require_relative 'queryable'
|
7
|
+
|
8
|
+
module Ymodel
|
9
|
+
# This is used to wrap a YAML file in a similar manner to ActiveRecord
|
10
|
+
# wrapping a database.
|
11
|
+
class Base
|
12
|
+
extend Ymodel::Relatable
|
13
|
+
extend Ymodel::Loadable
|
14
|
+
extend Ymodel::Triggerable
|
15
|
+
extend Ymodel::Queryable
|
16
|
+
|
17
|
+
def initialize(record = {})
|
18
|
+
record.each do |k, v|
|
19
|
+
instance_variable_set "@#{k}", v if attribute?(k)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
other.respond_to?(:attributes) && attributes == other.attributes
|
25
|
+
end
|
26
|
+
|
27
|
+
def attributes
|
28
|
+
# definitive schema
|
29
|
+
schema.attributes
|
30
|
+
.each_with_object({}) { |attr, memo| memo[attr] = send(attr) }
|
31
|
+
end
|
32
|
+
|
33
|
+
def [](property)
|
34
|
+
instance_variable_get("@#{property}")
|
35
|
+
end
|
36
|
+
|
37
|
+
def attribute?(key)
|
38
|
+
# definitive schema
|
39
|
+
schema.include?(key) || key == :id
|
40
|
+
end
|
41
|
+
|
42
|
+
def index
|
43
|
+
self[index_key]
|
44
|
+
end
|
45
|
+
|
46
|
+
private
|
47
|
+
|
48
|
+
def schema
|
49
|
+
self.class.schema
|
50
|
+
end
|
51
|
+
|
52
|
+
def index_key
|
53
|
+
self.class.instance_variable_get('@index') || :id
|
54
|
+
end
|
55
|
+
|
56
|
+
def index_set?
|
57
|
+
# The name of the key of the index is stored as an instance variable in
|
58
|
+
# the class.
|
59
|
+
index = self.class.instance_variable_get('@index') || :id
|
60
|
+
self[index]
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubocop:disable Style/Documentation
|
4
|
+
module Ymodel
|
5
|
+
module Errors; end
|
6
|
+
|
7
|
+
class YmodelError < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
class SourceFileNotFound < YmodelError
|
11
|
+
attr_reader :model
|
12
|
+
|
13
|
+
def initialize(message = nil, model = nil)
|
14
|
+
@model = model
|
15
|
+
|
16
|
+
super(message)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class MissingConstant < YmodelError
|
21
|
+
end
|
22
|
+
|
23
|
+
class UnacceptableOptionsError < YmodelError
|
24
|
+
end
|
25
|
+
|
26
|
+
class UnpermittedParamsError < YmodelError
|
27
|
+
end
|
28
|
+
|
29
|
+
class RecordNotFound < YmodelError
|
30
|
+
end
|
31
|
+
|
32
|
+
class DuplicateIndexError < YmodelError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
# rubocop:enable Style/Documentation
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ymodel
|
4
|
+
# This module contains YModel logic for managing relations.
|
5
|
+
module Helper
|
6
|
+
def self.model_class(model)
|
7
|
+
as_const = model.to_s.singularize.camelcase
|
8
|
+
Kernel.const_get(as_const)
|
9
|
+
rescue StandardError
|
10
|
+
message = "relation `#{model}` couldn't be made because constant "\
|
11
|
+
"`#{as_const}` doesn't exist."
|
12
|
+
raise Ymodel::MissingConstant, message
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'schema'
|
4
|
+
|
5
|
+
module Ymodel
|
6
|
+
# Module resposible for loading the schema and data from the yaml files.
|
7
|
+
module Loadable
|
8
|
+
# This method can be called from within a concrete implementation to
|
9
|
+
# overwrite the default ymodel filename associated with that model.
|
10
|
+
def source_file(filename)
|
11
|
+
@source = filename
|
12
|
+
|
13
|
+
# Similar to Trigger#inherited. A hook for loading the schema.
|
14
|
+
define_readers self
|
15
|
+
end
|
16
|
+
|
17
|
+
def pears_subject(subject)
|
18
|
+
@pears = true
|
19
|
+
if block_given?
|
20
|
+
@source = Pears.subject(subject) { |provider| yield(provider) }
|
21
|
+
else
|
22
|
+
@source = subject
|
23
|
+
end
|
24
|
+
# Similar to Trigger#inherited. A hook for loading the schema.
|
25
|
+
define_readers self
|
26
|
+
end
|
27
|
+
|
28
|
+
# With is used as the default value of the attribute
|
29
|
+
def default_attribute(attr_name, with: nil)
|
30
|
+
@schema << attr_name
|
31
|
+
define_reader attr_name, default: with
|
32
|
+
end
|
33
|
+
|
34
|
+
def schema
|
35
|
+
@schema = Schema.new(records, index)
|
36
|
+
if @manual_index
|
37
|
+
@schema ||= Schema.new(records)
|
38
|
+
else
|
39
|
+
@schema ||= Schema.new(records, :id)
|
40
|
+
end
|
41
|
+
rescue Errno::ENOENT => e
|
42
|
+
Schema.new({})
|
43
|
+
end
|
44
|
+
|
45
|
+
def load_records!
|
46
|
+
if load_indices?
|
47
|
+
all = records.each_with_index
|
48
|
+
.map { |record, i| new(record.merge({id: i})) }
|
49
|
+
else
|
50
|
+
all = records.map { |record| new(record) }
|
51
|
+
end
|
52
|
+
unless all.map(&:index) == all.map(&:index).uniq
|
53
|
+
raise DuplicateIndexError,
|
54
|
+
"#{name}: Some records share the same index"
|
55
|
+
end
|
56
|
+
|
57
|
+
all
|
58
|
+
end
|
59
|
+
|
60
|
+
def index_on(key)
|
61
|
+
@manual_index = true
|
62
|
+
@index = key
|
63
|
+
end
|
64
|
+
|
65
|
+
def index
|
66
|
+
@index || :id
|
67
|
+
end
|
68
|
+
|
69
|
+
def source_files
|
70
|
+
if compiled?
|
71
|
+
Dir.glob(File.join(source, '*.yml'))
|
72
|
+
else
|
73
|
+
[@source]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def define_reader(attribute, default: nil)
|
78
|
+
define_method(attribute.to_sym) do
|
79
|
+
value = instance_variable_get("@#{attribute}")
|
80
|
+
if value.nil? && !default.nil?
|
81
|
+
value = instance_variable_set(
|
82
|
+
"@#{attribute}",
|
83
|
+
default.respond_to?(:call) ? default.call : default
|
84
|
+
)
|
85
|
+
end
|
86
|
+
|
87
|
+
value
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
protected
|
92
|
+
|
93
|
+
def records
|
94
|
+
if @pears
|
95
|
+
@source[:data]
|
96
|
+
else
|
97
|
+
@records ||= source_files.flat_map { |name| YAML.load_file(name) }
|
98
|
+
.map(&:symbolize_keys)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
def source
|
103
|
+
@source ||= _source
|
104
|
+
end
|
105
|
+
|
106
|
+
private
|
107
|
+
|
108
|
+
def load_indices?
|
109
|
+
index == :id && !@manual_index
|
110
|
+
end
|
111
|
+
|
112
|
+
def compiled?
|
113
|
+
File.directory?(File.join(Rails.root, source))
|
114
|
+
end
|
115
|
+
|
116
|
+
def _source
|
117
|
+
File.join(Rails.root,
|
118
|
+
'config',
|
119
|
+
'ymodel',
|
120
|
+
name.gsub('::', '/').pluralize.underscore + '.yml')
|
121
|
+
end
|
122
|
+
|
123
|
+
def load_index=(value)
|
124
|
+
@load_index = value
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ymodel
|
4
|
+
# This mixin holds the query methods.
|
5
|
+
module Queryable
|
6
|
+
def find(index)
|
7
|
+
all.find { |record| record.index == index }
|
8
|
+
end
|
9
|
+
|
10
|
+
def find!(index)
|
11
|
+
find(index) || raise_record_not_found_exception!(index)
|
12
|
+
end
|
13
|
+
|
14
|
+
def find_by(attributes)
|
15
|
+
sanitized = sanitize_attributes(attributes)
|
16
|
+
all.each do |record|
|
17
|
+
return record if sanitized.all? { |k, v| record.send(k) == v }
|
18
|
+
end
|
19
|
+
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def find_by!(attributes)
|
24
|
+
find_by(attributes) || raise_record_not_found_exception!(attributes)
|
25
|
+
end
|
26
|
+
|
27
|
+
def find_by_key(key)
|
28
|
+
all.find do |record|
|
29
|
+
record.key == key.to_s
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def find_by_key!(key)
|
34
|
+
find_by_key(key) || raise_record_not_found_exception!(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def all
|
38
|
+
if @pears
|
39
|
+
load_records!
|
40
|
+
else
|
41
|
+
@all ||= load_records!
|
42
|
+
end
|
43
|
+
rescue Errno::ENOENT
|
44
|
+
raise SourceFileNotFound
|
45
|
+
end
|
46
|
+
|
47
|
+
def where(attributes)
|
48
|
+
sanitized = sanitize_attributes(attributes)
|
49
|
+
|
50
|
+
if sanitized.length != attributes.length
|
51
|
+
unpermitted = (attributes.keys.map(&:to_sym) - sanitized.keys)
|
52
|
+
message = "These attributes are not allowed: #{unpermitted}"
|
53
|
+
|
54
|
+
raise UnpermittedParamsError, message
|
55
|
+
end
|
56
|
+
|
57
|
+
all.select do |record|
|
58
|
+
sanitized.all? { |key, value| record.send(key) == value }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# Beware of using this method. If all attributes get removed during
|
63
|
+
# sanitation it returns the equivalent of #all.
|
64
|
+
def where!(attributes)
|
65
|
+
sanitized = sanitize_attributes(attributes)
|
66
|
+
all.select do |record|
|
67
|
+
sanitized.all? { |key, value| record.send(key) == value }
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def sanitize_attributes(attributes)
|
72
|
+
# definitive schema
|
73
|
+
attributes.symbolize_keys!
|
74
|
+
.select { |attr| schema.include?(attr) }
|
75
|
+
end
|
76
|
+
|
77
|
+
def raise_record_not_found_exception!(attributes = nil)
|
78
|
+
if attributes.is_a?(Hash)
|
79
|
+
message = "Couldn't find #{name} with "
|
80
|
+
attributes.each { |k, v| message += "#{k}: #{v}" }
|
81
|
+
else
|
82
|
+
message = "Couldn't find #{name} #{attributes}"
|
83
|
+
end
|
84
|
+
raise RecordNotFound, message
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'active_support/inflector'
|
4
|
+
require_relative 'helper'
|
5
|
+
|
6
|
+
module Ymodel
|
7
|
+
# This module contains YModel logic for managing relations.
|
8
|
+
module Relatable
|
9
|
+
# This method is used to define the key on which relations are build.
|
10
|
+
# We default to 'id'.
|
11
|
+
|
12
|
+
def belongs_to(model, options = {})
|
13
|
+
define_method(model) do
|
14
|
+
related_class =
|
15
|
+
Helper.model_class(options[:class_name] || model)
|
16
|
+
key =
|
17
|
+
if related_class < Base
|
18
|
+
:"#{model.to_s.singularize}_#{related_class.index}"
|
19
|
+
else
|
20
|
+
:"#{related_class.to_s.singularize}_id"
|
21
|
+
end
|
22
|
+
related_class.find(self[key])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# rubocop:disable Naming/PredicateName
|
27
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
28
|
+
def has_many(model, class_name: nil, as: nil, foreign_key: nil)
|
29
|
+
raise_options_error if as && foreign_key
|
30
|
+
|
31
|
+
foreign_key ||= default_foreign_key
|
32
|
+
define_method(model) do
|
33
|
+
relation_class = Helper.model_class(class_name || model)
|
34
|
+
if as
|
35
|
+
relation_class.where("#{as}_id" => id,
|
36
|
+
"#{as}_type" => self.class.name)
|
37
|
+
else
|
38
|
+
relation_class.where(foreign_key.to_sym => index)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def has_one(model, class_name: nil, as: nil, foreign_key: nil)
|
44
|
+
raise_options_error if as && foreign_key
|
45
|
+
|
46
|
+
foreign_key ||= default_foreign_key
|
47
|
+
define_method(model) do
|
48
|
+
relation_class = Helper.model_class(class_name || model)
|
49
|
+
return relation_class.find_by(foreign_key => index) unless as
|
50
|
+
|
51
|
+
relation_class.find_by("#{as}_id" => id,
|
52
|
+
"#{as}_type" => self.class.name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
# rubocop:enable Naming/PredicateName
|
56
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def default_foreign_key
|
61
|
+
"#{name.to_s.underscore.singularize}_#{index}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def raise_options_error
|
65
|
+
raise UnacceptableOptionsError, "Relations with an 'as' and"\
|
66
|
+
" 'foreign_key' are not supported."
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'forwardable'
|
4
|
+
|
5
|
+
module Ymodel
|
6
|
+
# Represents a schema of all keys found in the source.
|
7
|
+
class Schema
|
8
|
+
extend Forwardable
|
9
|
+
include Enumerable
|
10
|
+
attr_reader :attributes
|
11
|
+
|
12
|
+
def_delegators :attributes, :each
|
13
|
+
|
14
|
+
def initialize(source, *keys)
|
15
|
+
@attributes = source.flat_map(&:keys)
|
16
|
+
.map(&:to_sym)
|
17
|
+
.to_set + keys
|
18
|
+
end
|
19
|
+
|
20
|
+
def include?(key)
|
21
|
+
attributes.include?(key.to_sym)
|
22
|
+
end
|
23
|
+
|
24
|
+
def <<(attribute)
|
25
|
+
@attributes << attribute.to_sym
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Ymodel
|
4
|
+
# Handles events that decorate the instances with methods.
|
5
|
+
module Triggerable
|
6
|
+
protected
|
7
|
+
|
8
|
+
def define_readers(model)
|
9
|
+
model.instance_eval do
|
10
|
+
schema.attributes.each { |attribute| define_reader(attribute) }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def inherited(model)
|
17
|
+
define_readers(model)
|
18
|
+
@triggered = true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/ymodel/version.rb
CHANGED
data/lib/ymodel.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ymodel
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Steven Kemp
|
@@ -20,10 +20,19 @@ files:
|
|
20
20
|
- ".rspec"
|
21
21
|
- CHANGELOG.md
|
22
22
|
- Gemfile
|
23
|
+
- Gemfile.lock
|
23
24
|
- LICENSE.txt
|
24
25
|
- README.md
|
25
26
|
- Rakefile
|
26
27
|
- lib/ymodel.rb
|
28
|
+
- lib/ymodel/base.rb
|
29
|
+
- lib/ymodel/errors.rb
|
30
|
+
- lib/ymodel/helper.rb
|
31
|
+
- lib/ymodel/loadable.rb
|
32
|
+
- lib/ymodel/queryable.rb
|
33
|
+
- lib/ymodel/relatable.rb
|
34
|
+
- lib/ymodel/schema.rb
|
35
|
+
- lib/ymodel/triggerable.rb
|
27
36
|
- lib/ymodel/version.rb
|
28
37
|
- sig/ymodel.rbs
|
29
38
|
homepage:
|