morph 0.3.7 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (10) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +10 -8
  3. data/LICENSE +14 -11
  4. data/README.md +295 -0
  5. data/lib/morph.rb +192 -126
  6. metadata +23 -48
  7. data/Manifest +0 -5
  8. data/README +0 -287
  9. data/Rakefile +0 -37
  10. data/morph.gemspec +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c12c6f354c52dd3e39c28ea48a9a6aaac2dea6b3
4
- data.tar.gz: a56834d98a549d7a5e7b8a218bda2d0adb7a3acc
3
+ metadata.gz: dba2306af06acc1d857bb2a18f239d9e91a60adb
4
+ data.tar.gz: e27fce32e999fb4a2ba9909f037ab5e6820b91a8
5
5
  SHA512:
6
- metadata.gz: 521c12953766c73f27ef1409264b448d5fe76df0b236ecd9a6e6a1dc6cd2cf3bf6094e3f179f19d63f5ffbae1b17db785b5f372f19eab764fbe62f109382f5f6
7
- data.tar.gz: e049d3f4e85e0510602886c090cc37ede43b8fdd1f1d18756f7cc110488c68afa1f7d2e3955b7934e88307377cedf18ee1d62ac6a85cfbc71279432701f28c6a
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. removed show_ruby() method; updated script_generate() to utilise new rails commands
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. added from_csv(); require fastercsv; added morph_attributes() class method
19
+ v0.2.9. add from_csv(); require fastercsv; add morph_attributes() class method
18
20
 
19
- v0.2.8. added from_xml() and from_tsv(); updated for active_support; fixed from_hash() when hash root is an array
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. added Morph.from_hash method
31
+ v0.2.3. add Morph.from_hash method
30
32
 
31
- v0.2.2. changed rubygem requirement to be >= 1.2 instead of = 1.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. added script_generate method; fixed bug occurring when two morph classes exist
37
+ v0.2.0. add script_generate method; fix bug occurring when two morph classes exist
36
38
 
37
- v0.1.5. added morph_attributes instance method
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. fixed problems in packaging of v0.1.1 gem
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
- Copyright 2008 Rob McKinnon
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
- deal in the Software without restriction, including without limitation the
6
- rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
- sell copies of the Software, and to permit persons to whom the Software is
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
- all copies or substantial portions of the Software.
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
- THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
- IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
- CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
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
+ [![Build Status](https://travis-ci.org/robmckinnon/morph.svg?branch=master)](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 = "0.3.7" unless defined? Morph::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
- key = hash.keys.first
78
-
79
- if hash[key].is_a? Hash
80
- attributes = hash[key]
81
- object = object_from_name(key, namespace)
82
- add_to_object object, attributes, namespace
83
- object
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.to_s.camelize
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
- array.map! { |hash| object_from_hash(hash, name.singularize, namespace) }
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
- if superclass.respond_to?(:morph_attributes)
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
- methods = if RUBY_VERSION >= "1.9"
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
- def method_added symbol
232
- if @@adding_morph_method[self]
233
- @@morph_methods[self][symbol.to_s] = true
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
- Morph::InstanceMethods::Helper.morph_method_missing(self, symbol, *args)
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
- module InstanceMethods
261
-
262
- #
263
- # Set attribute value(s). Adds accessor methods to class if
264
- # they are not already present.
265
- #
266
- # Can be called with a +string+ and a value, a +symbol+ and a value,
267
- # or with a +hash+ of attribute to value pairs. For example.
268
- #
269
- # require 'rubygems'; require 'morph'
270
- #
271
- # class Order; include Morph; end
272
- #
273
- # order = Order.new
274
- # order.morph :drink => 'tea', :sugars => 2, 'milk' => 'yes please'
275
- # order.morph 'Payment type:', 'will wash dishes'
276
- # order.morph :lemon, false
277
- #
278
- # p order # -> #<Order:0x33c50c @lemon=false, @milk="yes please",
279
- # @payment_type="will wash dishes", @sugars=2, @drink="tea">
280
- #
281
- def morph attributes_or_label, value=nil
282
- if attributes_or_label.is_a? Hash
283
- attributes_or_label.each { |a, v| morph(a, v) }
284
- else
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
- module Helper
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
- def self.morph_method_missing object, symbol, *args
313
- attribute = symbol.to_s.chomp '='
314
- if RUBY_VERSION >= "1.9"
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
- if Object.instance_methods.include?(attribute)
319
- raise "'#{attribute}' is an instance_method on Object, cannot create accessor methods for '#{attribute}'"
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
- def self.argument_provided? args
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.3.7
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: 2014-02-24 00:00:00.000000000 Z
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
- - !ruby/object:Gem::Dependency
42
- name: echoe
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
- - CHANGELOG
63
- - LICENSE
64
- - README
46
+ - README.md
65
47
  files:
66
48
  - CHANGELOG
67
- - lib/morph.rb
68
49
  - LICENSE
69
- - README
70
- - Manifest
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: 'Read usage examples at: https://github.com/robmckinnon/morph#readme'
56
+ post_install_message:
77
57
  rdoc_options:
78
- - --line-numbers
79
- - --inline-source
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: '1.2'
71
+ version: '0'
97
72
  requirements: []
98
- rubyforge_project: morph
99
- rubygems_version: 2.0.3
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 calling assignment
103
- methods.
77
+ summary: Morph mixin allows you to emerge class definitions via assignment method
78
+ calls.
104
79
  test_files: []
data/Manifest DELETED
@@ -1,5 +0,0 @@
1
- CHANGELOG
2
- lib/morph.rb
3
- LICENSE
4
- README
5
- Manifest
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