collabda 0.0.1a
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/lib/collabda.rb +113 -0
- data/spec/lib/collabda_spec.rb +179 -0
- metadata +60 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 72411f5cfca47c38d7810c6d0619bfb68fda266a
|
4
|
+
data.tar.gz: 5c73e2d894bbb0d1fbb64c7c527686a4826873f9
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 647e46c9f8395fd4e99d9534764073ec36408e77dc3663fc9af315872081056678ca2da94c0f41c614169cc78a908ff8dcb699fc6adcba41f0772973dfbc0255
|
7
|
+
data.tar.gz: 762fa5e7c1d0e82ca7937b9d13330c2667c44bdd0147ba78cc5aaadfc58b4af65a0e14de5f9de1c6512218cb3650f9ed4153ce5680354254df8925f32e2ffb5a
|
data/lib/collabda.rb
ADDED
@@ -0,0 +1,113 @@
|
|
1
|
+
module Collabda
|
2
|
+
def self.included(base)
|
3
|
+
base.extend(Enumerable)
|
4
|
+
base.extend(ClassMethods)
|
5
|
+
@classes ||= []
|
6
|
+
@classes << base
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.rebuild_collections
|
10
|
+
return if @classes.nil?
|
11
|
+
@classes.each do |c|
|
12
|
+
c.build_collection
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.collection(class_name, &block)
|
17
|
+
model = Class.new do
|
18
|
+
include Collabda
|
19
|
+
self.class_eval(&block)
|
20
|
+
end
|
21
|
+
nesting = block.binding.eval("Module.nesting[0]") || Object
|
22
|
+
nesting.instance_eval{const_set(class_name, model)}
|
23
|
+
model.build_collection
|
24
|
+
return model
|
25
|
+
end
|
26
|
+
|
27
|
+
# def self.watch_files
|
28
|
+
# @classes.map{|c| c.source_path}
|
29
|
+
# end
|
30
|
+
|
31
|
+
InvalidSource = Class.new(StandardError)
|
32
|
+
MissingAttributes = Class.new(StandardError)
|
33
|
+
|
34
|
+
module ClassMethods
|
35
|
+
def source(path, options={})
|
36
|
+
@source_path = path
|
37
|
+
@format = options[:type] || :yaml
|
38
|
+
fetch_data
|
39
|
+
end
|
40
|
+
|
41
|
+
def all
|
42
|
+
@collabda_models
|
43
|
+
end
|
44
|
+
|
45
|
+
def each(&block)
|
46
|
+
all.each(&block)
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_validity
|
50
|
+
raise InvalidSource if @source_path.nil?
|
51
|
+
raise MissingAttributes if @properties.nil?
|
52
|
+
end
|
53
|
+
|
54
|
+
def build_collection
|
55
|
+
check_validity
|
56
|
+
fetch_data
|
57
|
+
@collabda_models = @parsed_data.map do |el|
|
58
|
+
build(el)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def build(attributes_hash)
|
63
|
+
model = self.new
|
64
|
+
@properties.each do |attribute|
|
65
|
+
model.instance_variable_set("@#{attribute}",attributes_hash[attribute])
|
66
|
+
end
|
67
|
+
model
|
68
|
+
end
|
69
|
+
|
70
|
+
def properties(*attributes)
|
71
|
+
@properties=attributes
|
72
|
+
end
|
73
|
+
|
74
|
+
def source_path
|
75
|
+
@source_path
|
76
|
+
end
|
77
|
+
|
78
|
+
def parsed_data
|
79
|
+
@parsed_data
|
80
|
+
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def fetch_data
|
84
|
+
io = File.open(@source_path)
|
85
|
+
@parsed_data = Readers.send(@format,io)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
module Readers
|
90
|
+
def self.yaml(io)
|
91
|
+
YAML.load(io).map{|model| model.symbolize_keys}
|
92
|
+
end
|
93
|
+
def self.json(io)
|
94
|
+
JSON.load(io).map{|model| model.symbolize_keys}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
if defined? Rails
|
100
|
+
# TODO: add development autoreload code
|
101
|
+
else
|
102
|
+
class Hash
|
103
|
+
def symbolize_keys
|
104
|
+
dup.symbolize_keys!
|
105
|
+
end
|
106
|
+
def symbolize_keys!
|
107
|
+
keys.each do |key|
|
108
|
+
self[(key.to_sym rescue key) || key] = delete(key)
|
109
|
+
end
|
110
|
+
self
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
require 'collabda'
|
2
|
+
|
3
|
+
FOO_YAML = <<-yaml
|
4
|
+
- name: Foo
|
5
|
+
description: "yo foo"
|
6
|
+
- name: Bar
|
7
|
+
description: "at the bar"
|
8
|
+
- name: Baz
|
9
|
+
description: "get some baz"
|
10
|
+
yaml
|
11
|
+
|
12
|
+
FOO_JSON = <<-json
|
13
|
+
[
|
14
|
+
{"name": "FooJ", "description": "yo foo"},
|
15
|
+
{"name": "BarJ", "description": "at the bar"},
|
16
|
+
{"name": "BazJ", "description": "get some baz"}
|
17
|
+
]
|
18
|
+
json
|
19
|
+
|
20
|
+
describe "Collabda" do
|
21
|
+
before(:each) do
|
22
|
+
File.stub(:open){StringIO.open(FOO_YAML)}
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:class_builder) do
|
26
|
+
->(&block) do
|
27
|
+
-> do
|
28
|
+
Class.new do
|
29
|
+
include Collabda
|
30
|
+
# include Collabda::Document
|
31
|
+
block.call(self)
|
32
|
+
attr_reader :description
|
33
|
+
properties :name, :description
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
let(:yaml_class_factory) do
|
40
|
+
class_builder.call{|c| c.source "spec/lib/foo.yaml", :type=>:yaml}
|
41
|
+
end
|
42
|
+
|
43
|
+
|
44
|
+
it "opens specified file during class definition" do
|
45
|
+
File.should_receive(:open)
|
46
|
+
model_class = yaml_class_factory.call
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "errors" do
|
50
|
+
let(:model_class){Class.new{include Collabda}}
|
51
|
+
it "raises InvalidSource error if source not set" do
|
52
|
+
expect{model_class.build_collection}.to raise_error Collabda::InvalidSource
|
53
|
+
end
|
54
|
+
it "raises MissingAttributes error if not set" do
|
55
|
+
model_class.source "foo.yaml"
|
56
|
+
expect{model_class.build_collection}.to raise_error Collabda::MissingAttributes
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "Yaml model" do
|
61
|
+
let!(:model_class){yaml_class_factory.call}
|
62
|
+
specify "source sets a source_path class variable" do
|
63
|
+
expect(model_class.source_path).to be_a String
|
64
|
+
end
|
65
|
+
|
66
|
+
specify "properties sets fields to use" do
|
67
|
+
expect(model_class.instance_variable_get(:@properties)).to eq [:name, :description]
|
68
|
+
end
|
69
|
+
|
70
|
+
it "tracks where Collabda is included" do
|
71
|
+
FooList = model_class
|
72
|
+
expect(Collabda.instance_variable_get(:@classes)).to include FooList
|
73
|
+
end
|
74
|
+
|
75
|
+
# it "maintains a list of yaml files to watch" do
|
76
|
+
# expect(Collabda.watch_files).to include "spec/lib/foo.yaml"
|
77
|
+
# end
|
78
|
+
|
79
|
+
it "loads yaml data from file" do
|
80
|
+
expect(model_class.parsed_data.count).to eq 3
|
81
|
+
end
|
82
|
+
|
83
|
+
it "uses symbols as attribute keys for consistency" do
|
84
|
+
expect(model_class.parsed_data.first.keys.first).to be_a Symbol
|
85
|
+
end
|
86
|
+
|
87
|
+
it "builds a new model for each element in the data" do
|
88
|
+
model_class.should_receive(:new).exactly(3).times.and_return(double(:test))
|
89
|
+
model_class.build_collection
|
90
|
+
end
|
91
|
+
|
92
|
+
it "reloads the yaml file on build_collection" do
|
93
|
+
model_class.should_receive(:fetch_data).and_call_original
|
94
|
+
model_class.build_collection
|
95
|
+
end
|
96
|
+
|
97
|
+
it "can build a new instance from an attributes hash, bypassing yaml" do
|
98
|
+
instance = model_class.build(:name=>"Bat",:description=>"man")
|
99
|
+
expect(instance.description).to eq "man"
|
100
|
+
end
|
101
|
+
|
102
|
+
context "when collection built" do
|
103
|
+
before(:each){model_class.build_collection}
|
104
|
+
it "it provides access to all models" do
|
105
|
+
expect(model_class.all.count).to eq 3
|
106
|
+
expect(model_class.all.first.class).to eq model_class
|
107
|
+
end
|
108
|
+
|
109
|
+
it "sets instance variables for each yaml attribute" do
|
110
|
+
expect(model_class.all.first.instance_variable_get(:@name)).to eq "Foo"
|
111
|
+
end
|
112
|
+
|
113
|
+
it "implements enumerable correctly" do
|
114
|
+
expect(model_class.map{|m| m.instance_variable_get(:@name)}).to eq ["Foo","Bar","Baz"]
|
115
|
+
end
|
116
|
+
|
117
|
+
it "works alongside attr_reader" do
|
118
|
+
expect(model_class.first.description).to eq "yo foo"
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
describe "Json model" do
|
124
|
+
before(:each) do
|
125
|
+
File.stub(:open){StringIO.open(FOO_JSON)}
|
126
|
+
end
|
127
|
+
let(:json_class_factory) do
|
128
|
+
class_builder.call{|c| c.source "http://foo.bar/baz.json", :type=>:json}
|
129
|
+
end
|
130
|
+
it "parses json into ruby data" do
|
131
|
+
model_class = json_class_factory.call
|
132
|
+
expect(model_class.parsed_data.first[:name]).to eq "FooJ"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
describe "collection" do
|
137
|
+
let(:built_collection) do
|
138
|
+
Collabda.collection(:Food) do
|
139
|
+
source ""
|
140
|
+
properties :none
|
141
|
+
def foo
|
142
|
+
end
|
143
|
+
def self.bar
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
after(:each){Object.send(:remove_const,:Food) rescue nil}
|
149
|
+
|
150
|
+
it "creates a class in parent context" do
|
151
|
+
built_collection = module TestModels
|
152
|
+
class C
|
153
|
+
Collabda.collection(:Food){source "";properties :none}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
expect(built_collection.to_s).to eq "TestModels::C::Food"
|
157
|
+
end
|
158
|
+
|
159
|
+
it "creates a class root context if not in module" do
|
160
|
+
expect(built_collection.to_s).to eq "Food"
|
161
|
+
end
|
162
|
+
|
163
|
+
it "includes Collabda in the new class" do
|
164
|
+
expect(built_collection).to respond_to :source
|
165
|
+
end
|
166
|
+
|
167
|
+
it "executes its block while building the class" do
|
168
|
+
expect(built_collection).to respond_to :bar
|
169
|
+
end
|
170
|
+
|
171
|
+
it "executes is block in the correct context" do
|
172
|
+
expect(built_collection.new).to respond_to :foo
|
173
|
+
end
|
174
|
+
|
175
|
+
it "auto-builds the collection from its source" do
|
176
|
+
expect{Collabda.collection(:Food){}}.to raise_error Collabda::InvalidSource
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
metadata
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: collabda
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1a
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- James Edwards-Jones
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-07-19 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '2.6'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '2.6'
|
27
|
+
description: Collabda uses JSON or YAML files to build a collection of classes.
|
28
|
+
email:
|
29
|
+
executables: []
|
30
|
+
extensions: []
|
31
|
+
extra_rdoc_files: []
|
32
|
+
files:
|
33
|
+
- lib/collabda.rb
|
34
|
+
- spec/lib/collabda_spec.rb
|
35
|
+
homepage: https://github.com/Jamedjo/Collabda
|
36
|
+
licenses:
|
37
|
+
- Apache-2.0
|
38
|
+
metadata: {}
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - '>'
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: 1.3.1
|
53
|
+
requirements: []
|
54
|
+
rubyforge_project:
|
55
|
+
rubygems_version: 2.1.9
|
56
|
+
signing_key:
|
57
|
+
specification_version: 4
|
58
|
+
summary: A library for building models from data files
|
59
|
+
test_files:
|
60
|
+
- spec/lib/collabda_spec.rb
|