cistern 0.0.3 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +1 -0
- data/Gemfile +13 -0
- data/Guardfile +6 -0
- data/README.md +7 -1
- data/Rakefile +4 -1
- data/cistern.gemspec +1 -3
- data/lib/cistern/attributes.rb +137 -167
- data/lib/cistern/collection.rb +13 -47
- data/lib/cistern/formatter.rb +5 -0
- data/lib/cistern/formatters/awesome_print.rb +31 -0
- data/lib/cistern/formatters/default.rb +5 -0
- data/lib/cistern/formatters/formatador.rb +44 -0
- data/lib/cistern/model.rb +10 -12
- data/lib/cistern/version.rb +1 -1
- data/lib/cistern.rb +4 -1
- data/spec/collection_spec.rb +35 -0
- data/spec/model_spec.rb +108 -0
- data/spec/spec_helper.rb +6 -0
- metadata +16 -21
data/.gitignore
CHANGED
data/Gemfile
CHANGED
@@ -2,3 +2,16 @@ source 'https://rubygems.org'
|
|
2
2
|
|
3
3
|
# Specify your gem's dependencies in cistern.gemspec
|
4
4
|
gemspec
|
5
|
+
|
6
|
+
group :test do
|
7
|
+
gem "rspec", "~> 2.0"
|
8
|
+
gem "guard-rspec"
|
9
|
+
gem "rake"
|
10
|
+
gem 'rb-fsevent', '~> 0.9.1'
|
11
|
+
end
|
12
|
+
|
13
|
+
group :formatters do
|
14
|
+
gem 'formatador'
|
15
|
+
gem 'awesome_print'
|
16
|
+
end
|
17
|
+
|
data/Guardfile
ADDED
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# Cistern
|
2
2
|
|
3
|
+
[![Build Status](https://secure.travis-ci.org/lanej/cistern.png)](http://travis-ci.org/lanej/cistern)
|
4
|
+
|
3
5
|
TODO: Write a gem description
|
4
6
|
|
5
7
|
## Installation
|
@@ -20,10 +22,14 @@ Or install it yourself as:
|
|
20
22
|
|
21
23
|
TODO: Write usage instructions here
|
22
24
|
|
25
|
+
## Releasing
|
26
|
+
|
27
|
+
$ gem bump -trv (major|minor|patch)
|
28
|
+
|
23
29
|
## Contributing
|
24
30
|
|
25
31
|
1. Fork it
|
26
32
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
33
|
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
34
|
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
-
|
35
|
+
. Create new Pull Request
|
data/Rakefile
CHANGED
data/cistern.gemspec
CHANGED
@@ -6,7 +6,7 @@ Gem::Specification.new do |gem|
|
|
6
6
|
gem.email = ["me@joshualane.com"]
|
7
7
|
gem.description = %q{API client framework extracted from Fog}
|
8
8
|
gem.summary = %q{API client framework}
|
9
|
-
gem.homepage = ""
|
9
|
+
gem.homepage = "http://joshualane.com/cistern"
|
10
10
|
|
11
11
|
gem.files = `git ls-files`.split($\)
|
12
12
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
@@ -14,6 +14,4 @@ Gem::Specification.new do |gem|
|
|
14
14
|
gem.name = "cistern"
|
15
15
|
gem.require_paths = ["lib"]
|
16
16
|
gem.version = Cistern::VERSION
|
17
|
-
|
18
|
-
gem.add_dependency "formatador"
|
19
17
|
end
|
data/lib/cistern/attributes.rb
CHANGED
@@ -1,203 +1,173 @@
|
|
1
|
-
module Cistern
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
1
|
+
module Cistern::Attributes
|
2
|
+
def self.parsers
|
3
|
+
@parsers ||= {
|
4
|
+
:string => lambda{|v,opts| v.to_s},
|
5
|
+
:time => lambda{|v,opts| v.is_a?(Time) ? v : v && Time.parse(v.to_s)},
|
6
|
+
:integer => lambda{|v,opts| v && v.to_i},
|
7
|
+
:float => lambda{|v,opts| v && v.to_f},
|
8
|
+
:array => lambda{|v,opts| [*v]},
|
9
|
+
:boolean => Proc.new do |v, opts|
|
10
|
+
{
|
11
|
+
true => true,
|
12
|
+
"true" => true,
|
13
|
+
"1" => true,
|
14
|
+
1 => true,
|
15
|
+
false => false,
|
16
|
+
"false" => false,
|
17
|
+
"0" => false,
|
18
|
+
0 => false,
|
19
|
+
}[v]
|
20
|
+
end,
|
21
|
+
}
|
22
|
+
end
|
16
23
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
def #{name}=(new_#{name})
|
27
|
-
attributes[:#{name}] = case new_#{name}
|
28
|
-
when true,'true'
|
29
|
-
true
|
30
|
-
when false,'false'
|
31
|
-
false
|
32
|
-
end
|
33
|
-
end
|
34
|
-
EOS
|
35
|
-
when :float
|
36
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
37
|
-
def #{name}=(new_#{name})
|
38
|
-
attributes[:#{name}] = new_#{name} && new_#{name}.to_f
|
39
|
-
end
|
40
|
-
EOS
|
41
|
-
when :integer
|
42
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
43
|
-
def #{name}=(new_#{name})
|
44
|
-
attributes[:#{name}] = new_#{name} && new_#{name}.to_i
|
45
|
-
end
|
46
|
-
EOS
|
47
|
-
when :string
|
48
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
49
|
-
def #{name}=(new_#{name})
|
50
|
-
attributes[:#{name}] = new_#{name} && new_#{name}.to_s
|
51
|
-
end
|
52
|
-
EOS
|
53
|
-
when :time
|
54
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
55
|
-
def #{name}=(new_#{name})
|
56
|
-
attributes[:#{name}] = if new_#{name}.nil? || new_#{name} == "" || new_#{name}.is_a?(Time)
|
57
|
-
new_#{name}
|
58
|
-
else
|
59
|
-
Time.parse(new_#{name})
|
60
|
-
end
|
61
|
-
end
|
62
|
-
EOS
|
63
|
-
when :array
|
64
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
65
|
-
def #{name}=(new_#{name})
|
66
|
-
attributes[:#{name}] = [*new_#{name}]
|
67
|
-
end
|
68
|
-
EOS
|
69
|
-
else
|
70
|
-
if squash = options[:squash]
|
71
|
-
class_eval <<-EOS, __FILE__, __LINE__
|
72
|
-
def #{name}=(new_data)
|
73
|
-
if new_data.is_a?(::Hash)
|
74
|
-
if new_data.has_key?(:'#{squash}')
|
75
|
-
attributes[:#{name}] = new_data[:'#{squash}']
|
76
|
-
elsif new_data.has_key?("#{squash}")
|
77
|
-
attributes[:#{name}] = new_data["#{squash}"]
|
78
|
-
else
|
79
|
-
attributes[:#{name}] = [ new_data ]
|
80
|
-
end
|
81
|
-
else
|
82
|
-
attributes[:#{name}] = new_data
|
83
|
-
end
|
84
|
-
end
|
85
|
-
EOS
|
24
|
+
def self.transforms
|
25
|
+
@transforms ||= {
|
26
|
+
:squash => Proc.new do |k, v, options|
|
27
|
+
squash = options[:squash]
|
28
|
+
if v.is_a?(::Hash)
|
29
|
+
if v.key?(squash.to_s.to_sym)
|
30
|
+
v[squash.to_s.to_sym]
|
31
|
+
elsif v.has_key?(squash.to_s)
|
32
|
+
v[squash.to_s]
|
86
33
|
else
|
87
|
-
|
88
|
-
def #{name}=(new_#{name})
|
89
|
-
attributes[:#{name}] = new_#{name}
|
90
|
-
end
|
91
|
-
EOS
|
34
|
+
v
|
92
35
|
end
|
36
|
+
else v
|
93
37
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
end
|
99
|
-
end
|
38
|
+
end,
|
39
|
+
:none => lambda{|k, v, opts| v},
|
40
|
+
}
|
41
|
+
end
|
100
42
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
end
|
43
|
+
def self.default_parser
|
44
|
+
@default_parser ||= lambda{|v, opts| v}
|
45
|
+
end
|
105
46
|
|
106
|
-
|
107
|
-
|
108
|
-
|
47
|
+
module ClassMethods
|
48
|
+
def _load(marshalled)
|
49
|
+
new(Marshal.load(marshalled))
|
50
|
+
end
|
109
51
|
|
110
|
-
|
111
|
-
|
112
|
-
|
52
|
+
def aliases
|
53
|
+
@aliases ||= {}
|
54
|
+
end
|
113
55
|
|
56
|
+
def attributes
|
57
|
+
@attributes ||= []
|
114
58
|
end
|
115
59
|
|
116
|
-
|
60
|
+
def attribute(name, options = {})
|
61
|
+
parser = Cistern::Attributes.parsers[options[:type]] ||
|
62
|
+
options[:parser] ||
|
63
|
+
Cistern::Attributes.default_parser
|
64
|
+
transform = Cistern::Attributes.transforms[options[:squash] ? :squash : :none] ||
|
65
|
+
Cistern::Attributes.default_transform
|
117
66
|
|
118
|
-
|
119
|
-
|
67
|
+
self.send(:define_method, name) do
|
68
|
+
attributes[name.to_s.to_sym]
|
120
69
|
end
|
121
70
|
|
122
|
-
|
123
|
-
|
71
|
+
self.send(:define_method, "#{name}=") do |value|
|
72
|
+
transformed = transform.call(name, value, options)
|
73
|
+
attributes[name.to_s.to_sym]= parser.call(transformed, options)
|
124
74
|
end
|
125
75
|
|
126
|
-
|
127
|
-
|
128
|
-
copy.dup_attributes!
|
129
|
-
copy
|
130
|
-
end
|
76
|
+
@attributes ||= []
|
77
|
+
@attributes |= [name]
|
131
78
|
|
132
|
-
|
133
|
-
|
79
|
+
for new_alias in [*options[:aliases]]
|
80
|
+
aliases[new_alias] = name
|
134
81
|
end
|
82
|
+
end
|
135
83
|
|
136
|
-
|
137
|
-
|
138
|
-
|
84
|
+
def identity(name, options = {})
|
85
|
+
@identity = name
|
86
|
+
self.attribute(name, options)
|
87
|
+
end
|
139
88
|
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
if aliased_key = self.class.aliases[key]
|
144
|
-
send("#{aliased_key}=", value)
|
145
|
-
elsif self.respond_to?("#{key}=",true)
|
146
|
-
send("#{key}=", value)
|
147
|
-
else
|
148
|
-
attributes[key] = value
|
149
|
-
end
|
150
|
-
end
|
151
|
-
end
|
152
|
-
self
|
153
|
-
end
|
89
|
+
def ignore_attributes(*args)
|
90
|
+
@ignored_attributes = args
|
91
|
+
end
|
154
92
|
|
155
|
-
|
156
|
-
|
157
|
-
|
93
|
+
def ignored_attributes
|
94
|
+
@ignored_attributes ||= []
|
95
|
+
end
|
96
|
+
end
|
158
97
|
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
raise(ArgumentError, "#{missing.first} is required for this operation")
|
164
|
-
elsif missing.any?
|
165
|
-
raise(ArgumentError, "#{missing[0...-1].join(", ")} and #{missing[-1]} are required for this operation")
|
166
|
-
end
|
167
|
-
end
|
98
|
+
module InstanceMethods
|
99
|
+
def _dump(level)
|
100
|
+
Marshal.dump(attributes)
|
101
|
+
end
|
168
102
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
raise(ArgumentError, "#{missing[0...-1].join(", ")} or #{missing[-1]} are required for this operation")
|
173
|
-
end
|
174
|
-
end
|
103
|
+
def attributes
|
104
|
+
@attributes ||= {}
|
105
|
+
end
|
175
106
|
|
176
|
-
|
107
|
+
def attributes=(attributes)
|
108
|
+
@attributes = attributes
|
109
|
+
end
|
110
|
+
|
111
|
+
def dup
|
112
|
+
copy = super
|
113
|
+
copy.attributes= copy.attributes.dup
|
114
|
+
copy
|
115
|
+
end
|
177
116
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
117
|
+
def identity
|
118
|
+
send(self.class.instance_variable_get('@identity'))
|
119
|
+
end
|
120
|
+
|
121
|
+
def identity=(new_identity)
|
122
|
+
send("#{self.class.instance_variable_get('@identity')}=", new_identity)
|
123
|
+
end
|
124
|
+
|
125
|
+
def merge_attributes(new_attributes = {})
|
126
|
+
for key, value in new_attributes
|
127
|
+
unless self.class.ignored_attributes.include?(key)
|
128
|
+
if aliased_key = self.class.aliases[key]
|
129
|
+
send("#{aliased_key}=", value)
|
130
|
+
elsif self.respond_to?("#{key}=", true)
|
131
|
+
send("#{key}=", value)
|
132
|
+
else
|
133
|
+
attributes[key] = value
|
183
134
|
end
|
184
135
|
end
|
185
|
-
missing
|
186
136
|
end
|
137
|
+
self
|
138
|
+
end
|
187
139
|
|
188
|
-
|
189
|
-
|
140
|
+
def new_record?
|
141
|
+
!identity
|
142
|
+
end
|
143
|
+
|
144
|
+
# check that the attributes specified in args exist and is not nil
|
145
|
+
def requires(*args)
|
146
|
+
missing = missing_attributes(args)
|
147
|
+
if missing.length == 1
|
148
|
+
raise(ArgumentError, "#{missing.first} is required for this operation")
|
149
|
+
elsif missing.any?
|
150
|
+
raise(ArgumentError, "#{missing[0...-1].join(", ")} and #{missing[-1]} are required for this operation")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def requires_one(*args)
|
155
|
+
missing = missing_attributes(args)
|
156
|
+
if missing.length == args.length
|
157
|
+
raise(ArgumentError, "#{missing[0...-1].join(", ")} or #{missing[-1]} are required for this operation")
|
190
158
|
end
|
159
|
+
end
|
191
160
|
|
192
|
-
|
161
|
+
protected
|
193
162
|
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
163
|
+
def missing_attributes(args)
|
164
|
+
missing = []
|
165
|
+
for arg in [:connection] | args
|
166
|
+
unless send("#{arg}") || attributes.has_key?(arg)
|
167
|
+
missing << arg
|
199
168
|
end
|
200
169
|
end
|
170
|
+
missing
|
201
171
|
end
|
202
172
|
end
|
203
173
|
end
|
data/lib/cistern/collection.rb
CHANGED
@@ -2,29 +2,23 @@ class Cistern::Collection < Array
|
|
2
2
|
extend Cistern::Attributes::ClassMethods
|
3
3
|
include Cistern::Attributes::InstanceMethods
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
super
|
13
|
-
end
|
14
|
-
EOS
|
5
|
+
%w[reject select slice].each do |method|
|
6
|
+
define_method(method) do |*args, &block|
|
7
|
+
unless @loaded
|
8
|
+
lazy_load
|
9
|
+
end
|
10
|
+
data = super(*args, &block)
|
11
|
+
self.clone.clear.concat(data)
|
15
12
|
end
|
16
13
|
end
|
17
14
|
|
18
|
-
%w[
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
lazy_load
|
23
|
-
end
|
24
|
-
data = super
|
25
|
-
self.clone.clear.concat(data)
|
15
|
+
%w[first last].each do |method|
|
16
|
+
define_method(method) do
|
17
|
+
unless @loaded
|
18
|
+
lazy_load
|
26
19
|
end
|
27
|
-
|
20
|
+
super()
|
21
|
+
end
|
28
22
|
end
|
29
23
|
|
30
24
|
def self.model(new_model=nil)
|
@@ -80,40 +74,12 @@ class Cistern::Collection < Array
|
|
80
74
|
self
|
81
75
|
end
|
82
76
|
|
83
|
-
def inspect
|
84
|
-
Thread.current[:formatador] ||= Formatador.new
|
85
|
-
data = "#{Thread.current[:formatador].indentation}<#{self.class.name}\n"
|
86
|
-
Thread.current[:formatador].indent do
|
87
|
-
unless self.class.attributes.empty?
|
88
|
-
data << "#{Thread.current[:formatador].indentation}"
|
89
|
-
data << self.class.attributes.map {|attribute| "#{attribute}=#{send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
|
90
|
-
data << "\n"
|
91
|
-
end
|
92
|
-
data << "#{Thread.current[:formatador].indentation}["
|
93
|
-
unless self.empty?
|
94
|
-
data << "\n"
|
95
|
-
Thread.current[:formatador].indent do
|
96
|
-
data << self.map {|member| member.inspect}.join(",\n")
|
97
|
-
data << "\n"
|
98
|
-
end
|
99
|
-
data << Thread.current[:formatador].indentation
|
100
|
-
end
|
101
|
-
data << "]\n"
|
102
|
-
end
|
103
|
-
data << "#{Thread.current[:formatador].indentation}>"
|
104
|
-
data
|
105
|
-
end
|
106
|
-
|
107
77
|
def reload
|
108
78
|
clear
|
109
79
|
lazy_load
|
110
80
|
self
|
111
81
|
end
|
112
82
|
|
113
|
-
def table(attributes = nil)
|
114
|
-
Formatador.display_table(self.map {|instance| instance.attributes}, attributes)
|
115
|
-
end
|
116
|
-
|
117
83
|
private
|
118
84
|
|
119
85
|
def lazy_load
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
module Cistern::Formatter::AwesomePrint
|
4
|
+
def self.call(model)
|
5
|
+
model.ai
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
module AwesomePrint::Cistern
|
10
|
+
def self.included(base)
|
11
|
+
base.send :alias_method, :cast_without_cistern, :cast
|
12
|
+
base.send :alias_method, :cast, :cast_with_cistern
|
13
|
+
end
|
14
|
+
|
15
|
+
def cast_with_cistern(object, type)
|
16
|
+
cast = cast_without_cistern(object, type)
|
17
|
+
if object.is_a?(Cistern::Model)
|
18
|
+
cast = :cistern_model
|
19
|
+
end
|
20
|
+
cast
|
21
|
+
end
|
22
|
+
|
23
|
+
# Format Cistern::Model
|
24
|
+
#------------------------------------------------------------------------------
|
25
|
+
def awesome_cistern_model(object)
|
26
|
+
data = object.attributes.keys.inject({}){|r,k| r.merge(k => object.send(k))}
|
27
|
+
"#{object} " << awesome_hash(data)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
AwesomePrint::Formatter.send(:include, AwesomePrint::Cistern)
|
@@ -0,0 +1,44 @@
|
|
1
|
+
require 'formatador'
|
2
|
+
|
3
|
+
module Cistern::Formatter::Formatador
|
4
|
+
def self.call(model)
|
5
|
+
Thread.current[:formatador] ||= Formatador.new
|
6
|
+
data = "#{Thread.current[:formatador].indentation}<#{model.class.name}"
|
7
|
+
Thread.current[:formatador].indent do
|
8
|
+
unless model.class.attributes.empty?
|
9
|
+
data << "\n#{Thread.current[:formatador].indentation}"
|
10
|
+
data << model.class.attributes.map {|attribute| "#{attribute}=#{model.send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
data << "\n#{Thread.current[:formatador].indentation}>"
|
14
|
+
data
|
15
|
+
end
|
16
|
+
|
17
|
+
def inspect
|
18
|
+
Thread.current[:formatador] ||= Formatador.new
|
19
|
+
data = "#{Thread.current[:formatador].indentation}<#{self.class.name}\n"
|
20
|
+
Thread.current[:formatador].indent do
|
21
|
+
unless self.class.attributes.empty?
|
22
|
+
data << "#{Thread.current[:formatador].indentation}"
|
23
|
+
data << self.class.attributes.map {|attribute| "#{attribute}=#{send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
|
24
|
+
data << "\n"
|
25
|
+
end
|
26
|
+
data << "#{Thread.current[:formatador].indentation}["
|
27
|
+
unless self.empty?
|
28
|
+
data << "\n"
|
29
|
+
Thread.current[:formatador].indent do
|
30
|
+
data << self.map {|member| member.inspect}.join(",\n")
|
31
|
+
data << "\n"
|
32
|
+
end
|
33
|
+
data << Thread.current[:formatador].indentation
|
34
|
+
end
|
35
|
+
data << "]\n"
|
36
|
+
end
|
37
|
+
data << "#{Thread.current[:formatador].indentation}>"
|
38
|
+
data
|
39
|
+
end
|
40
|
+
|
41
|
+
def table(attributes = nil)
|
42
|
+
Formatador.display_table(self.map {|instance| instance.attributes}, attributes)
|
43
|
+
end
|
44
|
+
end
|
data/lib/cistern/model.rb
CHANGED
@@ -4,23 +4,21 @@ class Cistern::Model
|
|
4
4
|
|
5
5
|
attr_accessor :collection, :connection
|
6
6
|
|
7
|
-
def
|
8
|
-
|
7
|
+
def self.formatter
|
8
|
+
@formatter ||= Cistern::Formatter::Default
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.formatter=(formatter)
|
12
|
+
@formatter = formatter
|
9
13
|
end
|
10
14
|
|
11
15
|
def inspect
|
12
|
-
|
13
|
-
data = "#{Thread.current[:formatador].indentation}<#{self.class.name}"
|
14
|
-
Thread.current[:formatador].indent do
|
15
|
-
unless self.class.attributes.empty?
|
16
|
-
data << "\n#{Thread.current[:formatador].indentation}"
|
17
|
-
data << self.class.attributes.map {|attribute| "#{attribute}=#{send(attribute).inspect}"}.join(",\n#{Thread.current[:formatador].indentation}")
|
18
|
-
end
|
19
|
-
end
|
20
|
-
data << "\n#{Thread.current[:formatador].indentation}>"
|
21
|
-
data
|
16
|
+
self.class.formatter.call(self)
|
22
17
|
end
|
23
18
|
|
19
|
+
def initialize(attributes={})
|
20
|
+
merge_attributes(attributes)
|
21
|
+
end
|
24
22
|
|
25
23
|
def save
|
26
24
|
raise NotImplementedError
|
data/lib/cistern/version.rb
CHANGED
data/lib/cistern.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'cistern/version'
|
2
|
-
require '
|
2
|
+
require 'time'
|
3
3
|
|
4
4
|
module Cistern
|
5
5
|
Error = Class.new(StandardError)
|
@@ -12,6 +12,9 @@ module Cistern
|
|
12
12
|
require 'cistern/model'
|
13
13
|
require 'cistern/service'
|
14
14
|
|
15
|
+
autoload :Formatter, 'cistern/formatter'
|
16
|
+
|
17
|
+
|
15
18
|
def self.timeout=(timeout)
|
16
19
|
@timeout= timeout
|
17
20
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Cistern::Collection" do
|
4
|
+
class SampleCollectionModel < Cistern::Model
|
5
|
+
identity :id
|
6
|
+
attribute :name
|
7
|
+
end
|
8
|
+
class SampleCollection < Cistern::Collection
|
9
|
+
model SampleCollectionModel
|
10
|
+
|
11
|
+
def all
|
12
|
+
self.load([{id: 1}, {id: 3, name: "tom"}, {id: 2}])
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should give first" do
|
17
|
+
SampleCollection.new.first.should == SampleCollectionModel.new(id: 1)
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should give last" do
|
21
|
+
SampleCollection.new.last.should == SampleCollectionModel.new(id: 2)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should reject" do
|
25
|
+
SampleCollection.new.reject{|m| m.id == 2}.should == [SampleCollectionModel.new(id: 1), SampleCollectionModel.new(id: 3)]
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should select" do
|
29
|
+
SampleCollection.new.select{|m| m.id == 2}.should == [SampleCollectionModel.new(id: 2)]
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should slice" do
|
33
|
+
SampleCollection.new.slice(0,2).should == [SampleCollectionModel.new(id: 1), SampleCollectionModel.new(id: 3, name: "tom")]
|
34
|
+
end
|
35
|
+
end
|
data/spec/model_spec.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Cistern::Model" do
|
4
|
+
|
5
|
+
it "should duplicate a model" do
|
6
|
+
class DupSpec < Cistern::Model
|
7
|
+
identity :id
|
8
|
+
attribute :name
|
9
|
+
attribute :properties
|
10
|
+
end
|
11
|
+
|
12
|
+
model = DupSpec.new(id: 1, name: "string", properties: {value: "something", else: "what"})
|
13
|
+
duplicate = model.dup
|
14
|
+
|
15
|
+
duplicate.should == model
|
16
|
+
duplicate.should_not eql model
|
17
|
+
|
18
|
+
model.name= "anotherstring"
|
19
|
+
duplicate.name.should == "string"
|
20
|
+
end
|
21
|
+
|
22
|
+
context "attribute parsing" do
|
23
|
+
class TypeSpec < Cistern::Model
|
24
|
+
identity :id
|
25
|
+
attribute :name, type: :string
|
26
|
+
attribute :created_at, type: :time
|
27
|
+
attribute :flag, type: :boolean
|
28
|
+
attribute :list, type: :array
|
29
|
+
attribute :number, type: :integer
|
30
|
+
attribute :floater, type: :float
|
31
|
+
attribute :butternut, type: :integer, aliases: "squash", squash: "id"
|
32
|
+
attribute :custom, parser: lambda{|v, opts| "X!#{v}"}
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should parse string" do
|
36
|
+
TypeSpec.new(name: 1).name.should == "1"
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should parse time" do
|
40
|
+
time = Time.now
|
41
|
+
created_at = TypeSpec.new(created_at: time.to_s).created_at
|
42
|
+
created_at.should be_a(Time)
|
43
|
+
created_at.to_i.should == time.to_i
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should parse boolean" do
|
47
|
+
TypeSpec.new(flag: "false").flag.should be_false
|
48
|
+
TypeSpec.new(flag: "true").flag.should be_true
|
49
|
+
TypeSpec.new(flag: false).flag.should be_false
|
50
|
+
TypeSpec.new(flag: true).flag.should be_true
|
51
|
+
TypeSpec.new(flag: "0").flag.should be_false
|
52
|
+
TypeSpec.new(flag: "1").flag.should be_true
|
53
|
+
TypeSpec.new(flag: 0).flag.should be_false
|
54
|
+
TypeSpec.new(flag: 1).flag.should be_true
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should parse an array" do
|
58
|
+
TypeSpec.new(list: []).list.should == []
|
59
|
+
TypeSpec.new(list: "item").list.should == ["item"]
|
60
|
+
end
|
61
|
+
|
62
|
+
it "should parse a float" do
|
63
|
+
TypeSpec.new(floater: "0.01").floater.should == 0.01
|
64
|
+
TypeSpec.new(floater: 0.01).floater.should == 0.01
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should use custom parser" do
|
68
|
+
TypeSpec.new(custom: "15").custom.should == "X!15"
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should squash and cast" do
|
72
|
+
TypeSpec.new({"squash" => {"id" => "12"}}).butternut.should == 12
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context "inspection engine" do
|
77
|
+
class InspectorSpec < Cistern::Model
|
78
|
+
identity :id
|
79
|
+
attribute :name
|
80
|
+
end
|
81
|
+
|
82
|
+
after(:all) do
|
83
|
+
InspectorSpec.formatter= Cistern::Formatter::Default
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should default to default formatter" do
|
87
|
+
InspectorSpec.formatter.should == Cistern::Formatter::Default
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should use default" do
|
91
|
+
InspectorSpec.new(id: 1, name: "name").inspect.should match /#<InspectorSpec:0x[0-9a-f]+ attributes={id:1,name:\"name\"}/
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should use awesome_print" do
|
95
|
+
defined?(AwesomePrint).should be_false # don't require if not used
|
96
|
+
InspectorSpec.formatter= Cistern::Formatter::AwesomePrint
|
97
|
+
|
98
|
+
InspectorSpec.new(id: 1, name: "name").inspect.match /(?x-mi:\#<InspectorSpec:0x[0-9a-f]+>\ {\n\ \ \ \ \ \ :id\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[1;34m1\x1B\[0m,\n\ \ \ \ :name\x1B\[0;37m\ =>\ \x1B\[0m\x1B\[0;33m"name"\x1B\[0m\n})/
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should use formatador" do
|
102
|
+
defined?(Formatador).should be_false # don't require if not used
|
103
|
+
InspectorSpec.formatter= Cistern::Formatter::Formatador
|
104
|
+
|
105
|
+
InspectorSpec.new(id: 1, name: "name").inspect.should == " <InspectorSpec\n id=1,\n name=\"name\"\n >"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cistern
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,24 +9,8 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
13
|
-
dependencies:
|
14
|
-
- !ruby/object:Gem::Dependency
|
15
|
-
name: formatador
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
|
-
prerelease: false
|
24
|
-
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
12
|
+
date: 2012-11-30 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
30
14
|
description: API client framework extracted from Fog
|
31
15
|
email:
|
32
16
|
- me@joshualane.com
|
@@ -36,6 +20,7 @@ extra_rdoc_files: []
|
|
36
20
|
files:
|
37
21
|
- .gitignore
|
38
22
|
- Gemfile
|
23
|
+
- Guardfile
|
39
24
|
- LICENSE
|
40
25
|
- README.md
|
41
26
|
- Rakefile
|
@@ -43,13 +28,20 @@ files:
|
|
43
28
|
- lib/cistern.rb
|
44
29
|
- lib/cistern/attributes.rb
|
45
30
|
- lib/cistern/collection.rb
|
31
|
+
- lib/cistern/formatter.rb
|
32
|
+
- lib/cistern/formatters/awesome_print.rb
|
33
|
+
- lib/cistern/formatters/default.rb
|
34
|
+
- lib/cistern/formatters/formatador.rb
|
46
35
|
- lib/cistern/hash.rb
|
47
36
|
- lib/cistern/mock.rb
|
48
37
|
- lib/cistern/model.rb
|
49
38
|
- lib/cistern/service.rb
|
50
39
|
- lib/cistern/version.rb
|
51
40
|
- lib/cistern/wait_for.rb
|
52
|
-
|
41
|
+
- spec/collection_spec.rb
|
42
|
+
- spec/model_spec.rb
|
43
|
+
- spec/spec_helper.rb
|
44
|
+
homepage: http://joshualane.com/cistern
|
53
45
|
licenses: []
|
54
46
|
post_install_message:
|
55
47
|
rdoc_options: []
|
@@ -73,4 +65,7 @@ rubygems_version: 1.8.24
|
|
73
65
|
signing_key:
|
74
66
|
specification_version: 3
|
75
67
|
summary: API client framework
|
76
|
-
test_files:
|
68
|
+
test_files:
|
69
|
+
- spec/collection_spec.rb
|
70
|
+
- spec/model_spec.rb
|
71
|
+
- spec/spec_helper.rb
|