knish 0.2.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +29 -0
- data/Rakefile +2 -0
- data/knish.gemspec +25 -0
- data/lib/knish.rb +36 -0
- data/lib/knish/builder.rb +35 -0
- data/lib/knish/collection.rb +41 -0
- data/lib/knish/collection_config.rb +53 -0
- data/lib/knish/db_config.rb +29 -0
- data/lib/knish/delegate_inspector.rb +17 -0
- data/lib/knish/existing_models.rb +28 -0
- data/lib/knish/member.rb +31 -0
- data/lib/knish/model.rb +87 -0
- data/lib/knish/model_config.rb +51 -0
- data/lib/knish/reader.rb +47 -0
- data/lib/knish/version.rb +3 -0
- data/lib/knish/writer.rb +40 -0
- data/spec/builder_spec.rb +82 -0
- data/spec/collection_config_spec.rb +43 -0
- data/spec/collection_spec.rb +98 -0
- data/spec/fixtures/active_model_mock.rb +7 -0
- data/spec/knish_spec.rb +32 -0
- data/spec/model_config_spec.rb +54 -0
- data/spec/model_spec.rb +188 -0
- data/spec/reader_spec.rb +56 -0
- data/spec/spec_helper.rb +34 -0
- data/spec/support/file_helpers.rb +19 -0
- data/spec/support/model_classes.rb +32 -0
- data/spec/writer_spec.rb +59 -0
- metadata +132 -0
@@ -0,0 +1,51 @@
|
|
1
|
+
module Knish
|
2
|
+
class ModelConfig < SimpleDelegator
|
3
|
+
attr_accessor :db_config, :path, :id, :omitted_namespace
|
4
|
+
attr_writer :data_attributes, :markdown_attributes, :collections
|
5
|
+
|
6
|
+
def initialize(db_config, path, id=nil)
|
7
|
+
super(db_config)
|
8
|
+
@path = path
|
9
|
+
@db_config = db_config
|
10
|
+
@id = id || next_id
|
11
|
+
end
|
12
|
+
|
13
|
+
def data_attributes
|
14
|
+
@data_attributes ||= []
|
15
|
+
end
|
16
|
+
|
17
|
+
def markdown_attributes
|
18
|
+
@markdown_attributes ||= []
|
19
|
+
end
|
20
|
+
|
21
|
+
def collections
|
22
|
+
@collections ||= []
|
23
|
+
end
|
24
|
+
|
25
|
+
def all_attributes
|
26
|
+
data_attributes + markdown_attributes
|
27
|
+
end
|
28
|
+
|
29
|
+
def collection_root
|
30
|
+
"#{db_config.db_root}/#{path}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def model_root
|
34
|
+
"#{collection_root}/#{id}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def next_id
|
38
|
+
ExistingModels.new(collection_root).next_id
|
39
|
+
end
|
40
|
+
|
41
|
+
def template_path
|
42
|
+
"#{view_to_db_path}/#{db_name}/#{path}/#{id}"
|
43
|
+
end
|
44
|
+
|
45
|
+
def inspect
|
46
|
+
DelegateInspector.new(self,
|
47
|
+
[:db_config, :path, :id, :omitted_namespace, :data_attributes, :markdown_attributes, :collections]
|
48
|
+
).to_inspect
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/knish/reader.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
module Knish
|
2
|
+
class Reader
|
3
|
+
attr_reader :config
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def get_json
|
10
|
+
JSON.parse(read_file(config.data_filename) || '{}')
|
11
|
+
end
|
12
|
+
|
13
|
+
def get_markdown
|
14
|
+
config.markdown_attributes.inject({}) do |hash, key|
|
15
|
+
hash[key] = read_markdown(key)
|
16
|
+
hash
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def get_collections
|
21
|
+
config.collections.inject({}) do |hash, collection_name|
|
22
|
+
collection = Collection.new(collection_name, config)
|
23
|
+
collection.load
|
24
|
+
hash[collection_name] = collection
|
25
|
+
hash
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def template(key)
|
30
|
+
"#{config.template_path}/#{key}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def persisted?
|
34
|
+
!!read_file(config.data_filename)
|
35
|
+
end
|
36
|
+
|
37
|
+
def read_file(filename)
|
38
|
+
File.read("#{config.model_root}/#{filename}") rescue nil
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def read_markdown(key)
|
44
|
+
read_file("_#{key}.md") || ""
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/knish/writer.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Knish
|
2
|
+
class Writer
|
3
|
+
attr_reader :config
|
4
|
+
|
5
|
+
def initialize(config)
|
6
|
+
@config = config
|
7
|
+
end
|
8
|
+
|
9
|
+
def build_directories(directories)
|
10
|
+
make_root
|
11
|
+
directories.each {|dir| make_directory(dir) }
|
12
|
+
end
|
13
|
+
|
14
|
+
def save_json(attributes)
|
15
|
+
make_root
|
16
|
+
write_file(config.data_filename, attributes.to_json)
|
17
|
+
end
|
18
|
+
|
19
|
+
def save_markdown(attributes)
|
20
|
+
make_root
|
21
|
+
attributes.each do |key, data|
|
22
|
+
write_file("_#{key}.md", data)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
|
28
|
+
def write_file(filename, data)
|
29
|
+
File.open("#{config.model_root}/#{filename}", 'w') { |f| f.write(data) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def make_root
|
33
|
+
FileUtils.mkdir_p(config.model_root) unless File.exist?(config.model_root)
|
34
|
+
end
|
35
|
+
|
36
|
+
def make_directory(dir)
|
37
|
+
FileUtils.mkdir_p("#{config.model_root}/#{dir}") unless File.exist?("#{config.model_root}/#{dir}")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Knish, '.build' do
|
4
|
+
let(:config) { project_class.config }
|
5
|
+
|
6
|
+
context 'when all model level configuration is provided' do
|
7
|
+
let(:project_class) {
|
8
|
+
Project # defined in support
|
9
|
+
}
|
10
|
+
|
11
|
+
it 'builds a model class with the right attributes' do
|
12
|
+
expect(project_class.ancestors).to include(Knish::Model)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has the right data in the model config' do
|
16
|
+
expect(config).to be_a(Knish::ModelConfig)
|
17
|
+
expect(config.path).to eq('projects')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'adds the right class configuration attributes to the model class' do
|
21
|
+
expect(config.data_attributes).to eq(['name', 'url'])
|
22
|
+
expect(config.markdown_attributes).to eq(['description'])
|
23
|
+
expect(config.collections).to eq(['stories'])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when some model level configuration is set' do
|
28
|
+
let(:project_class) {
|
29
|
+
Knish.build('projects') { |c| c.collections = ['features'] }
|
30
|
+
}
|
31
|
+
|
32
|
+
it 'sets missing model configuration to an empty array' do
|
33
|
+
expect(config.data_attributes).to eq([])
|
34
|
+
expect(config.markdown_attributes).to eq([])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when setting db level configuration' do
|
39
|
+
let(:project_class) {
|
40
|
+
Knish.build('projects') do |config|
|
41
|
+
config.db_name = 'pizza'
|
42
|
+
config.data_attributes = ['name', 'url']
|
43
|
+
end
|
44
|
+
}
|
45
|
+
|
46
|
+
it 'updates the database related configuration' do
|
47
|
+
expect(config.db_name).to eq('pizza')
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context 'when Knish already has custom global database configuration' do
|
52
|
+
let(:project_class) {
|
53
|
+
Knish.build('projects') do |config|
|
54
|
+
config.db_name = 'pizza'
|
55
|
+
config.data_attributes = ['name', 'url']
|
56
|
+
end
|
57
|
+
}
|
58
|
+
|
59
|
+
before do
|
60
|
+
Knish.configure do |config|
|
61
|
+
config.data_filename = 'json-data.json'
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'does not mutate the Knish global configuration' do
|
66
|
+
expect(Knish.config.db_name).to_not eq('pizza')
|
67
|
+
end
|
68
|
+
|
69
|
+
it 'model db configuration includes changes made at Knish level' do
|
70
|
+
expect(config.data_filename).to eq('json-data.json')
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'includes configured modifications to the db setup' do
|
74
|
+
expect(config.db_name).to eq('pizza')
|
75
|
+
end
|
76
|
+
|
77
|
+
it 'uses model level configurations' do
|
78
|
+
expect(config.data_attributes).to eq(['name', 'url'])
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Knish::CollectionConfig do
|
4
|
+
let(:config) { Knish::CollectionConfig.new(model_config, path) }
|
5
|
+
let(:model_config) { Project.config }
|
6
|
+
let(:path) { 'stories' }
|
7
|
+
|
8
|
+
let(:project_path) { full_db_path + '/projects/1' }
|
9
|
+
let(:stories_path) { project_path + '/stories' }
|
10
|
+
|
11
|
+
before do
|
12
|
+
# setup file system of a collection
|
13
|
+
FileUtils.mkdir_p(project_path + "/stories/1")
|
14
|
+
File.open(project_path + "/data.json", 'w') {|f| f.write({name: 'hello project'}.to_json) }
|
15
|
+
File.open(project_path + "/stories/1/data.json", 'w') {|f| f.write({name: 'hello feature', ___type: 'Feature'}.to_json) }
|
16
|
+
end
|
17
|
+
|
18
|
+
after { clear_db(full_db_path) }
|
19
|
+
|
20
|
+
describe '#collection_root' do
|
21
|
+
it 'should be based on the model config and the path' do
|
22
|
+
expect(config.collection_root).to eq(stories_path)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
describe '#next_id' do
|
27
|
+
it 'should be the next available id' do
|
28
|
+
expect(config.next_id).to eq(2)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
describe '#memeber_config(klass, id)' do
|
33
|
+
let(:member_config) { config.member_config(Bug.config, 2) }
|
34
|
+
|
35
|
+
it 'should have the model attributes of the class' do
|
36
|
+
expect(member_config.all_attributes).to eq(Bug.config.all_attributes)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should have the path specific to this model' do
|
40
|
+
expect(member_config.model_root).to eq(project_path + "/stories/2")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Knish::Collection do
|
4
|
+
let(:collection) { Knish::Collection.new('stories', parent_config) }
|
5
|
+
let(:parent_config) { Knish::ModelConfig.new(fixture_db_config, 'projects') }
|
6
|
+
|
7
|
+
let(:model_class) {
|
8
|
+
Knish.build('features') do |config|
|
9
|
+
config.db_directory = db_fixture_path
|
10
|
+
config.data_attributes = ['name']
|
11
|
+
config.markdown_attributes = ['description']
|
12
|
+
end
|
13
|
+
}
|
14
|
+
|
15
|
+
describe '#add' do
|
16
|
+
let(:model) { model_class.new }
|
17
|
+
|
18
|
+
it 'puts the model in the collection' do
|
19
|
+
collection.add(model)
|
20
|
+
expect(collection.first).to eq(model)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'raises an error if the model is not a Knish object' do
|
24
|
+
expect{
|
25
|
+
collection.add({})
|
26
|
+
}.to raise_error
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'changes the configuration on the way in' do
|
30
|
+
collection.add(model)
|
31
|
+
expect(model.config.path).to eq('projects/1/stories')
|
32
|
+
end
|
33
|
+
|
34
|
+
context 'when objects of that class already exist in the model configured location' do
|
35
|
+
let(:original_model_collection_path) { db_fixture_path + "/knish/features" }
|
36
|
+
|
37
|
+
before do
|
38
|
+
FileUtils.mkdir_p(original_model_collection_path + "/3")
|
39
|
+
end
|
40
|
+
|
41
|
+
after do
|
42
|
+
clear_db(db_fixture_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
it 'reevaluates the id in the configuration' do
|
46
|
+
expect {
|
47
|
+
collection.add(model)
|
48
|
+
}.to change { model.config.id }
|
49
|
+
|
50
|
+
expect(collection.first.id).to eq(1)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#save' do
|
56
|
+
let(:models) {
|
57
|
+
[model_class.new(name: 'bar'), model_class.new(name: 'foo')]
|
58
|
+
}
|
59
|
+
|
60
|
+
it 'calls save on each of the models' do
|
61
|
+
models.each do |m|
|
62
|
+
expect(m).to receive(:save)
|
63
|
+
collection.add(m)
|
64
|
+
end
|
65
|
+
collection.save
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe '#load' do
|
70
|
+
before do
|
71
|
+
FileUtils.mkdir_p(collection.config.collection_root + "/1")
|
72
|
+
File.open(collection.config.collection_root + "/1/data.json", 'w') {|f| f.write({name: 'Hello', ___type: 'Feature'}.to_json) }
|
73
|
+
FileUtils.mkdir_p(collection.config.collection_root + "/2")
|
74
|
+
File.open(collection.config.collection_root + "/2/data.json", 'w') {|f| f.write({name: 'Goodbye', ___type: 'Bug'}.to_json) }
|
75
|
+
end
|
76
|
+
|
77
|
+
after { clear_db(db_fixture_path) }
|
78
|
+
|
79
|
+
it 'should be the right size' do
|
80
|
+
collection.load
|
81
|
+
expect(collection.size).to eq(2)
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should remove existing models' do
|
85
|
+
project = Project.new(name: 'not for saving, thanks!')
|
86
|
+
collection << project
|
87
|
+
collection.load
|
88
|
+
expect(collection).not_to include(project)
|
89
|
+
end
|
90
|
+
|
91
|
+
it 'should build the right type of models for each' do
|
92
|
+
collection.load
|
93
|
+
|
94
|
+
expect(collection.first).to be_a(Feature)
|
95
|
+
expect(collection.last).to be_a(Bug)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
data/spec/knish_spec.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Knish, '.configure' do
|
4
|
+
it 'allows the configuration any values in the global config' do
|
5
|
+
Knish.configure do |config|
|
6
|
+
config.db_directory = db_fixture_path
|
7
|
+
config.view_to_db_path = 'view_to_db_path'
|
8
|
+
config.data_filename = 'my-data.json'
|
9
|
+
config.db_name = 'my-db'
|
10
|
+
end
|
11
|
+
|
12
|
+
expect(Knish.config.db_directory).to eq(db_fixture_path)
|
13
|
+
expect(Knish.config.view_to_db_path).to eq('view_to_db_path')
|
14
|
+
expect(Knish.config.data_filename).to eq('my-data.json')
|
15
|
+
expect(Knish.config.db_name).to eq('my-db')
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'uses a set of default values if not configured' do
|
19
|
+
expect(Knish.config.view_to_db_path).to eq('/../..')
|
20
|
+
expect(Knish.config.data_filename).to eq('data.json')
|
21
|
+
expect(Knish.config.db_name).to eq('knish')
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'constructs the database root from the db directory and the db name' do
|
25
|
+
Knish.configure do |config|
|
26
|
+
config.db_directory = 'db_dir'
|
27
|
+
config.db_name = 'my-db'
|
28
|
+
end
|
29
|
+
|
30
|
+
expect(Knish.config.db_root).to eq('db_dir/my-db')
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Knish::ModelConfig do
|
4
|
+
let(:config) { Knish::ModelConfig.new(fixture_db_config, 'projects') }
|
5
|
+
|
6
|
+
describe '#id' do
|
7
|
+
context 'when no id is passed in' do
|
8
|
+
it 'uses the next id' do
|
9
|
+
expect(config.id).to eq(config.next_id)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
context 'when an id is given' do
|
14
|
+
let(:config) { Knish::ModelConfig.new(fixture_db_config, 'projects', 44) }
|
15
|
+
|
16
|
+
it 'uses it' do
|
17
|
+
expect(config.id).to eq(44)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe '#collection_root' do
|
23
|
+
it 'returns the collection root path' do
|
24
|
+
expect(config.collection_root).to eq(File.expand_path("#{db_fixture_path}/knish/projects"))
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#next_id' do
|
29
|
+
before { clear_db(db_fixture_path) }
|
30
|
+
after { clear_db(db_fixture_path) }
|
31
|
+
|
32
|
+
context 'when there are no models in the collection' do
|
33
|
+
it 'returns 1' do
|
34
|
+
expect(config.next_id).to eq(1)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
context 'when there are already models in the system' do
|
39
|
+
before { FileUtils.mkdir_p("#{config.collection_root}/1") }
|
40
|
+
|
41
|
+
it 'returns the next available id' do
|
42
|
+
expect(config.next_id).to eq(2)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
context 'when a model is missing but was built' do
|
47
|
+
before { FileUtils.mkdir_p("#{config.collection_root}/2") }
|
48
|
+
|
49
|
+
it 'returns the next available id' do
|
50
|
+
expect(config.next_id).to eq(3)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|