structure 0.1.0 → 0.2.0
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/.gitignore +0 -1
- data/README.md +57 -20
- data/Rakefile +11 -0
- data/lib/structure.rb +84 -32
- data/lib/structure/json.rb +18 -0
- data/lib/structure/version.rb +3 -0
- data/spec/models/person.rb +5 -0
- data/spec/spec_helper.rb +7 -0
- data/spec/structure/json_spec.rb +31 -0
- data/spec/structure_spec.rb +62 -0
- data/structure.gemspec +19 -4
- metadata +60 -11
- data/test/structure_test.rb +0 -46
data/README.md
CHANGED
@@ -1,25 +1,62 @@
|
|
1
1
|
Structure
|
2
2
|
=========
|
3
3
|
|
4
|
-
Structure is a
|
4
|
+
Structure is a better struct.
|
5
|
+
|
6
|
+
|
7
|
+
#_ d
|
8
|
+
##_ d#
|
9
|
+
NN#p j0NN
|
10
|
+
40NNh_ _gN#B0
|
11
|
+
4JF@NNp_ _g0WNNL@
|
12
|
+
JLE5@WRNp_ _g@NNNF3_L
|
13
|
+
_F`@q4WBN@Np_ _gNN@ZL#p"Fj_
|
14
|
+
"0^#-LJ_9"NNNMp__ _gN#@#"R_#g@q^9"
|
15
|
+
a0,3_j_j_9FN@N@0NMp__ __ggNZNrNM"P_f_f_E,0a
|
16
|
+
j L 6 9""Q"#^q@NDNNNMpg____ ____gggNNW#W4p^p@jF"P"]"j F
|
17
|
+
rNrr4r*pr4r@grNr@q@Ng@q@N0@N#@NNMpmggggmqgNN@NN@#@4p*@M@p4qp@w@m@Mq@r#rq@r
|
18
|
+
F Jp 9__b__M,Juw*w*^#^9#""EED*dP_@EZ@^E@*#EjP"5M"gM@p*Ww&,jL_J__f F j
|
19
|
+
-r#^^0""E" 6 q q__hg-@4""*,_Z*q_"^pwr""p*C__@""0N-qdL_p" p J" 3""5^^0r-
|
20
|
+
t J __,Jb--N""", *_s0M`""q_a@NW__JP^u_p"""p4a,p" _F""V--wL,_F_ F #
|
21
|
+
_,Jp*^#""9 L 5_a*N"""q__INr" "q_e^"*,p^""qME_ y"""p6u,f j' f "N^--LL_
|
22
|
+
L ] k,w@#"""_ "_a*^E ba-" ^qj-""^pe" J^-u_f _f "q@w,j f jL
|
23
|
+
#_,J@^""p `_ _jp-""q _Dw^" ^cj*""*,j^ "p#_ y""^wE_ _F F"^qN,_j
|
24
|
+
w*^0 4 9__sAF" `L _Dr" m__m""q__a^"m__* "qA_ j" ""Au__f J 0^--
|
25
|
+
] J_,x-E 3_ jN^" `u _w^*_ _RR_ _J^w_ j" "pL_ f 7^-L_F #
|
26
|
+
jLs*^6 `_ _&*" q _,NF "wp" "*g" _NL_ p "-d_ F ]"*u_F
|
27
|
+
,x-"F ] Ax^" q hp" `u jM""u a^ ^, j" "*g_ p ^mg_ D.H. 1992
|
28
|
+
|
29
|
+
|
30
|
+
Usage
|
31
|
+
-----
|
32
|
+
|
33
|
+
Structure, like Struct, is great for defining ephemeral models.
|
5
34
|
|
6
35
|
require 'structure'
|
7
|
-
|
8
|
-
|
9
|
-
:
|
10
|
-
:
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
36
|
+
|
37
|
+
class Person < Structure
|
38
|
+
key :name
|
39
|
+
key :age, :type => Integer
|
40
|
+
key :friends, :type => Array
|
41
|
+
end
|
42
|
+
|
43
|
+
john = Person.new(:name => "John",
|
44
|
+
:age => 28)
|
45
|
+
|
46
|
+
jane = Person.new(:name => "Jane",
|
47
|
+
:age => 24)
|
48
|
+
|
49
|
+
john.friends = [jane]
|
50
|
+
|
51
|
+
When it comes to dumping JSON, Structure is more aesthetically-minded.
|
52
|
+
|
53
|
+
require 'structure/json'
|
54
|
+
|
55
|
+
json = john.to_json
|
56
|
+
=> {"json_class":"Person","name":"John","age":28,"friends":[{"json_class":"Person","name":"Jane","age":24,"friends":null}]}
|
57
|
+
|
58
|
+
person = JSON.parse(json)
|
59
|
+
person.friends.first.name
|
60
|
+
=> "Jane"
|
61
|
+
person.friends.first.age
|
62
|
+
=> 24
|
data/Rakefile
CHANGED
@@ -1,2 +1,13 @@
|
|
1
1
|
require 'bundler'
|
2
|
+
require 'cucumber/rake/task'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
|
2
5
|
Bundler::GemHelper.install_tasks
|
6
|
+
|
7
|
+
desc 'Run all specs in spec directory'
|
8
|
+
RSpec::Core::RakeTask.new(:spec) do |t|
|
9
|
+
t.pattern = "spec/**/*_spec.rb"
|
10
|
+
t.rspec_opts = %w(-fd -c)
|
11
|
+
end
|
12
|
+
|
13
|
+
task :default => [:spec]
|
data/lib/structure.rb
CHANGED
@@ -1,44 +1,96 @@
|
|
1
|
-
require '
|
2
|
-
|
3
|
-
#
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
# A better struct.
|
4
|
+
class Structure
|
5
|
+
|
6
|
+
# Mix in the Enumerable module.
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
@@keys = []
|
10
|
+
|
11
|
+
# Defines an attribute key.
|
12
|
+
#
|
13
|
+
# Takes a name and an optional hash of options. Available options are:
|
14
|
+
#
|
15
|
+
# * :type, which can be Integer, Float, String, and Array.
|
16
|
+
#
|
17
|
+
# class Book
|
18
|
+
# key :title, :type => String
|
19
|
+
# key :authors, :type => Array
|
20
|
+
# end
|
21
|
+
#
|
22
|
+
def self.key(name, options={})
|
23
|
+
if method_defined?(name)
|
24
|
+
raise NameError, "#{name} is already defined"
|
12
25
|
end
|
13
|
-
end
|
14
26
|
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
27
|
+
name = name.to_sym
|
28
|
+
type = options[:type]
|
29
|
+
@@keys << name
|
30
|
+
|
31
|
+
module_eval do
|
32
|
+
|
33
|
+
# Define a getter.
|
34
|
+
define_method(name) { @attributes[name] }
|
35
|
+
|
36
|
+
# Define a setter. The setter will optionally typecast.
|
37
|
+
define_method("#{name}=") do |value|
|
38
|
+
modifiable[name] =
|
39
|
+
if type && value
|
40
|
+
Kernel.send(type.to_s, value)
|
41
|
+
else
|
42
|
+
value
|
43
|
+
end
|
21
44
|
end
|
22
|
-
modifiable[new_ostruct_member(mname)] = structure(args[0])
|
23
|
-
elsif len == 0
|
24
|
-
@table[mid]
|
25
|
-
else
|
26
|
-
raise NoMethodError, "undefined method `#{mname}' for #{self}", caller(1)
|
27
45
|
end
|
28
46
|
end
|
29
47
|
|
30
|
-
|
48
|
+
# Creates a new structure.
|
49
|
+
#
|
50
|
+
# Optionally, populates the structure with a hash of attributes. Otherwise,
|
51
|
+
# all values default to nil.
|
52
|
+
def initialize(seed = {})
|
53
|
+
@attributes =
|
54
|
+
@@keys.inject({}) do |attributes, name|
|
55
|
+
attributes[name] = nil
|
56
|
+
attributes
|
57
|
+
end
|
58
|
+
|
59
|
+
seed.each { |key, value| self.send("#{key}=", value) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# A hash of attributes.
|
63
|
+
attr_reader :attributes
|
64
|
+
|
65
|
+
def each(&block)
|
66
|
+
@attributes.each { |value| block.call(value) }
|
67
|
+
end
|
68
|
+
|
69
|
+
# Returns an array populated with the attribute keys.
|
70
|
+
def keys
|
71
|
+
@attributes.keys
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns an array populated with the attribute values.
|
75
|
+
def values
|
76
|
+
@attributes.values
|
77
|
+
end
|
78
|
+
|
79
|
+
# Compares this object with another object for equality. A Structure is equal
|
80
|
+
# to the other object when latter is also a Structure and the two objects'
|
81
|
+
# attributes are equal.
|
82
|
+
def ==(other)
|
83
|
+
other.is_a?(Structure) && @attributes == other.attributes
|
84
|
+
end
|
31
85
|
|
32
86
|
private
|
33
87
|
|
34
|
-
def
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
o.map { |o| structure(o) }
|
40
|
-
else
|
41
|
-
o
|
88
|
+
def modifiable
|
89
|
+
begin
|
90
|
+
@modifiable = true
|
91
|
+
rescue
|
92
|
+
raise TypeError, "can't modify frozen #{self.class}", caller(3)
|
42
93
|
end
|
94
|
+
@attributes
|
43
95
|
end
|
44
96
|
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
unless Object.const_defined?(:JSON) and ::JSON.const_defined?(:JSON_LOADED) and
|
2
|
+
::JSON::JSON_LOADED
|
3
|
+
require 'json'
|
4
|
+
end
|
5
|
+
|
6
|
+
class Structure
|
7
|
+
def self.json_create(object)
|
8
|
+
object.delete('json_class')
|
9
|
+
new(object)
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_json(*args)
|
13
|
+
klass = self.class.name
|
14
|
+
{ JSON.create_id => klass }.
|
15
|
+
merge(@attributes).
|
16
|
+
to_json(*args)
|
17
|
+
end
|
18
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Structure do
|
4
|
+
context "when `structure/json' is required" do
|
5
|
+
let(:person) { Person.new(:name => 'Joe', :age => 28) }
|
6
|
+
let(:json) { '{"json_class":"Person","name":"Joe","age":28,"friends":null}' }
|
7
|
+
|
8
|
+
before do
|
9
|
+
require 'structure/json'
|
10
|
+
end
|
11
|
+
|
12
|
+
it "dumps to JSON" do
|
13
|
+
person.to_json.should eql json
|
14
|
+
end
|
15
|
+
|
16
|
+
it "loads from JSON" do
|
17
|
+
JSON.parse(json).should == person
|
18
|
+
end
|
19
|
+
|
20
|
+
context "when nesting other structures" do
|
21
|
+
before do
|
22
|
+
person.friends = [Person.new(:name => 'Jane')]
|
23
|
+
end
|
24
|
+
|
25
|
+
it "loads nested structures from JSON" do
|
26
|
+
json = person.to_json
|
27
|
+
JSON.parse(json).friends.first.name.should eql 'Jane'
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Structure do
|
4
|
+
let(:person) { Person.new }
|
5
|
+
|
6
|
+
it "is enumerable" do
|
7
|
+
person.name ="Joe"
|
8
|
+
person.map { |key, value| value }.should include "Joe"
|
9
|
+
end
|
10
|
+
|
11
|
+
context "when object is frozen" do
|
12
|
+
before do
|
13
|
+
person.freeze
|
14
|
+
end
|
15
|
+
|
16
|
+
it "raises an error" do
|
17
|
+
expect do
|
18
|
+
person.name = 'Joe'
|
19
|
+
end.to raise_error TypeError
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe ".key" do
|
24
|
+
it "defines accessors" do
|
25
|
+
%w{name name=}.each { |method| person.should respond_to method }
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when name clashes with an existing method" do
|
29
|
+
it "raises an error" do
|
30
|
+
expect do
|
31
|
+
Person.key :name
|
32
|
+
end.to raise_error NameError
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when a type is specified" do
|
37
|
+
context "when setting the attribute to a non-nil value" do
|
38
|
+
it "casts the value" do
|
39
|
+
person.age = "28"
|
40
|
+
person.age.should eql 28
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context "when setting the attribute to nil" do
|
45
|
+
it "does not set the value" do
|
46
|
+
person.age = nil
|
47
|
+
person.age.should be_nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
describe ".new" do
|
54
|
+
context "when attributes are specified" do
|
55
|
+
it "initializes the object with those attributes" do
|
56
|
+
jane = Person.new(:name => 'Jane', :age => "29")
|
57
|
+
jane.name.should eql 'Jane'
|
58
|
+
jane.age.should eql 29
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/structure.gemspec
CHANGED
@@ -1,16 +1,31 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path('../lib', __FILE__)
|
3
|
+
require 'structure/version'
|
4
|
+
|
2
5
|
Gem::Specification.new do |s|
|
3
6
|
s.name = "structure"
|
4
|
-
s.version =
|
7
|
+
s.version = Sucker::VERSION
|
5
8
|
s.platform = Gem::Platform::RUBY
|
6
9
|
s.authors = ["Paper Cavalier"]
|
7
10
|
s.email = ["code@papercavalier.com"]
|
8
|
-
s.homepage = ""
|
9
|
-
s.summary =
|
10
|
-
s.description =
|
11
|
+
s.homepage = "http://rubygems.com/gems/structure"
|
12
|
+
s.summary = "Structure is a better Struct."
|
13
|
+
s.description = <<-END_OF_DESCRIPTION.strip
|
14
|
+
Structure is a better Struct.
|
15
|
+
|
16
|
+
Like Struct, it is great for setting up ephemeral models. It also handles
|
17
|
+
typecasting and, unlike Struct, dumps nicely-formatted JSON.
|
18
|
+
END_OF_DESCRIPTION
|
11
19
|
|
12
20
|
s.rubyforge_project = "structure"
|
13
21
|
|
22
|
+
{
|
23
|
+
'rspec' => '~> 2.6.0',
|
24
|
+
'ruby-debug19' => '~> 0.11.6'
|
25
|
+
}.each do |lib, version|
|
26
|
+
s.add_development_dependency lib, version
|
27
|
+
end
|
28
|
+
|
14
29
|
s.files = `git ls-files`.split("\n")
|
15
30
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
16
31
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,8 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: structure
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
prerelease:
|
5
|
-
|
4
|
+
prerelease: false
|
5
|
+
segments:
|
6
|
+
- 0
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 0.2.0
|
6
10
|
platform: ruby
|
7
11
|
authors:
|
8
12
|
- Paper Cavalier
|
@@ -10,11 +14,44 @@ autorequire:
|
|
10
14
|
bindir: bin
|
11
15
|
cert_chain: []
|
12
16
|
|
13
|
-
date: 2011-
|
17
|
+
date: 2011-05-26 00:00:00 +01:00
|
14
18
|
default_executable:
|
15
|
-
dependencies:
|
16
|
-
|
17
|
-
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
21
|
+
name: rspec
|
22
|
+
prerelease: false
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
24
|
+
none: false
|
25
|
+
requirements:
|
26
|
+
- - ~>
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
segments:
|
29
|
+
- 2
|
30
|
+
- 6
|
31
|
+
- 0
|
32
|
+
version: 2.6.0
|
33
|
+
type: :development
|
34
|
+
version_requirements: *id001
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: ruby-debug19
|
37
|
+
prerelease: false
|
38
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
- 11
|
46
|
+
- 6
|
47
|
+
version: 0.11.6
|
48
|
+
type: :development
|
49
|
+
version_requirements: *id002
|
50
|
+
description: |-
|
51
|
+
Structure is a better Struct.
|
52
|
+
|
53
|
+
Like Struct, it is great for setting up ephemeral models. It also handles
|
54
|
+
typecasting and, unlike Struct, dumps nicely-formatted JSON.
|
18
55
|
email:
|
19
56
|
- code@papercavalier.com
|
20
57
|
executables: []
|
@@ -29,10 +66,15 @@ files:
|
|
29
66
|
- README.md
|
30
67
|
- Rakefile
|
31
68
|
- lib/structure.rb
|
69
|
+
- lib/structure/json.rb
|
70
|
+
- lib/structure/version.rb
|
71
|
+
- spec/models/person.rb
|
72
|
+
- spec/spec_helper.rb
|
73
|
+
- spec/structure/json_spec.rb
|
74
|
+
- spec/structure_spec.rb
|
32
75
|
- structure.gemspec
|
33
|
-
- test/structure_test.rb
|
34
76
|
has_rdoc: true
|
35
|
-
homepage:
|
77
|
+
homepage: http://rubygems.com/gems/structure
|
36
78
|
licenses: []
|
37
79
|
|
38
80
|
post_install_message:
|
@@ -45,19 +87,26 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
45
87
|
requirements:
|
46
88
|
- - ">="
|
47
89
|
- !ruby/object:Gem::Version
|
90
|
+
segments:
|
91
|
+
- 0
|
48
92
|
version: "0"
|
49
93
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
94
|
none: false
|
51
95
|
requirements:
|
52
96
|
- - ">="
|
53
97
|
- !ruby/object:Gem::Version
|
98
|
+
segments:
|
99
|
+
- 0
|
54
100
|
version: "0"
|
55
101
|
requirements: []
|
56
102
|
|
57
103
|
rubyforge_project: structure
|
58
|
-
rubygems_version: 1.
|
104
|
+
rubygems_version: 1.3.7
|
59
105
|
signing_key:
|
60
106
|
specification_version: 3
|
61
|
-
summary: Structure is a
|
107
|
+
summary: Structure is a better Struct.
|
62
108
|
test_files:
|
63
|
-
-
|
109
|
+
- spec/models/person.rb
|
110
|
+
- spec/spec_helper.rb
|
111
|
+
- spec/structure/json_spec.rb
|
112
|
+
- spec/structure_spec.rb
|
data/test/structure_test.rb
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
require 'minitest/spec'
|
2
|
-
require File.expand_path('../../lib/structure', __FILE__)
|
3
|
-
|
4
|
-
MiniTest::Unit.autorun
|
5
|
-
|
6
|
-
describe Structure do
|
7
|
-
before do
|
8
|
-
@hash = {
|
9
|
-
:name => "John",
|
10
|
-
:children => [ { :name => "Jim" } ],
|
11
|
-
:location => { :city => { :name => "London" } }
|
12
|
-
}
|
13
|
-
end
|
14
|
-
|
15
|
-
describe ".new" do
|
16
|
-
before do
|
17
|
-
@person = Structure.new(@hash)
|
18
|
-
end
|
19
|
-
|
20
|
-
it "structures nested hashes" do
|
21
|
-
@person.location.city.name.must_equal "London"
|
22
|
-
end
|
23
|
-
|
24
|
-
it "structures hashes in arrays" do
|
25
|
-
@person.children.must_be_instance_of Array
|
26
|
-
@person.children.first.name.must_equal "Jim"
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
describe "#=" do
|
31
|
-
before do
|
32
|
-
@person = Structure.new
|
33
|
-
end
|
34
|
-
|
35
|
-
it "structures nested hashes" do
|
36
|
-
@person.location = { :city => { :name => "London" } }
|
37
|
-
@person.location.city.name.must_equal "London"
|
38
|
-
end
|
39
|
-
|
40
|
-
it "structures hashes in arrays" do
|
41
|
-
@person.children = [ { :name => "Jim" } ]
|
42
|
-
@person.children.must_be_instance_of Array
|
43
|
-
@person.children.first.name.must_equal "Jim"
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|