ygg 0.0.2 → 1.0.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +12 -14
- data/emulations/samples/event.ygg +3 -0
- data/emulations/samples/invalid_resource.ygg +10 -0
- data/emulations/samples/to_modify.ygg +177 -0
- data/emulations/samples/to_modify_2.ygg +153 -0
- data/emulations/samples/user.ygg +6 -0
- data/emulations/samples/valid.ygg +3 -0
- data/emulations/save_yaml_object.rb +16 -0
- data/emulations/tree.ygg +7 -0
- data/features/accessors.feature +26 -0
- data/features/dsl.feature +24 -0
- data/features/events.feature +17 -0
- data/features/simple_record.feature +40 -0
- data/features/steps/accessors.rb +8 -0
- data/features/steps/dsl.rb +20 -0
- data/features/steps/events.rb +12 -0
- data/features/steps/simple_record.rb +104 -0
- data/features/support/env.rb +2 -0
- data/features/support/hooks.rb +3 -0
- data/lib/ygg.rb +97 -3
- data/lib/ygg/bck/base.rb +63 -0
- data/lib/ygg/bck/branch.rb +31 -0
- data/lib/ygg/bck/exceptions.rb +3 -0
- data/lib/ygg/bck/link.rb +6 -0
- data/lib/ygg/{base.rb → bck/model.rb} +7 -107
- data/lib/ygg/databundle.rb +25 -0
- data/lib/ygg/exceptions.rb +6 -0
- data/lib/ygg/record.rb +7 -0
- data/lib/ygg/session.rb +44 -0
- data/lib/ygg/version.rb +3 -3
- data/spec/spec_helper.rb +61 -0
- data/spec/ygg/databundle_spec.rb +54 -0
- data/spec/ygg/session_spec.rb +203 -0
- data/spec/ygg/ygg_spec.rb +364 -0
- data/ygg.gemspec +5 -0
- metadata +67 -4
@@ -0,0 +1,24 @@
|
|
1
|
+
Feature: Ygg Domain Specific Language
|
2
|
+
In order to access ygg features easily
|
3
|
+
As a Ygg user
|
4
|
+
I want to have a nice DSL
|
5
|
+
|
6
|
+
Background: All that from the other features
|
7
|
+
Given the class User has the field :name
|
8
|
+
And extends Record
|
9
|
+
And the class Account has the field :amount
|
10
|
+
And the class Account has the field :currency
|
11
|
+
And extends Record
|
12
|
+
And I have the file "emulations/samples/user.ygg"
|
13
|
+
|
14
|
+
Scenario: Data on session closed
|
15
|
+
When I load the file
|
16
|
+
And I ask User for records
|
17
|
+
Then I should get an user named "Xavier Via"
|
18
|
+
|
19
|
+
Scenario: Giving path & identifier
|
20
|
+
Given the path "emulations/samples"
|
21
|
+
And the identifier :user
|
22
|
+
When I call ygg with those arguments
|
23
|
+
And I ask User for records
|
24
|
+
Then I should get an user named "Xavier Via"
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Feature: Event managing
|
2
|
+
In order to recall data from past interactions
|
3
|
+
As an ygg user
|
4
|
+
I want to manage an event log
|
5
|
+
|
6
|
+
Background: Setting up the enviroment
|
7
|
+
Given I have the base path "emulations/samples"
|
8
|
+
And the class User has the field :name
|
9
|
+
|
10
|
+
Scenario: Accessing the last event
|
11
|
+
Given the file "emulations/samples/event.ygg" contains:
|
12
|
+
"""
|
13
|
+
- !ruby/object:YggDemo::User
|
14
|
+
name: John Doe
|
15
|
+
"""
|
16
|
+
When I interact with the resource :event
|
17
|
+
Then I should see that the last event loaded some User
|
@@ -0,0 +1,40 @@
|
|
1
|
+
Feature: Load and save a simple record from a file
|
2
|
+
In order to check Ygg's ability to manage record
|
3
|
+
As a Ygg developer
|
4
|
+
I want to load and save simple YAML records
|
5
|
+
|
6
|
+
Scenario: Load a basic User record
|
7
|
+
Given the class User has the field :name
|
8
|
+
And the file "emulations/samples/user.ygg" contains:
|
9
|
+
"""
|
10
|
+
- !ruby/object:YggDemo::User
|
11
|
+
name: Xavier Via
|
12
|
+
"""
|
13
|
+
When I load the file and get the records
|
14
|
+
Then the first record should be a User
|
15
|
+
And it should have the name "Xavier Via"
|
16
|
+
|
17
|
+
@saves
|
18
|
+
Scenario: Save a basic User record
|
19
|
+
Given the class User has the field :name
|
20
|
+
And I have a user "Catalina" in an array
|
21
|
+
When I load the file "emulations/samples/saving.ygg" and add the array to the data
|
22
|
+
Then the file should contain:
|
23
|
+
"""
|
24
|
+
- !ruby/object:YggDemo::User
|
25
|
+
name: Catalina
|
26
|
+
"""
|
27
|
+
|
28
|
+
Scenario: Set a base folder and load files with symbols
|
29
|
+
Given I have the file "emulations/samples/user.ygg"
|
30
|
+
When I set "emulations/samples" as base folder
|
31
|
+
And I load :user
|
32
|
+
Then I should get non empty data
|
33
|
+
|
34
|
+
Scenario: No block given, no data saved
|
35
|
+
Given the class User has the field :name
|
36
|
+
And I have the file "emulations/samples/user.ygg"
|
37
|
+
And I get its modified date
|
38
|
+
When I load it without block
|
39
|
+
Then I should get non empty data
|
40
|
+
And the file should not have been modified
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Given /^the path "(.+?)"$/ do |path|
|
2
|
+
@argument_path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
3
|
+
@argument_path += "/#{path}"
|
4
|
+
end
|
5
|
+
|
6
|
+
Given /^the identifier :(.+?)$/ do |identifier_name|
|
7
|
+
@the_identifier = identifier_name.to_sym
|
8
|
+
end
|
9
|
+
|
10
|
+
When /^I call ygg with those arguments$/ do
|
11
|
+
ygg @argument_path, @the_identifier
|
12
|
+
end
|
13
|
+
|
14
|
+
When /^I load the file$/ do
|
15
|
+
ygg @the_path
|
16
|
+
end
|
17
|
+
|
18
|
+
When /^I ask (.+?) for records$/ do |type|
|
19
|
+
@the_records = YggDemo.const_get(type).from_ygg
|
20
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
Given /^I have the base path "(.+?)"$/ do |path|
|
2
|
+
Ygg.base_dir = File.dirname(File.dirname(File.dirname(__FILE__))) +
|
3
|
+
"/#{path}"
|
4
|
+
end
|
5
|
+
|
6
|
+
When /^I interact with the resource :(.+?)$/ do |resource_name|
|
7
|
+
ygg resource_name.to_sym
|
8
|
+
end
|
9
|
+
|
10
|
+
Then /^I should see that the last event loaded some (.+?)$/ do |what|
|
11
|
+
ygg.events.last.should have_loaded_some(YggDemo.const_get(what))
|
12
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
module YggDemo
|
2
|
+
end
|
3
|
+
|
4
|
+
# Given
|
5
|
+
Given /the class (.+?) has the field :(.+?)$/ do |class_name, field_name|
|
6
|
+
if YggDemo.const_defined? class_name
|
7
|
+
@the_class = YggDemo.const_get class_name
|
8
|
+
else
|
9
|
+
@the_class = YggDemo.const_set class_name, Class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
@the_class.send :attr_accessor, field_name.to_sym
|
13
|
+
end
|
14
|
+
|
15
|
+
Given /the file "(.+?)" contains:/ do |path, content|
|
16
|
+
base_path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
17
|
+
@the_path = "#{base_path}/#{path}"
|
18
|
+
@the_content = File.read @the_path
|
19
|
+
@the_content.should include(content)
|
20
|
+
end
|
21
|
+
|
22
|
+
Given /I have a user "(.+?)"/ do |user_name|
|
23
|
+
@the_user = YggDemo::User.new
|
24
|
+
@the_user.name = user_name
|
25
|
+
end
|
26
|
+
|
27
|
+
Given /^I have the file "(.+?)"$/ do |path|
|
28
|
+
base_path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
29
|
+
@the_path = "#{base_path}/#{path}"
|
30
|
+
File.should exist(@the_path)
|
31
|
+
end
|
32
|
+
|
33
|
+
Given /^I get its modified date$/ do
|
34
|
+
@last_modification_time = File.new(@the_path).mtime
|
35
|
+
end
|
36
|
+
|
37
|
+
# When
|
38
|
+
When /I load the file and get the records/ do
|
39
|
+
@the_records = nil
|
40
|
+
ygg @the_path do
|
41
|
+
@the_records = records
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
When /^I load the file and request for objects of type (.+?)$/ do |type|
|
46
|
+
@the_records = nil
|
47
|
+
ygg @the_path do
|
48
|
+
@the_records = records.of_type YggDemo.const_get(type)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
When /I load the file "(.+?)" and add the array to the data$/ do |file_name|
|
53
|
+
base_path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
54
|
+
@the_path = "#{base_path}/#{file_name}"
|
55
|
+
ygg @the_path do
|
56
|
+
add @the_user
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
When /I set "(.+?)" as base folder/ do |path|
|
61
|
+
base_path = File.dirname(File.dirname(File.dirname(__FILE__)))
|
62
|
+
@base_dir = "#{base_path}/#{path}"
|
63
|
+
Ygg.base_dir = @base_dir
|
64
|
+
end
|
65
|
+
|
66
|
+
When /^I load :([a-z]+?)$/ do |file|
|
67
|
+
@the_data = nil
|
68
|
+
ygg file.to_sym do
|
69
|
+
@the_data = records
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
When /^I load it without block$/ do
|
74
|
+
@the_data = ygg @the_path
|
75
|
+
end
|
76
|
+
|
77
|
+
# Them
|
78
|
+
Then /the first record should be a (.+?)$/ do |type|
|
79
|
+
@first_record = @the_records.first
|
80
|
+
@first_record.should be_a(YggDemo.const_get(type))
|
81
|
+
end
|
82
|
+
|
83
|
+
Then /it should have the (.+?) "(.+?)"/ do |field_name, value|
|
84
|
+
@first_record.send(field_name.to_sym).should == value
|
85
|
+
end
|
86
|
+
|
87
|
+
Then /the file should contain:/ do |content|
|
88
|
+
File.should exist(@the_path)
|
89
|
+
@the_content = File.read @the_path
|
90
|
+
@the_content.should include(content)
|
91
|
+
end
|
92
|
+
|
93
|
+
Then /^I should get non empty data$/ do
|
94
|
+
@the_data.should_not be_empty
|
95
|
+
end
|
96
|
+
|
97
|
+
Then /^the file should not have been modified$/ do
|
98
|
+
@current_modification_time = File.new(@the_path).mtime
|
99
|
+
@current_modification_time.should == @last_modification_time
|
100
|
+
end
|
101
|
+
|
102
|
+
Then /^I should get an user named "(.+?)"$/ do |user_name|
|
103
|
+
@the_records.first.name.should == user_name
|
104
|
+
end
|
data/lib/ygg.rb
CHANGED
@@ -1,5 +1,99 @@
|
|
1
|
-
require "
|
1
|
+
require "yaml"
|
2
|
+
require "metafun/delegator"
|
2
3
|
|
3
|
-
|
4
|
+
# Its time to start over. Bit by bit. Comment everything until
|
5
|
+
# features and specs are supplied
|
4
6
|
|
5
|
-
|
7
|
+
require "ygg/databundle"
|
8
|
+
require "ygg/session"
|
9
|
+
require "ygg/record"
|
10
|
+
require "ygg/exceptions"
|
11
|
+
|
12
|
+
module Ygg
|
13
|
+
def self.reset
|
14
|
+
@@base_dir = "."
|
15
|
+
@@sessions = []
|
16
|
+
end
|
17
|
+
|
18
|
+
reset
|
19
|
+
|
20
|
+
def self.path_to( source )
|
21
|
+
if source.is_a? Symbol
|
22
|
+
return "#{@@base_dir}/#{source}.ygg"
|
23
|
+
else
|
24
|
+
return source
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.transaction( source, &block )
|
29
|
+
if not block and not File.exist?(path_to(source))
|
30
|
+
raise UnavailableResourceException,
|
31
|
+
"No resource was found in #{path_to(source)}"
|
32
|
+
end
|
33
|
+
|
34
|
+
@@sessions << Session.new(path_to(source))
|
35
|
+
|
36
|
+
if block
|
37
|
+
@@within_block = true
|
38
|
+
yield block
|
39
|
+
@@within_block = nil
|
40
|
+
@@sessions.last.shutdown
|
41
|
+
end
|
42
|
+
return @@sessions.last.records
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.ygg(*args, &block)
|
46
|
+
return Ygg if args.empty?
|
47
|
+
|
48
|
+
self.base_dir = args.shift if args.length > 1
|
49
|
+
source = args.shift
|
50
|
+
|
51
|
+
transaction(source, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.add(*resources)
|
55
|
+
raise ArgumentError if resources.empty?
|
56
|
+
|
57
|
+
begin
|
58
|
+
event = events.last
|
59
|
+
resources.each do |resource|
|
60
|
+
event.records << resource
|
61
|
+
end
|
62
|
+
rescue NoInteractionDoneException => e
|
63
|
+
raise UnavailableResourceException, "No resource was loaded, impossible to add the object"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def self.records
|
68
|
+
events.last.records
|
69
|
+
end
|
70
|
+
|
71
|
+
def self.base_dir=(dir)
|
72
|
+
@@base_dir = dir
|
73
|
+
end
|
74
|
+
|
75
|
+
def self.base_dir
|
76
|
+
@@base_dir
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.events
|
80
|
+
raise NoInteractionDoneException, "No events to load in the log." if @@sessions.empty?
|
81
|
+
@@sessions
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.resource(*args)
|
85
|
+
if args.empty?
|
86
|
+
raise OutOfBlockException, "The resource is not available outside an ygg block." unless @@within_block
|
87
|
+
events.last
|
88
|
+
else
|
89
|
+
case args[0]
|
90
|
+
when :path
|
91
|
+
events.last.source
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
extend Metafun::Delegator
|
98
|
+
|
99
|
+
delegate Ygg, :ygg, :records, :add
|
data/lib/ygg/bck/base.rb
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
module Ygg
|
2
|
+
DEFAULT_SOURCE = "your_tree.ygg"
|
3
|
+
|
4
|
+
class Base
|
5
|
+
@@source = Ygg::DEFAULT_SOURCE
|
6
|
+
@@data = {}
|
7
|
+
|
8
|
+
def self.load(source, &block)
|
9
|
+
Base.assign_source source if source
|
10
|
+
Base.assign_data if File.exists? @@source
|
11
|
+
|
12
|
+
if block; yield; self.save; self.close; end
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.assign_source(source)
|
16
|
+
if source.is_a? Symbol then @@source = "#{source}.ygg"
|
17
|
+
else @@source = source
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.assign_data
|
21
|
+
@@data = YAML.load_file @@source
|
22
|
+
|
23
|
+
# Clarify!
|
24
|
+
@@data.each do |key, model|
|
25
|
+
model.each do |resource|
|
26
|
+
resource.loading! if resource.respond_to? :loading!
|
27
|
+
end if model.kind_of? Array
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.save
|
32
|
+
Base.finalize
|
33
|
+
|
34
|
+
Base.write
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.finalize
|
38
|
+
@@data.each do |key, model|
|
39
|
+
model.each do |resource|
|
40
|
+
resource._finalize! if resource.respond_to? :_finalize!
|
41
|
+
end if model.kind_of? Array
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
def self.data
|
46
|
+
@@data
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.close
|
50
|
+
@@data = nil
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.write
|
54
|
+
File.open @@source, "w" do |file|
|
55
|
+
file.write YAML.dump(@@data)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def self.ygg(source = nil, &block)
|
61
|
+
Base.load source, block
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Ygg
|
2
|
+
class Branch < Array
|
3
|
+
def method_missing(x, *args)
|
4
|
+
if matches = x.to_s.match(/^sort_by_(.+)$/)
|
5
|
+
key = matches[1].to_sym
|
6
|
+
self.sort_by { |x| x.send(key) }
|
7
|
+
|
8
|
+
elsif matches = x.to_s.match(/^by_(.+)$/) # Extend with regexps
|
9
|
+
key = matches[1].to_sym
|
10
|
+
|
11
|
+
if to_find = args.shift
|
12
|
+
result = Branch.new
|
13
|
+
self.each do |res|
|
14
|
+
if res.respond_to? key
|
15
|
+
datus = res.send key
|
16
|
+
result << res if datus == to_find
|
17
|
+
end
|
18
|
+
end
|
19
|
+
return Branch.new if result.length == 0
|
20
|
+
return result[0] if result.length == 1
|
21
|
+
return result
|
22
|
+
|
23
|
+
else
|
24
|
+
self.send(:"sort_by_#{key}")[0] unless empty?
|
25
|
+
end
|
26
|
+
|
27
|
+
return Branch.new
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|