morph 0.3.7 → 0.4.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.
- checksums.yaml +4 -4
- data/CHANGELOG +10 -8
- data/LICENSE +14 -11
- data/README.md +295 -0
- data/lib/morph.rb +192 -126
- metadata +23 -48
- data/Manifest +0 -5
- data/README +0 -287
- data/Rakefile +0 -37
- data/morph.gemspec +0 -39
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dba2306af06acc1d857bb2a18f239d9e91a60adb
|
4
|
+
data.tar.gz: e27fce32e999fb4a2ba9909f037ab5e6820b91a8
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 46b5e3356b1ce4dda6b611fe005c2393ffb2f1df0856eb8dc844e4f4623eba96b8dc84e227e6885cabea9e42a6de21ca890e61c9fa33e5837e7133a04041b3e8
|
7
|
+
data.tar.gz: 812b34cc1a12d5985619becf42f8a1ed7bdc6dbe7b80883830cf67149eaf61fa8734023b49b1e34365ff014897476b0b09babe090a5d1a8b84dffa9684d58db8
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
v0.4.0. add from_json() and register_listener() methods
|
2
|
+
|
1
3
|
v0.3.7. don't mix in private methods when Morph is included
|
2
4
|
|
3
5
|
v0.3.6. call to_time() on XMLRPC::DateTime when setting it as an attribute value
|
@@ -6,7 +8,7 @@ v0.3.5. retain morph_methods when inheriting class and parent both include Morph
|
|
6
8
|
|
7
9
|
v0.3.4. return instance morph_attributes ordered by original addition of attribute
|
8
10
|
|
9
|
-
v0.3.3.
|
11
|
+
v0.3.3. remove show_ruby() method; updated script_generate() to utilise new rails commands
|
10
12
|
|
11
13
|
v0.3.2. from_hash() can now handle keys that are symbols
|
12
14
|
|
@@ -14,9 +16,9 @@ v0.3.1. only show fastercsv install message if Morph.from_csv() called
|
|
14
16
|
|
15
17
|
v0.3.0. modifications for ruby 1.9.2 compatibility
|
16
18
|
|
17
|
-
v0.2.9.
|
19
|
+
v0.2.9. add from_csv(); require fastercsv; add morph_attributes() class method
|
18
20
|
|
19
|
-
v0.2.8.
|
21
|
+
v0.2.8. add from_xml() and from_tsv(); updated for active_support; fix from_hash() when hash root is an array
|
20
22
|
|
21
23
|
v0.2.7. handle dash when converting to method name, reported by danwrong
|
22
24
|
|
@@ -26,21 +28,21 @@ v0.2.5. camelize names for classes in from_hash()
|
|
26
28
|
|
27
29
|
v0.2.4. from_hash() creates classes in Morph namespace and handles arrays of hashes
|
28
30
|
|
29
|
-
v0.2.3.
|
31
|
+
v0.2.3. add Morph.from_hash method
|
30
32
|
|
31
|
-
v0.2.2.
|
33
|
+
v0.2.2. change rubygem requirement to be >= 1.2 instead of = 1.2
|
32
34
|
|
33
35
|
v0.2.1. better accomodation of unicode method names
|
34
36
|
|
35
|
-
v0.2.0.
|
37
|
+
v0.2.0. add script_generate method; fix bug occurring when two morph classes exist
|
36
38
|
|
37
|
-
v0.1.5.
|
39
|
+
v0.1.5. add morph_attributes instance method
|
38
40
|
|
39
41
|
v0.1.4. can now pass code block to customize the dynamically added methods
|
40
42
|
|
41
43
|
v0.1.3. can now set hash of attributes via morph method
|
42
44
|
|
43
|
-
v0.1.2.
|
45
|
+
v0.1.2. fix problems in packaging of v0.1.1 gem
|
44
46
|
|
45
47
|
v0.1.1. calling unknown reader method no longer adds accessor methods to class
|
46
48
|
|
data/LICENSE
CHANGED
@@ -1,18 +1,21 @@
|
|
1
|
-
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2016 Rob McKinnon
|
2
4
|
|
3
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
-
of this software and associated documentation files (the "Software"), to
|
5
|
-
|
6
|
-
|
7
|
-
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
10
|
furnished to do so, subject to the following conditions:
|
9
11
|
|
10
|
-
The above copyright notice and this permission notice shall be included in
|
11
|
-
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
12
14
|
|
13
15
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
16
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
16
|
-
|
17
|
-
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
-
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,295 @@
|
|
1
|
+
Morph allows you to emerge Ruby class definitions from data or by calling assignment methods.
|
2
|
+
|
3
|
+
[](https://travis-ci.org/robmckinnon/morph)
|
4
|
+
|
5
|
+
## Installing Morph
|
6
|
+
|
7
|
+
gem install morph
|
8
|
+
|
9
|
+
To use Morph:
|
10
|
+
|
11
|
+
require 'morph'
|
12
|
+
|
13
|
+
Tested to work with Ruby 1.8 - 2.3, JRuby 9, and Rubinius 3.
|
14
|
+
|
15
|
+
## Morph creating classes `from_json`
|
16
|
+
|
17
|
+
Here's an example showing Morph creating classes and objects from JSON:
|
18
|
+
|
19
|
+
json = '{
|
20
|
+
"id": "3599110793",
|
21
|
+
"type": "PushEvent",
|
22
|
+
"actor": {
|
23
|
+
"id": 3447,
|
24
|
+
"login": "robmckinnon",
|
25
|
+
"url": "https://api.github.com/users/robmckinnon"
|
26
|
+
},
|
27
|
+
"repo": {
|
28
|
+
"id": 5092,
|
29
|
+
"name": "robmckinnon/morph",
|
30
|
+
"url": "https://api.github.com/repos/robmckinnon/morph"
|
31
|
+
}
|
32
|
+
}'
|
33
|
+
|
34
|
+
module Github; end
|
35
|
+
|
36
|
+
type = :push_event
|
37
|
+
namespace = Github
|
38
|
+
|
39
|
+
event = Morph.from_json json, type, namespace
|
40
|
+
|
41
|
+
# => <Github::PushEvent @id="3599110793", @type="PushEvent",
|
42
|
+
@actor=#<Github::Actor:0x007faa0c86b790 @id=3447, @login="robmckinnon",
|
43
|
+
@url="https://api.github.com/users/robmckinnon">,
|
44
|
+
@repo=#<Github::Repo:0x007faa0c869198 @id=5092, @name="robmckinnon/morph",
|
45
|
+
@url="https://api.github.com/repos/robmckinnon/morph">
|
46
|
+
>
|
47
|
+
|
48
|
+
event.class
|
49
|
+
|
50
|
+
# => Github::PushEvent
|
51
|
+
|
52
|
+
event.class.morph_attributes
|
53
|
+
|
54
|
+
# => [:id, :type, :actor, :repo]
|
55
|
+
|
56
|
+
event.actor.class
|
57
|
+
|
58
|
+
# => Github::Actor
|
59
|
+
|
60
|
+
event.repo.class
|
61
|
+
|
62
|
+
# => Github::Repo
|
63
|
+
|
64
|
+
If namespace module not provided, new classes are created in Morph module.
|
65
|
+
|
66
|
+
event = Morph.from_json json, type, namespace
|
67
|
+
|
68
|
+
event.class
|
69
|
+
|
70
|
+
# => Morph::PushEvent
|
71
|
+
|
72
|
+
## Morph creating classes `from_csv`
|
73
|
+
|
74
|
+
Here's an example showing Morph playing with CSV (comma-separated values):
|
75
|
+
|
76
|
+
csv = %Q[name,party\nTed Roe,red\nAli Davidson,blue\nSue Smith,green]
|
77
|
+
|
78
|
+
people = Morph.from_csv(csv, 'person')
|
79
|
+
|
80
|
+
# => [#<Morph::Person @name="Ted Roe", @party="red">,
|
81
|
+
#<Morph::Person @name="Ali Davidson", @party="blue">,
|
82
|
+
#<Morph::Person @name="Sue Smith", @party="green">]
|
83
|
+
|
84
|
+
people.last.party
|
85
|
+
|
86
|
+
# => "green"
|
87
|
+
|
88
|
+
## Morph creating classes `from_tsv`
|
89
|
+
|
90
|
+
Here's example code showing Morph playing with TSV (tab-separated values):
|
91
|
+
|
92
|
+
tsv = %Q[name\tparty\nTed Roe\tred\nAli Davidson\tblue\nSue Smith\tgreen]
|
93
|
+
|
94
|
+
people = Morph.from_tsv(tsv, 'person')
|
95
|
+
|
96
|
+
# => [#<Morph::Person @name="Ted Roe", @party="red">,
|
97
|
+
#<Morph::Person @name="Ali Davidson", @party="blue">,
|
98
|
+
#<Morph::Person @name="Sue Smith", @party="green">]
|
99
|
+
|
100
|
+
people.last.party
|
101
|
+
|
102
|
+
# => "green"
|
103
|
+
|
104
|
+
## Morph creating classes `from_xml`
|
105
|
+
|
106
|
+
Here's example code showing Morph playing with XML:
|
107
|
+
|
108
|
+
xml = %Q[<?xml version="1.0" encoding="UTF-8"?>
|
109
|
+
<councils type="array">
|
110
|
+
<council code='1'>
|
111
|
+
<name>Aberdeen City Council</name>
|
112
|
+
</council>
|
113
|
+
<council code='2'>
|
114
|
+
<name>Allerdale Borough Council</name>
|
115
|
+
</council>
|
116
|
+
</councils>]
|
117
|
+
|
118
|
+
councils = Morph.from_xml(xml)
|
119
|
+
|
120
|
+
# => [#<Morph::Council @code="1", @name="Aberdeen City Council">,
|
121
|
+
#<Morph::Council @code="2", @name="Allerdale Borough Council">]
|
122
|
+
|
123
|
+
councils.first.name
|
124
|
+
|
125
|
+
# => "Aberdeen City Council"
|
126
|
+
|
127
|
+
## Registering a listener to new class / methods via `register_listener`
|
128
|
+
|
129
|
+
You can use `register_listener` to get callbacks when new methods on a class are
|
130
|
+
created.
|
131
|
+
|
132
|
+
For example given Morph used as a mixin:
|
133
|
+
|
134
|
+
class Project; include Morph; end
|
135
|
+
project = Project.new
|
136
|
+
|
137
|
+
Register listener:
|
138
|
+
|
139
|
+
listener = -> (klass, method) do
|
140
|
+
puts "class: #{klass.to_s} --- method: #{method}"
|
141
|
+
end
|
142
|
+
Morph.register_listener listener
|
143
|
+
|
144
|
+
Callback prints string as new methods are created via assignment calls:
|
145
|
+
|
146
|
+
project.deadline = "11 11 2075"
|
147
|
+
# class: Project --- method: deadline
|
148
|
+
|
149
|
+
project.completed = true
|
150
|
+
# class: Project --- method: completed
|
151
|
+
|
152
|
+
To unregister a listener use `unregister_listener`:
|
153
|
+
|
154
|
+
Morph.unregister_listener listener
|
155
|
+
|
156
|
+
For an example of Morph's `register_listener` being used to
|
157
|
+
[create a Github API](https://github.com/robmckinnon/hubbit/blob/master/lib/hubbit.rb)
|
158
|
+
see the [Hubbit module](https://github.com/robmckinnon/hubbit/blob/master/lib/hubbit.rb).
|
159
|
+
|
160
|
+
## Morph making sample Active Record line via `script_generate`
|
161
|
+
|
162
|
+
Time to generate an Active Record model? Get a sample script line like this:
|
163
|
+
|
164
|
+
Morph.script_generate(Project)
|
165
|
+
#=> "rails destroy model Project;
|
166
|
+
# rails generate model Project completed:string deadline:string
|
167
|
+
|
168
|
+
or specify the generator:
|
169
|
+
|
170
|
+
Morph.script_generate(Hubbit, :generator => 'rspec_model')
|
171
|
+
#=> "rails destroy rspec_model Project;
|
172
|
+
# rails generate rspec_model Project completed:string deadline:string
|
173
|
+
|
174
|
+
You'll have to edit this as it currently sets all data types to be string, and
|
175
|
+
doesn't understand associations.
|
176
|
+
|
177
|
+
|
178
|
+
## Morph setting hash of attributes via `morph`
|
179
|
+
|
180
|
+
class Order; include Morph; end
|
181
|
+
order = Order.new
|
182
|
+
|
183
|
+
How about adding a hash of attribute values?
|
184
|
+
|
185
|
+
order.morph :drink => 'tea', :spoons_of_sugar => 2, :milk => 'prefer soya thanks'
|
186
|
+
|
187
|
+
Looks like we got 'em:
|
188
|
+
|
189
|
+
order.drink # => "tea"
|
190
|
+
order.spoons_of_sugar # => 2
|
191
|
+
order.milk # => "prefer soya thanks"
|
192
|
+
|
193
|
+
|
194
|
+
## Morph obtaining hash of attributes via `morph_attributes`
|
195
|
+
|
196
|
+
Create an item:
|
197
|
+
|
198
|
+
class Item; include Morph; end
|
199
|
+
item = Item.new
|
200
|
+
item.morph :name => 'spinach', :cost => 0.50
|
201
|
+
|
202
|
+
Now an order:
|
203
|
+
|
204
|
+
class Order; include Morph; end
|
205
|
+
order = Order.new
|
206
|
+
order.no = 123
|
207
|
+
order.items = [item]
|
208
|
+
|
209
|
+
Want to retrieve all that as a nested hash of values? No problem:
|
210
|
+
|
211
|
+
order.morph_attributes
|
212
|
+
|
213
|
+
# => {:items=>[{:name=>"spinach", :cost=>0.5}], :no=>123}
|
214
|
+
|
215
|
+
|
216
|
+
## Last bits
|
217
|
+
|
218
|
+
See LICENSE for the terms of this software.
|
219
|
+
|
220
|
+
. ,
|
221
|
+
. ?7+~::+II~
|
222
|
+
. ?7: ,:+7
|
223
|
+
. 777IIII777? 7: :?7
|
224
|
+
. =I= I: 7? ,+7
|
225
|
+
. I? ,, 77 7: :I
|
226
|
+
. = ?7777 77 7 7 7+, :7
|
227
|
+
. 7 777777 ~77+=77 I+ I? ,7
|
228
|
+
. :7 77 ~77 I I7 7 ?: ?
|
229
|
+
. I 77 7, 7 7 :I I ?
|
230
|
+
. 7 ?77=7~ 77777 7 ~+ ,+
|
231
|
+
. 7~ 7 :I7?~ 7
|
232
|
+
. =? 7 ?I ~I77= I=
|
233
|
+
. 7 ? :, 7 I7777, 7 7
|
234
|
+
. ? 777?~~7777+ 7 7~ 7
|
235
|
+
. ?7 ,777777=, ,7 7 ,7
|
236
|
+
. 7= , =7 7: 7
|
237
|
+
. +7 :7 7 ,I
|
238
|
+
. :7 ?~ 7? 7
|
239
|
+
. 7 7 ~II7~, 7
|
240
|
+
. 7 7 , =7777777?+,,, I=
|
241
|
+
. :7, ~==, 7
|
242
|
+
. II~,, 77~
|
243
|
+
. ,I? +777
|
244
|
+
. 7+, ~7777:
|
245
|
+
. == :77
|
246
|
+
. :7: ,7I
|
247
|
+
. 7I 7
|
248
|
+
. I ,7, 7
|
249
|
+
. =7 77=7 7
|
250
|
+
. ,7 7I 7 7
|
251
|
+
. I, I7 7 7
|
252
|
+
. ?, ,7 7, 7
|
253
|
+
. 7 7~ 7, 7
|
254
|
+
. 7 ,7I 7 7
|
255
|
+
. =+ =7 7 ~=
|
256
|
+
. =7 7, 7 7
|
257
|
+
. ,7, ~7IIII7+, 7
|
258
|
+
. +: II I
|
259
|
+
. ?7 I? +~
|
260
|
+
. II, +I 7
|
261
|
+
. ~7 ,I 7
|
262
|
+
. 7= ~7 7
|
263
|
+
. ?7, ~7+ ?~
|
264
|
+
. ~7777I= ,7
|
265
|
+
. 7: 7
|
266
|
+
. I 7
|
267
|
+
. I ,:77I 7
|
268
|
+
. I :7 I
|
269
|
+
. I =~
|
270
|
+
. 7 , ,7
|
271
|
+
. +, 7 : ,7
|
272
|
+
. + 7 + 7
|
273
|
+
. + 7 + ,7
|
274
|
+
. 7 I ? ,7
|
275
|
+
. 7 +: 7 ,7
|
276
|
+
. 7 =+ 7 ,7
|
277
|
+
. 7 :I I ,7
|
278
|
+
. 7 :I 7 7
|
279
|
+
. 7 :I I 7
|
280
|
+
. I, ,7 I: 7
|
281
|
+
. =+ ,7 ? 7
|
282
|
+
. :?, ,7 7, 7
|
283
|
+
. I: ,7 7, ?
|
284
|
+
. :7 ,7 7, ,
|
285
|
+
. +I, : ? ,=
|
286
|
+
. += ~ =~ 7
|
287
|
+
. :II,, = I ?
|
288
|
+
. =I= ? 7, :7
|
289
|
+
. II~ I 7, ,II
|
290
|
+
. 7~ ~7 7 ,=7
|
291
|
+
. = =7 I, ::
|
292
|
+
. 77II?==?II777777777777777 7~ 7
|
293
|
+
. 77+,, 7:
|
294
|
+
. 777777+:,~777
|
295
|
+
.
|
data/lib/morph.rb
CHANGED
@@ -17,10 +17,136 @@ rescue Exception => e
|
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
+
module Chas
|
21
|
+
@adding_morph_method = Hash.new {|hash,klass| hash[klass] = false } unless defined?(@adding_morph_method)
|
22
|
+
@morph_methods = Hash.new {|hash,klass| hash[klass] = {} } unless defined?(@morph_methods)
|
23
|
+
@morph_attributes = Hash.new {|hash,klass| hash[klass] = [] } unless defined?(@morph_attributes)
|
24
|
+
@listeners = {}
|
25
|
+
|
26
|
+
def self.register_listener listener
|
27
|
+
@listeners[listener.object_id] = listener
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.unregister_listener listener
|
31
|
+
@listeners.delete(listener.object_id) if @listeners.has_key?(listener.object_id)
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.morph_classes
|
35
|
+
@morph_attributes.keys
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.add_method klass, symbol
|
39
|
+
if adding_morph_method?(klass)
|
40
|
+
@morph_methods[klass][symbol] = true
|
41
|
+
is_writer = symbol.to_s =~ /=$/
|
42
|
+
unless is_writer
|
43
|
+
@morph_attributes[klass] << symbol
|
44
|
+
@listeners.values.each { |l| l.call klass, symbol }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def self.remove_method klass, symbol
|
50
|
+
if @morph_methods[klass].has_key? symbol
|
51
|
+
@morph_methods[klass].delete symbol
|
52
|
+
is_writer = symbol.to_s =~ /=$/
|
53
|
+
@morph_attributes[klass].delete(symbol) unless is_writer
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.morph_attributes klass
|
58
|
+
if klass.superclass.respond_to?(:morph_attributes)
|
59
|
+
@morph_attributes[klass] + klass.superclass.morph_attributes
|
60
|
+
else
|
61
|
+
@morph_attributes[klass] + []
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.morph_methods klass
|
66
|
+
methods = if RUBY_VERSION >= "1.9"
|
67
|
+
@morph_methods[klass].keys.sort
|
68
|
+
else
|
69
|
+
@morph_methods[klass].keys.map(&:to_s).sort
|
70
|
+
end
|
71
|
+
|
72
|
+
if klass.superclass.respond_to?(:morph_attributes)
|
73
|
+
methods += klass.superclass.morph_methods
|
74
|
+
end
|
75
|
+
methods
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
def self.adding_morph_method? klass
|
80
|
+
@adding_morph_method[klass]
|
81
|
+
end
|
82
|
+
|
83
|
+
public
|
84
|
+
def self.start_adding_morph_method klass
|
85
|
+
@adding_morph_method[klass] = true
|
86
|
+
end
|
87
|
+
|
88
|
+
def self.finish_adding_morph_method klass
|
89
|
+
@adding_morph_method[klass] = false
|
90
|
+
end
|
91
|
+
|
92
|
+
def self.add_morph_attribute klass, attribute
|
93
|
+
start_adding_morph_method(klass)
|
94
|
+
klass.send(:attr_accessor, attribute)
|
95
|
+
finish_adding_morph_method(klass)
|
96
|
+
end
|
97
|
+
|
98
|
+
def self.morph_method_missing object, symbol, *args
|
99
|
+
attribute = symbol.to_s.chomp '='
|
100
|
+
if RUBY_VERSION >= "1.9"
|
101
|
+
attribute = attribute.to_sym
|
102
|
+
end
|
103
|
+
|
104
|
+
if Object.instance_methods.include?(attribute)
|
105
|
+
raise "'#{attribute}' is an instance_method on Object, cannot create accessor methods for '#{attribute}'"
|
106
|
+
elsif argument_provided? args
|
107
|
+
base = object.class
|
108
|
+
add_morph_attribute base, attribute
|
109
|
+
object.send(symbol, *args)
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def self.argument_provided? args
|
114
|
+
args.size > 0 && !args[0].nil? && !(args[0].is_a?(String) && args[0].strip.size == 0)
|
115
|
+
end
|
116
|
+
|
117
|
+
def self.convert_to_morph_class_name label
|
118
|
+
name = label.to_s + ''
|
119
|
+
name.tr!(',.:"\'/()\-*\\',' ')
|
120
|
+
name.gsub!('%','percentage')
|
121
|
+
name.strip!
|
122
|
+
name.gsub!(/^(\d)/, '_\1')
|
123
|
+
name.gsub!(/\s/,'_')
|
124
|
+
name.squeeze!('_')
|
125
|
+
name
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.convert_to_morph_method_name label
|
129
|
+
convert_to_morph_class_name label.to_s.downcase
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
20
134
|
module Morph
|
21
|
-
VERSION =
|
135
|
+
VERSION = '0.4.0' unless defined? Morph::VERSION
|
22
136
|
|
23
137
|
class << self
|
138
|
+
def classes
|
139
|
+
Chas.morph_classes
|
140
|
+
end
|
141
|
+
|
142
|
+
def register_listener listener
|
143
|
+
Chas.register_listener listener
|
144
|
+
end
|
145
|
+
|
146
|
+
def unregister_listener listener
|
147
|
+
Chas.unregister_listener listener
|
148
|
+
end
|
149
|
+
|
24
150
|
def generate_migrations object, options={}
|
25
151
|
options[:ignore] ||= []
|
26
152
|
options[:belongs_to_id] ||= ''
|
@@ -72,19 +198,22 @@ module Morph
|
|
72
198
|
from_hash hash, namespace
|
73
199
|
end
|
74
200
|
|
201
|
+
def from_json json, root_key=nil, namespace=Morph
|
202
|
+
require 'json' unless defined? JSON
|
203
|
+
hash = JSON.parse json
|
204
|
+
hash = { root_key => hash } if root_key
|
205
|
+
from_hash hash, namespace
|
206
|
+
end
|
207
|
+
|
75
208
|
def from_hash hash, namespace=Morph
|
76
209
|
if hash.keys.size == 1
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
elsif hash[key].is_a? Array
|
85
|
-
array = hash[key]
|
86
|
-
name = key.to_s.singularize
|
87
|
-
objects_from_array(array, name, namespace)
|
210
|
+
name = hash.keys.first
|
211
|
+
|
212
|
+
case hash[name]
|
213
|
+
when Hash
|
214
|
+
object_from_hash hash[name], name, namespace
|
215
|
+
when Array
|
216
|
+
objects_from_array hash[name], name, namespace
|
88
217
|
else
|
89
218
|
raise 'hash root value must be a Hash or an Array'
|
90
219
|
end
|
@@ -104,7 +233,6 @@ module Morph
|
|
104
233
|
|
105
234
|
def included(base)
|
106
235
|
base.extend ClassMethods
|
107
|
-
base.send(:include, InstanceMethods)
|
108
236
|
base.send(:include, MethodMissing)
|
109
237
|
end
|
110
238
|
|
@@ -142,7 +270,7 @@ module Morph
|
|
142
270
|
end
|
143
271
|
|
144
272
|
def object_from_name name, namespace
|
145
|
-
name = name.
|
273
|
+
name = Chas.convert_to_morph_class_name(name).camelize
|
146
274
|
begin
|
147
275
|
type = class_constant namespace, name
|
148
276
|
rescue NameError => e
|
@@ -160,7 +288,8 @@ module Morph
|
|
160
288
|
|
161
289
|
def objects_from_array array, name, namespace
|
162
290
|
if array.size > 0 && array.collect(&:class).uniq == [Hash]
|
163
|
-
|
291
|
+
name = name.to_s.singularize
|
292
|
+
array.map! { |hash| object_from_hash(hash, name, namespace) }
|
164
293
|
else
|
165
294
|
array
|
166
295
|
end
|
@@ -195,144 +324,81 @@ module Morph
|
|
195
324
|
|
196
325
|
module ClassMethods
|
197
326
|
|
198
|
-
@@adding_morph_method = Hash.new {|hash,klass| hash[klass] = false } unless defined?(@@adding_morph_method)
|
199
|
-
@@morph_methods = Hash.new {|hash,klass| hash[klass] = {} } unless defined?(@@morph_methods)
|
200
|
-
@@morph_attributes = Hash.new {|hash,klass| hash[klass] = [] } unless defined?(@@morph_attributes)
|
201
|
-
|
202
327
|
def morph_attributes
|
203
|
-
|
204
|
-
@@morph_attributes[self] + superclass.morph_attributes
|
205
|
-
else
|
206
|
-
@@morph_attributes[self] + []
|
207
|
-
end
|
328
|
+
Chas.morph_attributes(self)
|
208
329
|
end
|
209
330
|
|
210
331
|
def morph_methods
|
211
|
-
|
212
|
-
@@morph_methods[self].keys.sort.map(&:to_sym)
|
213
|
-
else
|
214
|
-
@@morph_methods[self].keys.sort
|
215
|
-
end
|
216
|
-
|
217
|
-
if superclass.respond_to?(:morph_attributes)
|
218
|
-
methods += superclass.morph_methods
|
219
|
-
end
|
220
|
-
methods
|
221
|
-
end
|
222
|
-
|
223
|
-
def add_morph_attribute attribute, *args
|
224
|
-
@@adding_morph_method[self] = true
|
225
|
-
class_eval "attr_accessor :#{attribute}"
|
226
|
-
@@adding_morph_method[self] = false
|
332
|
+
Chas.morph_methods(self)
|
227
333
|
end
|
228
334
|
|
229
335
|
protected
|
230
336
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
is_writer = symbol.to_s =~ /=$/
|
235
|
-
@@morph_attributes[self] << symbol unless is_writer
|
236
|
-
end
|
237
|
-
end
|
238
|
-
|
239
|
-
def method_removed symbol
|
240
|
-
if @@morph_methods[self].has_key? symbol.to_s
|
241
|
-
@@morph_methods[self].delete symbol.to_s
|
242
|
-
is_writer = symbol.to_s =~ /=$/
|
243
|
-
@@morph_attributes[self].delete(symbol) unless is_writer
|
244
|
-
end
|
245
|
-
end
|
337
|
+
def method_added symbol
|
338
|
+
Chas.add_method self, symbol
|
339
|
+
end
|
246
340
|
|
341
|
+
def method_removed symbol
|
342
|
+
Chas.remove_method self, symbol
|
343
|
+
end
|
247
344
|
end
|
248
345
|
|
249
346
|
module MethodMissing
|
250
347
|
def method_missing symbol, *args
|
251
348
|
is_writer = symbol.to_s =~ /=$/
|
252
349
|
if is_writer
|
253
|
-
|
350
|
+
Chas.morph_method_missing(self, symbol, *args)
|
254
351
|
else
|
255
352
|
super
|
256
353
|
end
|
257
354
|
end
|
258
355
|
end
|
259
356
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
attribute = Helper.convert_to_morph_method_name(attributes_or_label)
|
286
|
-
send("#{attribute}=".to_sym, value)
|
287
|
-
end
|
288
|
-
end
|
289
|
-
|
290
|
-
def morph_attributes
|
291
|
-
attributes = self.class.morph_attributes.inject({}) do |hash, attribute|
|
292
|
-
unless attribute =~ /=\Z/
|
293
|
-
symbol = attribute.to_sym
|
294
|
-
value = send(symbol)
|
295
|
-
|
296
|
-
value.each do |key, v|
|
297
|
-
value[key] = v.morph_attributes if v.respond_to?(:morph_attributes)
|
298
|
-
end if value.is_a? Hash
|
299
|
-
|
300
|
-
value = value.collect {|v| v.respond_to?(:morph_attributes) ? v.morph_attributes : v } if value.is_a? Array
|
301
|
-
value = value.morph_attributes if value.respond_to? :morph_attributes
|
302
|
-
|
303
|
-
|
304
|
-
hash[symbol] = value
|
305
|
-
end
|
306
|
-
hash
|
307
|
-
end
|
357
|
+
#
|
358
|
+
# Set attribute value(s). Adds accessor methods to class if
|
359
|
+
# they are not already present.
|
360
|
+
#
|
361
|
+
# Can be called with a +string+ and a value, a +symbol+ and a value,
|
362
|
+
# or with a +hash+ of attribute to value pairs. For example.
|
363
|
+
#
|
364
|
+
# require 'rubygems'; require 'morph'
|
365
|
+
#
|
366
|
+
# class Order; include Morph; end
|
367
|
+
#
|
368
|
+
# order = Order.new
|
369
|
+
# order.morph :drink => 'tea', :sugars => 2, 'milk' => 'yes please'
|
370
|
+
# order.morph 'Payment type:', 'will wash dishes'
|
371
|
+
# order.morph :lemon, false
|
372
|
+
#
|
373
|
+
# p order # -> #<Order:0x33c50c @lemon=false, @milk="yes please",
|
374
|
+
# @payment_type="will wash dishes", @sugars=2, @drink="tea">
|
375
|
+
#
|
376
|
+
def morph attributes_or_label, value=nil
|
377
|
+
if attributes_or_label.is_a? Hash
|
378
|
+
attributes_or_label.each { |a, v| morph(a, v) }
|
379
|
+
else
|
380
|
+
attribute = Chas.convert_to_morph_method_name(attributes_or_label)
|
381
|
+
send("#{attribute}=".to_sym, value)
|
308
382
|
end
|
383
|
+
end
|
309
384
|
|
310
|
-
|
385
|
+
def morph_attributes
|
386
|
+
attributes = self.class.morph_attributes.inject({}) do |hash, attribute|
|
387
|
+
unless attribute =~ /=\Z/
|
388
|
+
symbol = attribute.to_sym
|
389
|
+
value = send(symbol)
|
311
390
|
|
312
|
-
|
313
|
-
|
314
|
-
if
|
315
|
-
attribute = attribute.to_sym
|
316
|
-
end
|
391
|
+
value.each do |key, v|
|
392
|
+
value[key] = v.morph_attributes if v.respond_to?(:morph_attributes)
|
393
|
+
end if value.is_a? Hash
|
317
394
|
|
318
|
-
|
319
|
-
|
320
|
-
elsif Helper.argument_provided? args
|
321
|
-
base = object.class
|
322
|
-
base.add_morph_attribute attribute
|
323
|
-
object.send(symbol, *args)
|
324
|
-
end
|
325
|
-
end
|
395
|
+
value = value.collect {|v| v.respond_to?(:morph_attributes) ? v.morph_attributes : v } if value.is_a? Array
|
396
|
+
value = value.morph_attributes if value.respond_to? :morph_attributes
|
326
397
|
|
327
|
-
|
328
|
-
args.size > 0 && !args[0].nil? && !(args[0].is_a?(String) && args[0].strip.size == 0)
|
329
|
-
end
|
330
|
-
|
331
|
-
def self.convert_to_morph_method_name label
|
332
|
-
name = label.to_s.downcase.tr('()\-*',' ').gsub("'",' ').gsub('/',' ').gsub('%','percentage').strip.chomp(':').strip.gsub(/\s/,'_').squeeze('_')
|
333
|
-
name = '_'+name if name =~ /^\d/
|
334
|
-
name
|
398
|
+
hash[symbol] = value
|
335
399
|
end
|
400
|
+
hash
|
336
401
|
end
|
337
402
|
end
|
403
|
+
|
338
404
|
end
|
metadata
CHANGED
@@ -1,104 +1,79 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morph
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Rob McKinnon
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-02-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: 2.0.2
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: 2.0.2
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
28
|
name: rspec
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- -
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- -
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
|
42
|
-
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - '>='
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - '>='
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
description: |
|
56
|
-
Morph mixin allows you to emerge Ruby class definitions from data via calling assignment methods.
|
57
|
-
email:
|
58
|
-
- rob ~@nospam@~ rubyforge.org
|
41
|
+
description:
|
42
|
+
email: rob ~@nospam@~ rubyforge.org
|
59
43
|
executables: []
|
60
44
|
extensions: []
|
61
45
|
extra_rdoc_files:
|
62
|
-
-
|
63
|
-
- LICENSE
|
64
|
-
- README
|
46
|
+
- README.md
|
65
47
|
files:
|
66
48
|
- CHANGELOG
|
67
|
-
- lib/morph.rb
|
68
49
|
- LICENSE
|
69
|
-
- README
|
70
|
-
-
|
71
|
-
- morph.gemspec
|
72
|
-
- Rakefile
|
50
|
+
- README.md
|
51
|
+
- lib/morph.rb
|
73
52
|
homepage: https://github.com/robmckinnon/morph
|
74
|
-
licenses:
|
53
|
+
licenses:
|
54
|
+
- MIT
|
75
55
|
metadata: {}
|
76
|
-
post_install_message:
|
56
|
+
post_install_message:
|
77
57
|
rdoc_options:
|
78
|
-
- --
|
79
|
-
-
|
80
|
-
- --title
|
81
|
-
- Morph
|
82
|
-
- --main
|
83
|
-
- README
|
84
|
-
- --inline-source
|
58
|
+
- "--main"
|
59
|
+
- README.md
|
85
60
|
require_paths:
|
86
61
|
- lib
|
87
62
|
required_ruby_version: !ruby/object:Gem::Requirement
|
88
63
|
requirements:
|
89
|
-
- -
|
64
|
+
- - ">="
|
90
65
|
- !ruby/object:Gem::Version
|
91
66
|
version: '0'
|
92
67
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
93
68
|
requirements:
|
94
|
-
- -
|
69
|
+
- - ">="
|
95
70
|
- !ruby/object:Gem::Version
|
96
|
-
version: '
|
71
|
+
version: '0'
|
97
72
|
requirements: []
|
98
|
-
rubyforge_project:
|
99
|
-
rubygems_version: 2.
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.5.2
|
100
75
|
signing_key:
|
101
76
|
specification_version: 4
|
102
|
-
summary: Morph mixin allows you to emerge class definitions via
|
103
|
-
|
77
|
+
summary: Morph mixin allows you to emerge class definitions via assignment method
|
78
|
+
calls.
|
104
79
|
test_files: []
|
data/Manifest
DELETED
data/README
DELETED
@@ -1,287 +0,0 @@
|
|
1
|
-
Morph mixin allows you to emerge Ruby class definitions from data via calling assignment methods.
|
2
|
-
|
3
|
-
|
4
|
-
== Installing Morph
|
5
|
-
|
6
|
-
gem install morph
|
7
|
-
|
8
|
-
To use Morph:
|
9
|
-
|
10
|
-
require 'morph'
|
11
|
-
|
12
|
-
== Morph creating classes +from_csv+
|
13
|
-
|
14
|
-
Here's example code showing Morph playing with CSV (comma-separated values):
|
15
|
-
|
16
|
-
csv = %Q[name,party\nTed Roe,red\nAli Davidson,blue\nSue Smith,green]
|
17
|
-
|
18
|
-
people = Morph.from_csv(csv, 'person')
|
19
|
-
|
20
|
-
# => [#<Morph::Person @name="Ted Roe", @party="red">,
|
21
|
-
#<Morph::Person @name="Ali Davidson", @party="blue">,
|
22
|
-
#<Morph::Person @name="Sue Smith", @party="green">]
|
23
|
-
|
24
|
-
people.last.party
|
25
|
-
|
26
|
-
# => "green"
|
27
|
-
|
28
|
-
== Morph creating classes +from_tsv+
|
29
|
-
|
30
|
-
Here's example code showing Morph playing with TSV (tab-separated values):
|
31
|
-
|
32
|
-
tsv = %Q[name\tparty\nTed Roe\tred\nAli Davidson\tblue\nSue Smith\tgreen]
|
33
|
-
|
34
|
-
people = Morph.from_tsv(tsv, 'person')
|
35
|
-
|
36
|
-
# => [#<Morph::Person @name="Ted Roe", @party="red">,
|
37
|
-
#<Morph::Person @name="Ali Davidson", @party="blue">,
|
38
|
-
#<Morph::Person @name="Sue Smith", @party="green">]
|
39
|
-
|
40
|
-
people.last.party
|
41
|
-
|
42
|
-
# => "green"
|
43
|
-
|
44
|
-
== Morph creating classes +from_xml+
|
45
|
-
|
46
|
-
Here's example code showing Morph playing with XML:
|
47
|
-
|
48
|
-
xml = %Q[<?xml version="1.0" encoding="UTF-8"?>
|
49
|
-
<councils type="array">
|
50
|
-
<council code='1'>
|
51
|
-
<name>Aberdeen City Council</name>
|
52
|
-
</council>
|
53
|
-
<council code='2'>
|
54
|
-
<name>Allerdale Borough Council</name>
|
55
|
-
</council>
|
56
|
-
</councils>]
|
57
|
-
|
58
|
-
councils = Morph.from_xml(xml)
|
59
|
-
|
60
|
-
# => [#<Morph::Council @code="1", @name="Aberdeen City Council">,
|
61
|
-
#<Morph::Council @code="2", @name="Allerdale Borough Council">]
|
62
|
-
|
63
|
-
councils.first.name
|
64
|
-
|
65
|
-
# => "Aberdeen City Council"
|
66
|
-
|
67
|
-
== Morph playing with +Nokogiri+
|
68
|
-
|
69
|
-
Here's example code showing Morph playing with Nokogiri in Ruby 1.9.2:
|
70
|
-
|
71
|
-
require 'morph'; require 'nokogiri'; require 'open-uri'
|
72
|
-
|
73
|
-
|
74
|
-
class Hubbit
|
75
|
-
include Morph # allows class to morph
|
76
|
-
|
77
|
-
def initialize name
|
78
|
-
doc = Nokogiri::HTML open("https://github.com/#{name}")
|
79
|
-
|
80
|
-
profile_fields = doc.search('.vcard dt')
|
81
|
-
|
82
|
-
profile_fields.each do |node|
|
83
|
-
label = node.inner_text
|
84
|
-
value = node.next_element.inner_text.strip
|
85
|
-
|
86
|
-
morph(label, value) # morph magic adds accessor methods!
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
def member_since_date
|
91
|
-
Date.parse member_since
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
def Hubbit name; Hubbit.new name; end
|
96
|
-
|
97
|
-
The model emerges from the data. Let's start by looking up 'why':
|
98
|
-
|
99
|
-
why = Hubbit 'why'
|
100
|
-
|
101
|
-
What new methods do we have?
|
102
|
-
|
103
|
-
Hubbit.morph_methods.map {|m| m.to_s}
|
104
|
-
|
105
|
-
#=> ["location", "location=", "member_since", "member_since=", "name", "name="]
|
106
|
-
|
107
|
-
Ah-ha, so we have a name attribute now:
|
108
|
-
|
109
|
-
why.name # => "Squatting until _why gets home."
|
110
|
-
|
111
|
-
We wrote a +member_since_date+ method in our class, let's call that now:
|
112
|
-
|
113
|
-
why.member_since_date # => Wed, 19 Aug 2009
|
114
|
-
|
115
|
-
|
116
|
-
Let's add some of why's projects:
|
117
|
-
|
118
|
-
why.projects = %w[shoes hacketyhack camping hoodwinkd hpricot
|
119
|
-
markaby mousehole parkplace poignant sandbox]
|
120
|
-
|
121
|
-
That why's a productive fellow! Note new accessor methods have been added:
|
122
|
-
|
123
|
-
Hubbit.morph_methods.map {|m| m.to_s}
|
124
|
-
|
125
|
-
#=> ["location", "location=", "member_since", "member_since=", "name", "name=",
|
126
|
-
# "projects", "projects="]
|
127
|
-
|
128
|
-
|
129
|
-
Let's do some more morphing:
|
130
|
-
|
131
|
-
dhh = Hubbit 'dhh'
|
132
|
-
|
133
|
-
Do we have more methods now?
|
134
|
-
|
135
|
-
Hubbit.morph_methods.map {|m| m.to_s}
|
136
|
-
|
137
|
-
#=> ["company", "company=", "email", "email=", "location", "location=",
|
138
|
-
# "member_since", "member_since=", "name", "name=", "projects", "projects=",
|
139
|
-
# "website_blog", "website_blog="]
|
140
|
-
|
141
|
-
So, a new company method has appeared:
|
142
|
-
|
143
|
-
dhh.company #=> "37signals"
|
144
|
-
|
145
|
-
|
146
|
-
== Morph making sample Active Record line via +script_generate+
|
147
|
-
|
148
|
-
Time to generate an Active Record model? Get a sample script line like this:
|
149
|
-
|
150
|
-
Morph.script_generate(Hubbit)
|
151
|
-
|
152
|
-
#=> "rails destroy model Hubbit;
|
153
|
-
# rails generate model Hubbit company:string email:string location:string
|
154
|
-
# member_since:string name:string projects:string website_blog:string"
|
155
|
-
|
156
|
-
or specify the generator:
|
157
|
-
|
158
|
-
Morph.script_generate(Hubbit, :generator => 'rspec_model')
|
159
|
-
|
160
|
-
#=> "rails destroy rspec_model Hubbit;
|
161
|
-
# rails generate rspec_model Hubbit company:string email:string
|
162
|
-
# location:string member_since:string name:string projects:string
|
163
|
-
# website_blog:string"
|
164
|
-
|
165
|
-
You'll have to edit this as it currently sets all data types to be string, and
|
166
|
-
doesn't understand associations.
|
167
|
-
|
168
|
-
|
169
|
-
== Morph setting hash of attributes via +morph+
|
170
|
-
|
171
|
-
class Order; include Morph; end
|
172
|
-
order = Order.new
|
173
|
-
|
174
|
-
How about adding a hash of attribute values?
|
175
|
-
|
176
|
-
order.morph :drink => 'tea', :spoons_of_sugar => 2, :milk => 'prefer soya thanks'
|
177
|
-
|
178
|
-
Looks like we got 'em:
|
179
|
-
|
180
|
-
order.drink # => "tea"
|
181
|
-
order.spoons_of_sugar # => 2
|
182
|
-
order.milk # => "prefer soya thanks"
|
183
|
-
|
184
|
-
|
185
|
-
== Morph obtaining hash of attributes via +morph_attributes+
|
186
|
-
|
187
|
-
Create an item:
|
188
|
-
|
189
|
-
class Item; include Morph; end
|
190
|
-
item = Item.new
|
191
|
-
item.morph :name => 'spinach', :cost => 0.50
|
192
|
-
|
193
|
-
Now an order:
|
194
|
-
|
195
|
-
class Order; include Morph; end
|
196
|
-
order = Order.new
|
197
|
-
order.no = 123
|
198
|
-
order.items = [item]
|
199
|
-
|
200
|
-
Want to retrieve all that as a nested hash of values? No problem:
|
201
|
-
|
202
|
-
order.morph_attributes
|
203
|
-
|
204
|
-
# => {:items=>[{:name=>"spinach", :cost=>0.5}], :no=>123}
|
205
|
-
|
206
|
-
|
207
|
-
== Last bits
|
208
|
-
|
209
|
-
See examples/ directory for some example code.
|
210
|
-
See LICENSE for the terms of this software.
|
211
|
-
|
212
|
-
. ,
|
213
|
-
. ?7+~::+II~
|
214
|
-
. ?7: ,:+7
|
215
|
-
. 777IIII777? 7: :?7
|
216
|
-
. =I= I: 7? ,+7
|
217
|
-
. I? ,, 77 7: :I
|
218
|
-
. = ?7777 77 7 7 7+, :7
|
219
|
-
. 7 777777 ~77+=77 I+ I? ,7
|
220
|
-
. :7 77 ~77 I I7 7 ?: ?
|
221
|
-
. I 77 7, 7 7 :I I ?
|
222
|
-
. 7 ?77=7~ 77777 7 ~+ ,+
|
223
|
-
. 7~ 7 :I7?~ 7
|
224
|
-
. =? 7 ?I ~I77= I=
|
225
|
-
. 7 ? :, 7 I7777, 7 7
|
226
|
-
. ? 777?~~7777+ 7 7~ 7
|
227
|
-
. ?7 ,777777=, ,7 7 ,7
|
228
|
-
. 7= , =7 7: 7
|
229
|
-
. +7 :7 7 ,I
|
230
|
-
. :7 ?~ 7? 7
|
231
|
-
. 7 7 ~II7~, 7
|
232
|
-
. 7 7 , =7777777?+,,, I=
|
233
|
-
. :7, ~==, 7
|
234
|
-
. II~,, 77~
|
235
|
-
. ,I? +777
|
236
|
-
. 7+, ~7777:
|
237
|
-
. == :77
|
238
|
-
. :7: ,7I
|
239
|
-
. 7I 7
|
240
|
-
. I ,7, 7
|
241
|
-
. =7 77=7 7
|
242
|
-
. ,7 7I 7 7
|
243
|
-
. I, I7 7 7
|
244
|
-
. ?, ,7 7, 7
|
245
|
-
. 7 7~ 7, 7
|
246
|
-
. 7 ,7I 7 7
|
247
|
-
. =+ =7 7 ~=
|
248
|
-
. =7 7, 7 7
|
249
|
-
. ,7, ~7IIII7+, 7
|
250
|
-
. +: II I
|
251
|
-
. ?7 I? +~
|
252
|
-
. II, +I 7
|
253
|
-
. ~7 ,I 7
|
254
|
-
. 7= ~7 7
|
255
|
-
. ?7, ~7+ ?~
|
256
|
-
. ~7777I= ,7
|
257
|
-
. 7: 7
|
258
|
-
. I 7
|
259
|
-
. I ,:77I 7
|
260
|
-
. I :7 I
|
261
|
-
. I =~
|
262
|
-
. 7 , ,7
|
263
|
-
. +, 7 : ,7
|
264
|
-
. + 7 + 7
|
265
|
-
. + 7 + ,7
|
266
|
-
. 7 I ? ,7
|
267
|
-
. 7 +: 7 ,7
|
268
|
-
. 7 =+ 7 ,7
|
269
|
-
. 7 :I I ,7
|
270
|
-
. 7 :I 7 7
|
271
|
-
. 7 :I I 7
|
272
|
-
. I, ,7 I: 7
|
273
|
-
. =+ ,7 ? 7
|
274
|
-
. :?, ,7 7, 7
|
275
|
-
. I: ,7 7, ?
|
276
|
-
. :7 ,7 7, ,
|
277
|
-
. +I, : ? ,=
|
278
|
-
. += ~ =~ 7
|
279
|
-
. :II,, = I ?
|
280
|
-
. =I= ? 7, :7
|
281
|
-
. II~ I 7, ,II
|
282
|
-
. 7~ ~7 7 ,=7
|
283
|
-
. = =7 I, ::
|
284
|
-
. 77II?==?II777777777777777 7~ 7
|
285
|
-
. 77+,, 7:
|
286
|
-
. 777777+:,~777
|
287
|
-
.
|
data/Rakefile
DELETED
@@ -1,37 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require './lib/morph'
|
3
|
-
|
4
|
-
begin
|
5
|
-
require 'rspec'
|
6
|
-
rescue LoadError
|
7
|
-
puts "\nYou need to install the rspec gem to perform meta operations on this gem"
|
8
|
-
puts " gem install rspec\n"
|
9
|
-
end
|
10
|
-
|
11
|
-
begin
|
12
|
-
require 'echoe'
|
13
|
-
|
14
|
-
Echoe.new("morph") do |m|
|
15
|
-
m.author = ["Rob McKinnon"]
|
16
|
-
m.email = ["rob ~@nospam@~ rubyforge.org"]
|
17
|
-
m.summary = 'Morph mixin allows you to emerge class definitions via calling assignment methods.'
|
18
|
-
m.description = File.readlines("README").first
|
19
|
-
m.url = 'https://github.com/robmckinnon/morph'
|
20
|
-
m.install_message = 'Read usage examples at: https://github.com/robmckinnon/morph#readme'
|
21
|
-
m.version = Morph::VERSION
|
22
|
-
m.project = "morph"
|
23
|
-
m.rdoc_options << '--inline-source'
|
24
|
-
m.rdoc_pattern = ["README", "CHANGELOG", "LICENSE"]
|
25
|
-
m.runtime_dependencies = ["activesupport >=2.0.2"]
|
26
|
-
m.development_dependencies = ['rspec','echoe']
|
27
|
-
end
|
28
|
-
|
29
|
-
rescue LoadError
|
30
|
-
puts "\nYou need to install the echoe gem to perform meta operations on this gem"
|
31
|
-
puts " gem install echoe\n\n"
|
32
|
-
end
|
33
|
-
|
34
|
-
desc "Open an irb session preloaded with this library"
|
35
|
-
task :console do
|
36
|
-
sh "irb -rubygems -r ./lib/morph.rb"
|
37
|
-
end
|
data/morph.gemspec
DELETED
@@ -1,39 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
|
3
|
-
Gem::Specification.new do |s|
|
4
|
-
s.name = "morph"
|
5
|
-
s.version = "0.3.7"
|
6
|
-
|
7
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
-
s.authors = ["Rob McKinnon"]
|
9
|
-
s.date = "2014-02-24"
|
10
|
-
s.description = "Morph mixin allows you to emerge Ruby class definitions from data via calling assignment methods.\n"
|
11
|
-
s.email = ["rob ~@nospam@~ rubyforge.org"]
|
12
|
-
s.extra_rdoc_files = ["CHANGELOG", "LICENSE", "README"]
|
13
|
-
s.files = ["CHANGELOG", "lib/morph.rb", "LICENSE", "README", "Manifest", "morph.gemspec", "Rakefile"]
|
14
|
-
s.homepage = "https://github.com/robmckinnon/morph"
|
15
|
-
s.post_install_message = "Read usage examples at: https://github.com/robmckinnon/morph#readme"
|
16
|
-
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Morph", "--main", "README", "--inline-source"]
|
17
|
-
s.require_paths = ["lib"]
|
18
|
-
s.rubyforge_project = "morph"
|
19
|
-
s.rubygems_version = "2.0.3"
|
20
|
-
s.summary = "Morph mixin allows you to emerge class definitions via calling assignment methods."
|
21
|
-
|
22
|
-
if s.respond_to? :specification_version then
|
23
|
-
s.specification_version = 4
|
24
|
-
|
25
|
-
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
26
|
-
s.add_runtime_dependency(%q<activesupport>, [">= 2.0.2"])
|
27
|
-
s.add_development_dependency(%q<rspec>, [">= 0"])
|
28
|
-
s.add_development_dependency(%q<echoe>, [">= 0"])
|
29
|
-
else
|
30
|
-
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
31
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
32
|
-
s.add_dependency(%q<echoe>, [">= 0"])
|
33
|
-
end
|
34
|
-
else
|
35
|
-
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
36
|
-
s.add_dependency(%q<rspec>, [">= 0"])
|
37
|
-
s.add_dependency(%q<echoe>, [">= 0"])
|
38
|
-
end
|
39
|
-
end
|