setsumei 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/README.md +4 -0
- data/Rakefile +2 -0
- data/lib/setsumei.rb +2 -0
- data/lib/setsumei/build.rb +21 -0
- data/lib/setsumei/build/key.rb +28 -0
- data/lib/setsumei/describable.rb +80 -0
- data/lib/setsumei/describable/boolean_attribute.rb +34 -0
- data/lib/setsumei/describable/collection.rb +20 -0
- data/lib/setsumei/describable/float_attribute.rb +34 -0
- data/lib/setsumei/describable/int_attribute.rb +33 -0
- data/lib/setsumei/describable/object_attribute.rb +41 -0
- data/lib/setsumei/describable/string_attribute.rb +33 -0
- data/lib/setsumei/version.rb +3 -0
- data/setsumei.gemspec +24 -0
- data/spec/setsumei/build/key_spec.rb +88 -0
- data/spec/setsumei/build_spec.rb +44 -0
- data/spec/setsumei/describable/boolean_attribute_spec.rb +88 -0
- data/spec/setsumei/describable/collection_spec.rb +108 -0
- data/spec/setsumei/describable/float_attribute_spec.rb +86 -0
- data/spec/setsumei/describable/int_attribute_spec.rb +87 -0
- data/spec/setsumei/describable/object_attribute_spec.rb +105 -0
- data/spec/setsumei/describable/string_attribute_spec.rb +92 -0
- data/spec/setsumei/describable_spec.rb +132 -0
- data/spec/spec_helper.rb +4 -0
- data/spec/support/be_an_attribute_of_type_matcher.rb +5 -0
- data/spec/support/shared_examples/attribute_type_creation.rb +13 -0
- metadata +139 -0
data/.rspec
ADDED
data/Gemfile
ADDED
data/README.md
ADDED
data/Rakefile
ADDED
data/lib/setsumei.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'setsumei/build/key'
|
2
|
+
|
3
|
+
module Setsumei
|
4
|
+
module Build
|
5
|
+
def Build.a(klass,options = {})
|
6
|
+
inform_developer "#{klass} should be able to list its attributes" unless klass.respond_to? :defined_attributes
|
7
|
+
inform_developer "wrong number of arguments, options must include from: data }" unless options.keys.include? :from
|
8
|
+
|
9
|
+
klass.new.tap do |object|
|
10
|
+
klass.defined_attributes.each do |_,attribute|
|
11
|
+
attribute.set_value_on object, from_value_in: options[:from]
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def self.inform_developer(message)
|
18
|
+
raise ArgumentError.new message
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Build
|
3
|
+
class Key
|
4
|
+
def Key.for(name,options = { given: [name] } )
|
5
|
+
possible_keys = options[:given]
|
6
|
+
lower_camel_case(name,possible_keys) || at_symbol_case(name,possible_keys) || upper_camel_case(name,possible_keys)
|
7
|
+
end
|
8
|
+
def Key.lower_camel_case name, keys = nil
|
9
|
+
lower_camel_case = name.to_s.gsub(/_[a-zA-Z]/) { |a| a[1].upcase }
|
10
|
+
return_if lower_camel_case, in: keys
|
11
|
+
end
|
12
|
+
def Key.at_symbol_case name, keys = nil
|
13
|
+
at_symbol_case = "@" + lower_camel_case(name)
|
14
|
+
return_if at_symbol_case, in: keys
|
15
|
+
end
|
16
|
+
def Key.upper_camel_case name, keys = nil
|
17
|
+
lower_camel_case = self.lower_camel_case(name)
|
18
|
+
upper_camel_case = lower_camel_case[0].upcase + lower_camel_case[1..-1]
|
19
|
+
return_if upper_camel_case, in: keys
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def self.return_if value, options = { in: [] }
|
24
|
+
(options[:in] || [value]).select { |possible| possible == value }.first
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'setsumei/describable/boolean_attribute'
|
2
|
+
require 'setsumei/describable/string_attribute'
|
3
|
+
require 'setsumei/describable/float_attribute'
|
4
|
+
require 'setsumei/describable/int_attribute'
|
5
|
+
require 'setsumei/describable/object_attribute'
|
6
|
+
require 'setsumei/describable/collection'
|
7
|
+
|
8
|
+
module Setsumei
|
9
|
+
module Describable
|
10
|
+
|
11
|
+
def Describable.included(other)
|
12
|
+
other.extend DescriptionMethods
|
13
|
+
end
|
14
|
+
|
15
|
+
module DescriptionMethods
|
16
|
+
|
17
|
+
def defined_attributes
|
18
|
+
_defined_attributes.dup
|
19
|
+
end
|
20
|
+
|
21
|
+
def define field_name, options = {}
|
22
|
+
_defined_attributes[field_name] = attribute_type(options[:as_a]).named field_name, options
|
23
|
+
attr_accessor field_name
|
24
|
+
end
|
25
|
+
|
26
|
+
def collection_of klass, options = {}
|
27
|
+
self.class_eval do
|
28
|
+
include Enumerable
|
29
|
+
include Comparable
|
30
|
+
|
31
|
+
def each(*args,&block)
|
32
|
+
_internal_collection.each(*args,&block)
|
33
|
+
end
|
34
|
+
|
35
|
+
def include? value
|
36
|
+
_internal_collection.include? value
|
37
|
+
end
|
38
|
+
|
39
|
+
def <<(value)
|
40
|
+
_internal_collection << value
|
41
|
+
end
|
42
|
+
|
43
|
+
def [](value)
|
44
|
+
_internal_collection[value]
|
45
|
+
end
|
46
|
+
|
47
|
+
def []=(index,value)
|
48
|
+
_internal_collection[index] = value
|
49
|
+
end
|
50
|
+
|
51
|
+
def <=>(other)
|
52
|
+
_internal_collection <=> other || super
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
def _internal_collection
|
57
|
+
@_internal_collection ||= []
|
58
|
+
end
|
59
|
+
end
|
60
|
+
_defined_attributes[:_self] = Collection.of klass,options
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def _defined_attributes
|
65
|
+
(@_defined_attributes ||= {})
|
66
|
+
end
|
67
|
+
def attribute_type(type)
|
68
|
+
case type
|
69
|
+
when :boolean then BooleanAttribute
|
70
|
+
when :string then StringAttribute
|
71
|
+
when nil then StringAttribute
|
72
|
+
when :float then FloatAttribute
|
73
|
+
when :int then IntAttribute
|
74
|
+
else
|
75
|
+
ObjectAttribute if type.is_a?(Class)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class BooleanAttribute
|
4
|
+
|
5
|
+
def BooleanAttribute.named(name, options = {})
|
6
|
+
new.tap do |attribute|
|
7
|
+
attribute.name = name
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
def is_an_attribute_of_type?(type)
|
14
|
+
type == :boolean || type == self.class
|
15
|
+
end
|
16
|
+
|
17
|
+
def value_for(pre_type_cast_value)
|
18
|
+
pre_type_cast_value.to_s.downcase == "true" || pre_type_cast_value.to_s == "1"
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_value_on(object, options)
|
22
|
+
object.send accessor, value_from_hash(options[:from_value_in])
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def accessor
|
27
|
+
:"#{name}="
|
28
|
+
end
|
29
|
+
def value_from_hash(hash)
|
30
|
+
value_for hash[ Build::Key.for name, given: hash.keys ]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class Collection
|
4
|
+
def Collection.of(klass,options = {})
|
5
|
+
new.tap do |collection|
|
6
|
+
collection.klass = klass
|
7
|
+
collection.options = options
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :klass, :options
|
12
|
+
|
13
|
+
def set_value_on(object, options = {})
|
14
|
+
[options[:from_value_in][Build::Key.for((self.options[:within] || klass), given: options[:from_value_in].keys)]].flatten(1).each do |data|
|
15
|
+
object << Build.a(klass, from: data)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class FloatAttribute
|
4
|
+
|
5
|
+
def FloatAttribute.named(name, options = {})
|
6
|
+
new.tap do |attribute|
|
7
|
+
attribute.name = name
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :name
|
12
|
+
|
13
|
+
def is_an_attribute_of_type?(type)
|
14
|
+
type == :float || type == self.class
|
15
|
+
end
|
16
|
+
|
17
|
+
def value_for(pre_type_cast_value)
|
18
|
+
pre_type_cast_value.to_f
|
19
|
+
end
|
20
|
+
|
21
|
+
def set_value_on(object, options)
|
22
|
+
object.send accessor, value_from_hash(options[:from_value_in])
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
def accessor
|
27
|
+
:"#{name}="
|
28
|
+
end
|
29
|
+
def value_from_hash(hash)
|
30
|
+
value_for hash[ Build::Key.for name, given: hash.keys ]
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class IntAttribute
|
4
|
+
def IntAttribute.named(name, options = {})
|
5
|
+
new.tap do |attribute|
|
6
|
+
attribute.name = name
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
def is_an_attribute_of_type?(type)
|
13
|
+
type == :int || type == self.class
|
14
|
+
end
|
15
|
+
|
16
|
+
def value_for(pre_type_cast_value)
|
17
|
+
pre_type_cast_value.to_f.round
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_value_on(object, options)
|
21
|
+
object.send accessor, value_from_hash(options[:from_value_in])
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def accessor
|
26
|
+
:"#{name}="
|
27
|
+
end
|
28
|
+
def value_from_hash(hash)
|
29
|
+
value_for hash[ Build::Key.for name, given: hash.keys ]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class ObjectAttribute
|
4
|
+
|
5
|
+
def ObjectAttribute.named(name,options = {})
|
6
|
+
raise ArgumentError.new("you must specify what the object is") unless options.has_key? :as_a
|
7
|
+
new.tap do |attribute|
|
8
|
+
attribute.name = name
|
9
|
+
attribute.klass = options[:as_a]
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
attr_accessor :name, :klass
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
self.klass = Object
|
17
|
+
end
|
18
|
+
|
19
|
+
def value_for(data)
|
20
|
+
return nil if data.nil? || data.empty?
|
21
|
+
Build.a self.klass, from: data
|
22
|
+
end
|
23
|
+
|
24
|
+
def is_an_attribute_of_type?(type)
|
25
|
+
type == :object || type == self.class || type == self.klass
|
26
|
+
end
|
27
|
+
|
28
|
+
def set_value_on(object, options)
|
29
|
+
object.send accessor, value_from_hash(options[:from_value_in])
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
def accessor
|
34
|
+
:"#{name}="
|
35
|
+
end
|
36
|
+
def value_from_hash(hash)
|
37
|
+
value_for hash[ Build::Key.for name, given: hash.keys ]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Setsumei
|
2
|
+
module Describable
|
3
|
+
class StringAttribute
|
4
|
+
def StringAttribute.named(name,options = {})
|
5
|
+
new.tap do |attribute|
|
6
|
+
attribute.name = name
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
attr_accessor :name
|
11
|
+
|
12
|
+
def is_an_attribute_of_type?(type)
|
13
|
+
type == :string || type == self.class
|
14
|
+
end
|
15
|
+
|
16
|
+
def value_for(pre_type_cast_value)
|
17
|
+
pre_type_cast_value.to_s
|
18
|
+
end
|
19
|
+
|
20
|
+
def set_value_on(object, options)
|
21
|
+
object.send accessor, value_from_hash(options[:from_value_in])
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def accessor
|
26
|
+
:"#{name}="
|
27
|
+
end
|
28
|
+
def value_from_hash(hash)
|
29
|
+
value_for hash[ Build::Key.for name, given: hash.keys ]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
data/setsumei.gemspec
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "setsumei/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "setsumei"
|
7
|
+
s.version = Setsumei::VERSION
|
8
|
+
s.platform = Gem::Platform::RUBY
|
9
|
+
s.authors = ["Jon Rowe"]
|
10
|
+
s.email = ["hello@jonrowe.co.uk"]
|
11
|
+
s.homepage = "https://github.com/JonRowe/setsumei"
|
12
|
+
s.summary = %q{A tool for describing API's as ruby objects}
|
13
|
+
s.description = %q{A tool for describing API's as ruby objects. Currently supports building these objects from JSON}
|
14
|
+
|
15
|
+
s.files = `git ls-files`.split("\n")
|
16
|
+
s.test_files = `git ls-files -- {spec,features}/*`.split("\n")
|
17
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
18
|
+
s.require_paths = ["lib"]
|
19
|
+
|
20
|
+
s.add_runtime_dependency 'json'
|
21
|
+
s.add_development_dependency 'rspec'
|
22
|
+
s.add_development_dependency 'autotest-standalone'
|
23
|
+
s.add_development_dependency 'ruby-debug19'
|
24
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module Setsumei
|
4
|
+
module Build
|
5
|
+
describe Key do
|
6
|
+
describe ".for name, given: hash_keys" do
|
7
|
+
let(:hash_keys) { ["otherAttribute","@anotherAttribute","Array","Object"] }
|
8
|
+
|
9
|
+
subject { Key.for :attribute_name, given: hash_keys }
|
10
|
+
|
11
|
+
specify do
|
12
|
+
hash_keys.concat ["AttributeName", "attributeName", "@attributeName"]
|
13
|
+
subject.should == "attributeName"
|
14
|
+
end
|
15
|
+
|
16
|
+
specify do
|
17
|
+
hash_keys.concat ["AttributeName", "@attributeName"]
|
18
|
+
subject.should == "@attributeName"
|
19
|
+
end
|
20
|
+
|
21
|
+
specify do
|
22
|
+
hash_keys.concat ["AttributeName"]
|
23
|
+
subject.should == "AttributeName"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "lower_camel_case(name,keys)" do
|
28
|
+
let(:keys) { [] }
|
29
|
+
|
30
|
+
subject { Key.lower_camel_case :attribute_name, keys }
|
31
|
+
|
32
|
+
it "should return lower camelcase named when keys contain name" do
|
33
|
+
keys << "attributeName"
|
34
|
+
subject.should == "attributeName"
|
35
|
+
end
|
36
|
+
it "should return nil when keys do not contain name" do
|
37
|
+
subject.should be_nil
|
38
|
+
end
|
39
|
+
|
40
|
+
context "keys are ommited" do
|
41
|
+
it "should return lower camelcase name" do
|
42
|
+
Key.lower_camel_case(:attribute_name).should == "attributeName"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe "upper_camel_case(name,keys)" do
|
48
|
+
let(:keys) { [] }
|
49
|
+
|
50
|
+
subject { Key.upper_camel_case :attribute_name, keys }
|
51
|
+
|
52
|
+
it "should return upper camelcase named when keys contain name" do
|
53
|
+
keys << "AttributeName"
|
54
|
+
subject.should == "AttributeName"
|
55
|
+
end
|
56
|
+
it "should return nil when keys do not contain name" do
|
57
|
+
subject.should be_nil
|
58
|
+
end
|
59
|
+
|
60
|
+
context "keys are ommited" do
|
61
|
+
it "should return upper camelcase name" do
|
62
|
+
Key.upper_camel_case(:attribute_name).should == "AttributeName"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "at_symbol_case(name,keys)" do
|
68
|
+
let(:keys) { [] }
|
69
|
+
|
70
|
+
subject { Key.at_symbol_case :attribute_name, keys }
|
71
|
+
|
72
|
+
it "should return symbol case named when keys contain name" do
|
73
|
+
keys << "@attributeName"
|
74
|
+
subject.should == "@attributeName"
|
75
|
+
end
|
76
|
+
it "should return nil when keys do not contain name" do
|
77
|
+
subject.should be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
context "keys are ommited" do
|
81
|
+
it "should return at symbol case" do
|
82
|
+
Key.at_symbol_case(:attribute_name).should == "@attributeName"
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|