ygg 0.0.2 → 1.0.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.
- 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
|