data_files 1.0.0.rc1
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 +7 -0
- data/.rubocop.yml +2 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +3 -0
- data/Gemfile +7 -0
- data/Gemfile.lock +21 -0
- data/LICENSE.txt +21 -0
- data/README.md +139 -0
- data/Rakefile +19 -0
- data/data-files.gemspec +27 -0
- data/data/games.yml +31 -0
- data/data/lists.yml +18 -0
- data/lib/data_files/active_data.rb +172 -0
- data/lib/data_files/repl.rb +87 -0
- data/lib/data_files/version.rb +3 -0
- metadata +61 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 38ca97e41fc75a1a3799b5647fe95a25811515d81f8d84346635f190f46d0a3b
|
4
|
+
data.tar.gz: 29adfa22da840b9f96d9c10631140b3dcbd9d9c4f8d82c1895873efaee56db27
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 05bb9c56584c52153dbd84d95b24bb58dbeac9c2aec14237d6c3cedc20749f290c0ddfc0a097c2c14fca2ffea83aba240feaa3943e6f6daa7c02caa07b43327f
|
7
|
+
data.tar.gz: 3daec9182bc98e4036447bb73ed5d523b42dc9eb94cdf097df9de1e20bd1d2999cb70231bce9098e9ee6cb98b4257303ef4961b89f1559a5c7d68f693941f20d
|
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.7.0
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
data_files (1.0.0.rc1)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
minitest (5.13.0)
|
10
|
+
rake (12.3.3)
|
11
|
+
|
12
|
+
PLATFORMS
|
13
|
+
ruby
|
14
|
+
|
15
|
+
DEPENDENCIES
|
16
|
+
data_files!
|
17
|
+
minitest (~> 5.0)
|
18
|
+
rake (~> 12.0)
|
19
|
+
|
20
|
+
BUNDLED WITH
|
21
|
+
2.1.4
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) [2020] [Andreas Zecher]
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,139 @@
|
|
1
|
+
# REPL for Middleman Data Files
|
2
|
+
|
3
|
+
Written during Lab Week in January 2020 at Mynewsdesk.
|
4
|
+
|
5
|
+
This interactive shell allows users to manipulate [Middleman Data Files](https://middlemanapp.com/advanced/data-files/) with an API similar to ActiveRecord.
|
6
|
+
|
7
|
+
## Getting Started
|
8
|
+
|
9
|
+
To start the interactive shell run the following command from your Middleman project directory:
|
10
|
+
|
11
|
+
```
|
12
|
+
> rake data_files
|
13
|
+
```
|
14
|
+
|
15
|
+
## Querying data
|
16
|
+
|
17
|
+
Given a file located in `data/games.yml` in a Middleman project directory, we can query our data in different ways:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
> Game.first
|
21
|
+
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: nil, _id: 1>
|
22
|
+
|
23
|
+
> Game.last
|
24
|
+
#<Game title: "Xenon 2: Megablast", url: "http://www.bitmap-brothers.co.uk/our-games/past/xenon2.htm", year: 1989, _id: 11>
|
25
|
+
|
26
|
+
> Game.find_by(title: "Another World")
|
27
|
+
#<Game title: "Another World", url: "http://www.anotherworld.fr/anotherworld_uk/", year: 1991, _id: 4>
|
28
|
+
|
29
|
+
> Game.where(year: 1991)
|
30
|
+
[#<Game title: "Another World", url: "http://www.anotherworld.fr/anotherworld_uk/", year: 1991, _id: 4>, #<Game title: "Commander Keen in Goodbye, Galaxy", url: "http://legacy.3drealms.com/keen4/", year: 1991, _id: 7>]
|
31
|
+
|
32
|
+
> Game.all.count
|
33
|
+
10
|
34
|
+
```
|
35
|
+
|
36
|
+
The internal `_id` attribute is ephemeral and can change between different sessions. It is not saved to the YAML file. It can however be used for querying:
|
37
|
+
|
38
|
+
```ruby
|
39
|
+
> Game.find_by(_id: 12)
|
40
|
+
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: 12>
|
41
|
+
```
|
42
|
+
|
43
|
+
## Creating new data
|
44
|
+
|
45
|
+
We can add new items to our data file:
|
46
|
+
|
47
|
+
```ruby
|
48
|
+
> game = Game.new(title: "Super Mario Maker 2", year: 2019, url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/")
|
49
|
+
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: nil>
|
50
|
+
|
51
|
+
> game.save
|
52
|
+
true
|
53
|
+
|
54
|
+
> game
|
55
|
+
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: 12>
|
56
|
+
```
|
57
|
+
|
58
|
+
## Updating data
|
59
|
+
|
60
|
+
We can also update exisiting items:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
> game = Game.where(year: nil).first
|
64
|
+
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: nil, _id: 1>
|
65
|
+
|
66
|
+
> game.year = 2020
|
67
|
+
2020
|
68
|
+
|
69
|
+
> game.save
|
70
|
+
true
|
71
|
+
|
72
|
+
> game
|
73
|
+
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: 2020, _id: 1>
|
74
|
+
```
|
75
|
+
|
76
|
+
## Normalizing data
|
77
|
+
|
78
|
+
Items will ordered in the YAML file by their primary key. The first key in the array in the YAML file is considered the primary key. In our example the primary key is `title`:
|
79
|
+
|
80
|
+
```yaml
|
81
|
+
---
|
82
|
+
- title: A Light In Chorus
|
83
|
+
url: http://www.alightinchorus.com
|
84
|
+
year:
|
85
|
+
```
|
86
|
+
|
87
|
+
Leading and trailing whitespace is automatically removed from string attributes on `save`:
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
> game = Game.new(title: " Bubble Bobble ")
|
91
|
+
#<Game title: " Bubble Bobble ", url: nil, year: nil, _id: nil>
|
92
|
+
|
93
|
+
> game.save
|
94
|
+
true
|
95
|
+
|
96
|
+
> game
|
97
|
+
#<Game title: "Bubble Bobble", url: nil, year: nil, _id: 11>
|
98
|
+
```
|
99
|
+
|
100
|
+
## Validation
|
101
|
+
|
102
|
+
Data will be automatically be validated on `save`. The validation logic is derived from the exisiting values in the YAML files.
|
103
|
+
|
104
|
+
```ruby
|
105
|
+
> list = List.new(title: nil, user: 1, slug: false, ordered: "yes", featured: "no", published_at: "today", games: "A Light In Chorus, Advanced Wars")
|
106
|
+
#<List title: nil, user: 1, slug: false, ordered: "yes", featured: "no", published_at: "today", games: "A Light In Chorus, Advanced Wars", _id: nil>
|
107
|
+
|
108
|
+
> list.save
|
109
|
+
false
|
110
|
+
|
111
|
+
> list.errors
|
112
|
+
["title must be string", "user must be string", "slug must be string", "ordered must be false or true", "featured must be false or true", "published_at must be date", "games must be array"]
|
113
|
+
```
|
114
|
+
|
115
|
+
Here's an example for a valid `List` item. See [test/data/lists.yml](https://github.com/pixelate/data_files/blob/master/test/data/lists.yml) for the data structure that the validation logic is derived from.
|
116
|
+
|
117
|
+
```ruby
|
118
|
+
> list = List.new(title: "A list", user: "andreaszecher", slug: "a-list", ordered: true, featured: false, published_at: Date.today, games: [{title: "A Light In Chorus"}, {title: "Advanced Wars"}])
|
119
|
+
#<List title: "A list", user: "andreaszecher", slug: "a-list", ordered: true, featured: false, published_at: 2020-01-09, games: [{:title=>"A Light In Chorus"}, {:title=>"Advanced Wars"}], _id: nil>
|
120
|
+
> list.valid?
|
121
|
+
true
|
122
|
+
> list.errors
|
123
|
+
[]
|
124
|
+
> list.save
|
125
|
+
true
|
126
|
+
```
|
127
|
+
|
128
|
+
Primary keys must be unique within a YAML file:
|
129
|
+
|
130
|
+
```ruby
|
131
|
+
> game = Game.new(title: 'Another World')
|
132
|
+
#<Game title: "Another World", url: nil, year: nil, _id: nil>
|
133
|
+
|
134
|
+
> game.valid?
|
135
|
+
false
|
136
|
+
|
137
|
+
> game.errors
|
138
|
+
["Game with title Another World already exists"]
|
139
|
+
```
|
data/Rakefile
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require_relative "lib/data_files/repl"
|
4
|
+
|
5
|
+
Rake::TestTask.new(:test) do |t|
|
6
|
+
t.libs << "test"
|
7
|
+
t.libs << "lib"
|
8
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
9
|
+
end
|
10
|
+
|
11
|
+
task :"data_files" do |t|
|
12
|
+
unless Dir.exist?(File.join(Dir.pwd, 'data'))
|
13
|
+
puts 'Could not find data directory in working directory.'
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
|
17
|
+
data_files = DataFiles::REPL.new(Dir.pwd)
|
18
|
+
data_files.prompt
|
19
|
+
end
|
data/data-files.gemspec
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
require_relative 'lib/data_files/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = "data_files"
|
5
|
+
spec.version = DataFiles::VERSION
|
6
|
+
spec.authors = ["Andreas Zecher"]
|
7
|
+
spec.email = ["andreas@polylists.com"]
|
8
|
+
|
9
|
+
spec.summary = %q{REPL for Middleman Data Files}
|
10
|
+
spec.description = %q{This interactive shell allows users to manipulate Middleman Data Files with an API similar to ActiveRecord.}
|
11
|
+
spec.homepage = "https://github.com/pixelate/data-files"
|
12
|
+
spec.license = "MIT"
|
13
|
+
spec.required_ruby_version = Gem::Requirement.new(">= 2.3.0")
|
14
|
+
|
15
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
16
|
+
spec.metadata["source_code_uri"] = "https://github.com/pixelate/data-files"
|
17
|
+
spec.metadata["changelog_uri"] = "https://github.com/pixelate/data-files/blob/master/CHANGELOG.md"
|
18
|
+
|
19
|
+
# Specify which files should be added to the gem when it is released.
|
20
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
21
|
+
spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
|
22
|
+
`git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
23
|
+
end
|
24
|
+
spec.bindir = "exe"
|
25
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
26
|
+
spec.require_paths = ["lib"]
|
27
|
+
end
|
data/data/games.yml
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
---
|
2
|
+
- title: A Light In Chorus
|
3
|
+
url: http://www.alightinchorus.com
|
4
|
+
year:
|
5
|
+
- title: Advance Wars
|
6
|
+
url: https://www.nintendo.co.jp/n08/bgwj/index.html
|
7
|
+
year: 2001
|
8
|
+
- title: 'Animal Crossing: New Leaf'
|
9
|
+
url: http://animal-crossing.com/newleaf/
|
10
|
+
year: 2012
|
11
|
+
- title: Another World
|
12
|
+
url: http://www.anotherworld.fr/anotherworld_uk/
|
13
|
+
year: 1991
|
14
|
+
- title: BOXBOY!
|
15
|
+
url: https://www.hallab.co.jp/eng/works/detail/002704/
|
16
|
+
year: 2015
|
17
|
+
- title: Commander Keen in Goodbye, Galaxy
|
18
|
+
url: http://legacy.3drealms.com/keen4/
|
19
|
+
year: 1991
|
20
|
+
- title: Donut County
|
21
|
+
url: http://www.donutcounty.com/
|
22
|
+
year: 2018
|
23
|
+
- title: Firewatch
|
24
|
+
url: http://www.firewatchgame.com
|
25
|
+
year: 2016
|
26
|
+
- title: wipE'out
|
27
|
+
url:
|
28
|
+
year: 1995
|
29
|
+
- title: 'Xenon 2: Megablast'
|
30
|
+
url: http://www.bitmap-brothers.co.uk/our-games/past/xenon2.htm
|
31
|
+
year: 1989
|
data/data/lists.yml
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
---
|
2
|
+
- title: A list
|
3
|
+
user: andreaszecher
|
4
|
+
slug: a-list
|
5
|
+
ordered: true
|
6
|
+
featured: false
|
7
|
+
published_at: 2020-01-08
|
8
|
+
games:
|
9
|
+
- title: A Light In Chorus
|
10
|
+
- title: Advance Wars
|
11
|
+
- title: Another World
|
12
|
+
- title: 'Animal Crossing: New Leaf'
|
13
|
+
- title: BOXBOY!
|
14
|
+
- title: Commander Keen in Goodbye, Galaxy
|
15
|
+
- title: Donut County
|
16
|
+
- title: Firewatch
|
17
|
+
- title: wipE'out
|
18
|
+
- title: 'Xenon 2: Megablast'
|
@@ -0,0 +1,172 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Base class for data querying and manipulation.
|
4
|
+
module DataFiles
|
5
|
+
class ActiveData
|
6
|
+
attr_reader :errors
|
7
|
+
|
8
|
+
def self.all
|
9
|
+
data.collect do |item|
|
10
|
+
new(item)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.first
|
15
|
+
new(data.first)
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.last
|
19
|
+
new(data.last)
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.where(conditions)
|
23
|
+
all.select do |item|
|
24
|
+
selected = true
|
25
|
+
conditions.each do |key, value|
|
26
|
+
selected = false if item.send(key) != value
|
27
|
+
end
|
28
|
+
selected
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.find_by(conditions)
|
33
|
+
results = where(conditions)
|
34
|
+
if results.size.positive?
|
35
|
+
results.first
|
36
|
+
else
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.write_yaml
|
42
|
+
sort_by_primary_key
|
43
|
+
item_attributes = all.collect(&:attributes)
|
44
|
+
|
45
|
+
File.open("data/#{name.downcase}s.yml", 'w') do |file|
|
46
|
+
file.write(item_attributes.to_yaml)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.data
|
51
|
+
class_variable_get(:@@data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.data=(value)
|
55
|
+
class_variable_set(:@@data, value)
|
56
|
+
end
|
57
|
+
|
58
|
+
def self.attributes
|
59
|
+
class_variable_get(:@@attributes)
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.types
|
63
|
+
class_variable_get(:@@types)
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.sort_by_primary_key
|
67
|
+
self.data = data.sort_by do |item|
|
68
|
+
primary_key_value = item.values.first
|
69
|
+
if primary_key_value.is_a? String
|
70
|
+
primary_key_value.downcase
|
71
|
+
else
|
72
|
+
primary_key_value
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def initialize(attrs = {})
|
78
|
+
@_id = nil
|
79
|
+
attrs.each { |key, value| send("#{key}=", value) }
|
80
|
+
end
|
81
|
+
|
82
|
+
def attributes
|
83
|
+
attributes_hash = {}
|
84
|
+
self.class.attributes.each do |attr|
|
85
|
+
attributes_hash[attr] = send(attr) unless attr == '_id'
|
86
|
+
end
|
87
|
+
attributes_hash
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
joined_attributes = self.class.attributes.collect do |attr|
|
92
|
+
val = send(attr)
|
93
|
+
val = "\"#{val}\"" if val.is_a? String
|
94
|
+
"#{attr}: #{val.nil? ? 'nil' : val}"
|
95
|
+
end.join(', ')
|
96
|
+
|
97
|
+
"#<#{self.class} #{joined_attributes}>"
|
98
|
+
end
|
99
|
+
|
100
|
+
def inspect
|
101
|
+
to_s
|
102
|
+
end
|
103
|
+
|
104
|
+
def valid?
|
105
|
+
@errors = []
|
106
|
+
|
107
|
+
primary_key = self.class.attributes.first
|
108
|
+
primary_key_values = self.class.data.collect do |item|
|
109
|
+
{ item['_id'] => item[primary_key] }
|
110
|
+
end
|
111
|
+
|
112
|
+
primary_key_values.each do |item|
|
113
|
+
item.each do |key, value|
|
114
|
+
if key != @_id && value == send(primary_key)
|
115
|
+
@errors << "#{self.class.name} with #{primary_key} #{send(primary_key)} already exists"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
attributes.each do |key, value|
|
121
|
+
unless self.class.types[key].include?(value.class.name)
|
122
|
+
@errors << type_validation_error_message(key, self.class.types[key])
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
@errors.size.zero?
|
127
|
+
end
|
128
|
+
|
129
|
+
def save
|
130
|
+
return false unless valid?
|
131
|
+
|
132
|
+
strip
|
133
|
+
|
134
|
+
self.class.data = self.class.data.map do |item|
|
135
|
+
if item['_id'] == @_id
|
136
|
+
attributes.merge('_id' => @_id)
|
137
|
+
else
|
138
|
+
item
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
if @_id.nil?
|
143
|
+
@_id = next_id
|
144
|
+
self.class.data << attributes.merge('_id' => @_id)
|
145
|
+
end
|
146
|
+
|
147
|
+
self.class.write_yaml
|
148
|
+
true
|
149
|
+
end
|
150
|
+
|
151
|
+
def strip
|
152
|
+
self.class.attributes.each do |attr|
|
153
|
+
send("#{attr}=", send(attr).strip) if send(attr).is_a? String
|
154
|
+
end
|
155
|
+
self
|
156
|
+
end
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
def type_validation_error_message(attr, class_names)
|
161
|
+
allowed_types = class_names.map do |class_name|
|
162
|
+
class_name.gsub('Class', '').downcase
|
163
|
+
end
|
164
|
+
|
165
|
+
"#{attr} must be #{allowed_types.sort.join(', ')}".sub(/.*\K, /, ' or ')
|
166
|
+
end
|
167
|
+
|
168
|
+
def next_id
|
169
|
+
self.class.data.collect { |item| item['_id'] }.compact.max + 1
|
170
|
+
end
|
171
|
+
end
|
172
|
+
end
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'date'
|
4
|
+
require 'readline'
|
5
|
+
require 'yaml'
|
6
|
+
require_relative 'active_data.rb'
|
7
|
+
|
8
|
+
# Loads all yaml files in given directory, creates ActiveData subclass
|
9
|
+
# for each file and provides an interactive shell.
|
10
|
+
module DataFiles
|
11
|
+
class REPL
|
12
|
+
def initialize(directory)
|
13
|
+
@klass_names = []
|
14
|
+
parse_data(directory)
|
15
|
+
end
|
16
|
+
|
17
|
+
def parse_data(directory)
|
18
|
+
Dir.foreach(File.join(directory, 'data')) do |filename|
|
19
|
+
next unless filename.end_with?('.yml')
|
20
|
+
|
21
|
+
filepath = File.join(directory, 'data', filename)
|
22
|
+
key = File.basename(filepath, File.extname(filepath))
|
23
|
+
data = load_yaml(filepath)
|
24
|
+
|
25
|
+
klass_name = key.capitalize.delete_suffix('s')
|
26
|
+
create_class(klass_name, data)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def create_class(klass_name, data)
|
31
|
+
@klass_names << klass_name
|
32
|
+
|
33
|
+
types = parse_types(data)
|
34
|
+
klass = Class.new(ActiveData) do
|
35
|
+
class_variable_set(:@@data, data)
|
36
|
+
class_variable_set(:@@attributes, data.first.keys)
|
37
|
+
class_variable_set(:@@types, types)
|
38
|
+
attr_accessor(*data.first.keys)
|
39
|
+
end
|
40
|
+
|
41
|
+
Object.const_set(klass_name, klass)
|
42
|
+
end
|
43
|
+
|
44
|
+
def prompt
|
45
|
+
initial_prompt
|
46
|
+
read_input
|
47
|
+
end
|
48
|
+
|
49
|
+
def initial_prompt
|
50
|
+
puts 'Available data models:'
|
51
|
+
@klass_names.sort.each do |klass_name|
|
52
|
+
puts " - #{klass_name}"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def read_input
|
57
|
+
bnd = binding
|
58
|
+
while (input = Readline.readline('> ', true))
|
59
|
+
begin
|
60
|
+
puts bnd.eval(input).to_s
|
61
|
+
rescue StandardError => e
|
62
|
+
puts "\e[31m#{e.class}:\e[0m #{e.message}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def load_yaml(filepath)
|
70
|
+
YAML
|
71
|
+
.safe_load(File.read(filepath), permitted_classes: [Date])
|
72
|
+
.map
|
73
|
+
.each_with_index { |item, index| item.merge('_id' => index + 1) }
|
74
|
+
end
|
75
|
+
|
76
|
+
def parse_types(data)
|
77
|
+
types = {}
|
78
|
+
data.first.keys.each do |attr|
|
79
|
+
types[attr] = data.collect { |item| item[attr].class.name }
|
80
|
+
types[attr] << 'TrueClass' if types[attr].include?('FalseClass')
|
81
|
+
types[attr] << 'FalseClass' if types[attr].include?('TrueClass')
|
82
|
+
types[attr].uniq!
|
83
|
+
end
|
84
|
+
types
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
metadata
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: data_files
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0.rc1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andreas Zecher
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2020-01-09 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: This interactive shell allows users to manipulate Middleman Data Files
|
14
|
+
with an API similar to ActiveRecord.
|
15
|
+
email:
|
16
|
+
- andreas@polylists.com
|
17
|
+
executables: []
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- ".rubocop.yml"
|
22
|
+
- ".ruby-version"
|
23
|
+
- CHANGELOG.md
|
24
|
+
- Gemfile
|
25
|
+
- Gemfile.lock
|
26
|
+
- LICENSE.txt
|
27
|
+
- README.md
|
28
|
+
- Rakefile
|
29
|
+
- data-files.gemspec
|
30
|
+
- data/games.yml
|
31
|
+
- data/lists.yml
|
32
|
+
- lib/data_files/active_data.rb
|
33
|
+
- lib/data_files/repl.rb
|
34
|
+
- lib/data_files/version.rb
|
35
|
+
homepage: https://github.com/pixelate/data-files
|
36
|
+
licenses:
|
37
|
+
- MIT
|
38
|
+
metadata:
|
39
|
+
homepage_uri: https://github.com/pixelate/data-files
|
40
|
+
source_code_uri: https://github.com/pixelate/data-files
|
41
|
+
changelog_uri: https://github.com/pixelate/data-files/blob/master/CHANGELOG.md
|
42
|
+
post_install_message:
|
43
|
+
rdoc_options: []
|
44
|
+
require_paths:
|
45
|
+
- lib
|
46
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
47
|
+
requirements:
|
48
|
+
- - ">="
|
49
|
+
- !ruby/object:Gem::Version
|
50
|
+
version: 2.3.0
|
51
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">"
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 1.3.1
|
56
|
+
requirements: []
|
57
|
+
rubygems_version: 3.1.2
|
58
|
+
signing_key:
|
59
|
+
specification_version: 4
|
60
|
+
summary: REPL for Middleman Data Files
|
61
|
+
test_files: []
|