REPL for Middleman Data Files
Written during Lab Week in January 2020 at Mynewsdesk.
This interactive shell allows users to manipulate Middleman Data Files with an API similar to ActiveRecord.
Installation
Add the following line to your Gemfile:
gem 'data_files'
Then run:
bundle install
Getting Started
To start the interactive shell run the following command from your Middleman project directory:
> bundle exec data_files
Querying data
Given a file located in data/games.yml in a Middleman project directory, we can query our data in different ways:
> Game.first
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: nil, _id: 1>
> Game.last
#<Game title: "Xenon 2: Megablast", url: "http://www.bitmap-brothers.co.uk/our-games/past/xenon2.htm", year: 1989, _id: 11>
> Game.find_by(title: "Another World")
#<Game title: "Another World", url: "http://www.anotherworld.fr/anotherworld_uk/", year: 1991, _id: 4>
> Game.where(year: 1991)
[#<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>]
> Game.all.count
10
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:
> Game.find_by(_id: 12)
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: 12>
Autocompletion
The methods where and find_by support autocompletion for attribute values which can be invoked by pressing the TAB key. If several suggestions are available, the TAB key needs to be pressed twice to show the suggestions.
> Game.where(title: "⇥
A Light In Chorus Another World Donut County wipE'out
Advance Wars BOXBOY! Firewatch
Animal Crossing: New Leaf Commander Keen in Goodbye, Galaxy Xenon 2: Megablast
Creating new data
We can add new items to our data file:
> game = Game.new(title: "Super Mario Maker 2", year: 2019, url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/")
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: nil>
> game.save
true
> game
#<Game title: "Super Mario Maker 2", url: "https://www.nintendo.com/games/detail/super-mario-maker-2-switch/", year: 2019, _id: 12>
Updating data
We can also update exisiting items:
> game = Game.where(year: nil).first
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: nil, _id: 1>
> game.year = 2020
2020
> game.save
true
> game
#<Game title: "A Light In Chorus", url: "http://www.alightinchorus.com", year: 2020, _id: 1>
Normalizing data
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:
---
- title: A Light In Chorus
url: http://www.alightinchorus.com
year:
Leading and trailing whitespace is automatically removed from string attributes on save:
> game = Game.new(title: " Bubble Bobble ")
#<Game title: " Bubble Bobble ", url: nil, year: nil, _id: nil>
> game.save
true
> game
#<Game title: "Bubble Bobble", url: nil, year: nil, _id: 11>
Validation
Data will be automatically be validated on save. The validation logic is derived from the exisiting values in the YAML files.
> list = List.new(title: nil, user: 1, slug: false, ordered: "yes", featured: "no", published_at: "today", games: "A Light In Chorus, Advanced Wars")
#<List title: nil, user: 1, slug: false, ordered: "yes", featured: "no", published_at: "today", games: "A Light In Chorus, Advanced Wars", _id: nil>
> list.save
false
> list.errors
["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"]
Here's an example for a valid List item. See test/data/lists.yml for the data structure that the validation logic is derived from.
> 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"}])
#<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>
> list.valid?
true
> list.errors
[]
> list.save
true
Primary keys must be unique within a YAML file:
> game = Game.new(title: 'Another World')
#<Game title: "Another World", url: nil, year: nil, _id: nil>
> game.valid?
false
> game.errors
["Game with title Another World already exists"]