knish 0.2.6

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.
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Knish
2
+ VERSION = "0.2.6"
3
+ end
@@ -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
@@ -0,0 +1,7 @@
1
+ module ActiveModel
2
+ module Model
3
+ end
4
+
5
+ class Name < Struct.new(:klass, :namespace)
6
+ end
7
+ end
@@ -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