morph 0.2.8 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,5 @@
1
+ v0.2.9. added from_csv(); require fastercsv; added morph_attributes() class method
2
+
1
3
  v0.2.8. added from_xml() and from_tsv(); updated for active_support; fixed from_hash() when hash root is an array
2
4
 
3
5
  v0.2.7. handle dash when converting to method name, reported by danwrong
data/README CHANGED
@@ -1,12 +1,51 @@
1
1
  Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
2
2
 
3
3
 
4
+ == Morph creating classes +from_xml+
5
+
6
+ Here's example code showing Morph playing with XML:
7
+
8
+ require 'rubygems'; require 'morph'
9
+
10
+ xml = %Q[<?xml version="1.0" encoding="UTF-8"?>
11
+ <councils type="array">
12
+ <council code='1'>
13
+ <name>Aberdeen City Council</name>
14
+ </council>
15
+ <council code='2'>
16
+ <name>Allerdale Borough Council</name>
17
+ </council>
18
+ </councils>]
19
+
20
+ councils = Morph.from_xml(xml)
21
+ # => [#<Morph::Council @code="1", @name="Aberdeen City Council">,
22
+ #<Morph::Council @code="2", @name="Allerdale Borough Council">]
23
+
24
+ councils.first.name
25
+ # => "Aberdeen City Council"
26
+
27
+ == Morph creating classes +from_tsv+
28
+
29
+ Here's example code showing Morph playing with TSV (tab-separated values):
30
+
31
+ require 'rubygems'; require 'morph'
32
+
33
+ tsv = %Q[name\tparty\nTed Roe\tred\nAli Davidson\tblue\nSue Smith\tgreen]
34
+
35
+ people = Morph.from_tsv(tsv, 'person')
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
+ # => "green"
4
42
 
5
43
  == Morph playing with +Hpricot+
6
44
 
7
45
  Here's example code showing Morph playing with Hpricot:
8
46
 
9
- require 'hpricot'; require 'open-uri'; require 'morph'
47
+ require 'rubygems'; require 'hpricot'; require 'open-uri'
48
+ require 'morph'
10
49
 
11
50
  class Hubbit
12
51
  include Morph # allows class to morph
data/Rakefile CHANGED
@@ -18,6 +18,7 @@ begin
18
18
  m.rubyforge_name = "morph"
19
19
  m.rdoc_options << '--inline-source'
20
20
  m.dependencies = ["activesupport >=2.0.2"]
21
+ m.dependencies = ["fastercsv >=1.5.0"]
21
22
  m.rdoc_pattern = ["README", "CHANGELOG", "LICENSE"]
22
23
  end
23
24
 
data/lib/morph.rb CHANGED
@@ -1,3 +1,4 @@
1
+ require 'fastercsv'
1
2
  begin
2
3
  require 'active_support/core_ext/object/blank'
3
4
  require 'active_support/inflector'
@@ -13,7 +14,7 @@ rescue Exception => e
13
14
  end
14
15
 
15
16
  module Morph
16
- VERSION = "0.2.8"
17
+ VERSION = "0.2.9"
17
18
 
18
19
  class << self
19
20
  def generate_migrations object, options={}
@@ -24,6 +25,18 @@ module Morph
24
25
  add_migration name, object.morph_attributes, migrations, options
25
26
  end
26
27
 
28
+ def from_csv csv, class_name, namespace=Morph
29
+ objects = []
30
+ FasterCSV.parse(csv, { :headers => true }) do |row|
31
+ object = object_from_name class_name, namespace
32
+ row.each do |key, value|
33
+ object.morph(key, value)
34
+ end
35
+ objects << object
36
+ end
37
+ objects
38
+ end
39
+
27
40
  def from_tsv tsv, class_name, namespace=Morph
28
41
  lines = tsv.split("\n")
29
42
  attributes = lines[0].split("\t")
@@ -85,8 +98,7 @@ module Morph
85
98
  type = attribute_name[/date$/] ? 'date' : 'string'
86
99
  attribute_def = "#{attribute}:#{type}"
87
100
  migration.sub!(migration, "#{migration} #{attribute_def}")
88
- end
89
- when Array
101
+ end when Array
90
102
  options[:belongs_to_id] = " #{name}_id:integer"
91
103
  migrations = add_migration(attribute, '', migrations, options)
92
104
  when Hash
@@ -155,6 +167,11 @@ module Morph
155
167
 
156
168
  @@adding_morph_method = Hash.new {|hash,klass| hash[klass] = false }
157
169
  @@morph_methods = Hash.new {|hash,klass| hash[klass] = {} }
170
+ @@morph_attributes = Hash.new {|hash,klass| hash[klass] = [] }
171
+
172
+ def morph_attributes
173
+ @@morph_attributes[self] + []
174
+ end
158
175
 
159
176
  def morph_methods
160
177
  @@morph_methods[self].keys.sort
@@ -189,11 +206,19 @@ module Morph
189
206
  protected
190
207
 
191
208
  def method_added symbol
192
- @@morph_methods[self][symbol.to_s] = true if @@adding_morph_method[self]
209
+ if @@adding_morph_method[self]
210
+ @@morph_methods[self][symbol.to_s] = true
211
+ is_writer = symbol.to_s =~ /=$/
212
+ @@morph_attributes[self] << symbol unless is_writer
213
+ end
193
214
  end
194
215
 
195
216
  def method_removed symbol
196
- @@morph_methods[self].delete symbol.to_s if @@morph_methods[self].has_key? symbol.to_s
217
+ if @@morph_methods[self].has_key? symbol.to_s
218
+ @@morph_methods[self].delete symbol.to_s
219
+ is_writer = symbol.to_s =~ /=$/
220
+ @@morph_attributes[self].delete(symbol) unless is_writer
221
+ end
197
222
  end
198
223
 
199
224
  end
@@ -282,7 +307,7 @@ module Morph
282
307
  end
283
308
 
284
309
  def convert_to_morph_method_name label
285
- name = label.to_s.downcase.tr('()\-*',' ').gsub('%','percentage').strip.chomp(':').strip.gsub(/\s/,'_').squeeze('_')
310
+ name = label.to_s.downcase.tr('()\-*',' ').gsub("'",' ').gsub('/',' ').gsub('%','percentage').strip.chomp(':').strip.gsub(/\s/,'_').squeeze('_')
286
311
  name = '_'+name if name =~ /^\d/
287
312
  name
288
313
  end
data/morph.gemspec CHANGED
@@ -2,11 +2,11 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{morph}
5
- s.version = "0.2.8"
5
+ s.version = "0.2.9"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Rob McKinnon"]
9
- s.date = %q{2010-02-09}
9
+ s.date = %q{2010-04-04}
10
10
  s.description = %q{Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
11
11
  }
12
12
  s.email = ["rob ~@nospam@~ rubyforge.org"]
@@ -16,7 +16,7 @@ Gem::Specification.new do |s|
16
16
  s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Morph", "--main", "README", "--inline-source"]
17
17
  s.require_paths = ["lib"]
18
18
  s.rubyforge_project = %q{morph}
19
- s.rubygems_version = %q{1.3.5}
19
+ s.rubygems_version = %q{1.3.6}
20
20
  s.summary = %q{Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.}
21
21
 
22
22
  if s.respond_to? :specification_version then
@@ -24,11 +24,11 @@ Gem::Specification.new do |s|
24
24
  s.specification_version = 3
25
25
 
26
26
  if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
27
- s.add_runtime_dependency(%q<activesupport>, [">= 2.0.2"])
27
+ s.add_runtime_dependency(%q<fastercsv>, [">= 1.5.0"])
28
28
  else
29
- s.add_dependency(%q<activesupport>, [">= 2.0.2"])
29
+ s.add_dependency(%q<fastercsv>, [">= 1.5.0"])
30
30
  end
31
31
  else
32
- s.add_dependency(%q<activesupport>, [">= 2.0.2"])
32
+ s.add_dependency(%q<fastercsv>, [">= 1.5.0"])
33
33
  end
34
34
  end
@@ -91,6 +91,15 @@ describe Morph do
91
91
  attributes = @morph.morph_attributes
92
92
  attributes[:every].should == 'which'
93
93
  attributes[:loose].should == { :honky_tonk => {:way => 'but'} }
94
+ attributes.delete(:every)
95
+ attributes = @morph.morph_attributes
96
+ attributes[:every].should == 'which'
97
+
98
+ attributes = @morph.class.morph_attributes
99
+ attributes.should == [:every, :loose]
100
+ attributes.delete(:every)
101
+ attributes = @morph.class.morph_attributes
102
+ attributes.should == [:every, :loose]
94
103
  end
95
104
 
96
105
  after :each do
@@ -424,9 +433,15 @@ describe Morph do
424
433
 
425
434
  company_details = Morph.from_hash(h, Company::House)
426
435
  company_details.class.name.should == 'Company::House::CompanyDetails'
427
- company_details.class.morph_methods.include?('last_full_mem_date').should be_true
428
- company_details.class.morph_methods.include?('accounts').should be_true
429
-
436
+ morph_methods = company_details.class.morph_methods
437
+ morph_methods.include?('last_full_mem_date').should be_true
438
+ morph_methods.include?('accounts').should be_true
439
+
440
+ morph_methods.delete('accounts')
441
+ morph_methods.include?('accounts').should be_false
442
+ morph_methods = company_details.class.morph_methods
443
+ morph_methods.include?('accounts').should be_true
444
+
430
445
  company_details.accounts.class.name.should == 'Company::House::Accounts'
431
446
  company_details.accounts.overdue.should == 'NO'
432
447
  company_details.last_full_mem_date.should == "2002-03-25"
@@ -512,19 +527,19 @@ xsi_schema_location: xmlgwdev.companieshouse.gov.uk/v1-0/schema/CompanyDetails.x
512
527
  def xml
513
528
  %Q[<?xml version="1.0" encoding="UTF-8"?>
514
529
  <councils type="array">
515
- <council>
530
+ <council code='1'>
516
531
  <name>Aberdeen City Council</name>
517
532
  </council>
518
- <council>
533
+ <council code='2'>
519
534
  <name>Allerdale Borough Council</name>
520
535
  </council>
521
536
  </councils>]
522
537
  end
523
538
  end
524
539
 
525
- describe 'creating from tsv (tab separated value)' do
540
+ describe 'creating from' do
526
541
 
527
- def check_councillors councillors, class_name
542
+ def check_councillors councillors, class_name, nil_value=''
528
543
  councillors.class.should == Array
529
544
  councillors.size.should == 2
530
545
  councillor = councillors.first
@@ -538,31 +553,57 @@ xsi_schema_location: xmlgwdev.companieshouse.gov.uk/v1-0/schema/CompanyDetails.x
538
553
  councillor = councillors.last
539
554
  councillor.name.should == 'Ali Davidson'
540
555
  councillor.party.should == 'labour'
541
- councillor.councillors.should == ''
556
+ councillor.councillors.should == nil_value
542
557
  councillor.councils.should == 'Basildon District Council'
543
558
  councillor.respond_to?(:council_experience).should be_false
544
559
  end
545
560
 
546
- describe 'when class name is supplied' do
547
- it 'should create classes and object instances' do
548
- councillors = Morph.from_tsv(tsv, 'Councillor')
549
- check_councillors councillors, 'Morph::Councillor'
561
+ describe 'tsv (tab separated value)' do
562
+ describe 'when class name is supplied' do
563
+ it 'should create classes and object instances' do
564
+ councillors = Morph.from_tsv(tsv, 'Councillor')
565
+ check_councillors councillors, 'Morph::Councillor'
566
+ end
550
567
  end
551
- end
552
-
553
- describe 'when class name and module name is supplied' do
554
- it 'should create classes and object instances' do
555
- Object.const_set 'Ppc', Module.new
556
- councillors = Morph.from_tsv(tsv, 'Councillor', Ppc)
557
- check_councillors councillors, 'Ppc::Councillor'
568
+
569
+ describe 'when class name and module name is supplied' do
570
+ it 'should create classes and object instances' do
571
+ Object.const_set 'Ppc', Module.new
572
+ councillors = Morph.from_tsv(tsv, 'Councillor', Ppc)
573
+ check_councillors councillors, 'Ppc::Councillor'
574
+ end
558
575
  end
559
- end
560
576
 
561
- def tsv
562
- %Q[name party councillors councils council_experience
577
+ def tsv
578
+ %Q[name party councillors councils council_experience
563
579
  Ted Roe labour Councillor for Stretford Ward Trafford Council
564
580
  Ali Davidson labour Basildon District Council
565
581
  ]
582
+ end
583
+ end
584
+
585
+ describe 'csv (comma separated value)' do
586
+ describe 'when class name is supplied' do
587
+ it 'should create classes and object instances' do
588
+ councillors = Morph.from_csv(csv, 'Councillor')
589
+ check_councillors councillors, 'Morph::Councillor', nil
590
+ end
591
+ end
592
+
593
+ describe 'when class name and module name is supplied' do
594
+ it 'should create classes and object instances' do
595
+ Object.const_set 'Ppc', Module.new
596
+ councillors = Morph.from_csv(csv, 'Councillor', Ppc)
597
+ check_councillors councillors, 'Ppc::Councillor', nil
598
+ end
599
+ end
600
+
601
+ def csv
602
+ %Q[name,party,councillors,councils,council_experience
603
+ Ted Roe,labour,Councillor for Stretford Ward,Trafford Council,
604
+ Ali Davidson,labour,,Basildon District Council,
605
+ ]
606
+ end
566
607
  end
567
608
  end
568
609
 
metadata CHANGED
@@ -1,7 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: morph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.8
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 2
8
+ - 9
9
+ version: 0.2.9
5
10
  platform: ruby
6
11
  authors:
7
12
  - Rob McKinnon
@@ -9,19 +14,23 @@ autorequire:
9
14
  bindir: bin
10
15
  cert_chain: []
11
16
 
12
- date: 2010-02-09 00:00:00 +00:00
17
+ date: 2010-04-04 00:00:00 +01:00
13
18
  default_executable:
14
19
  dependencies:
15
20
  - !ruby/object:Gem::Dependency
16
- name: activesupport
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
21
+ name: fastercsv
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
20
24
  requirements:
21
25
  - - ">="
22
26
  - !ruby/object:Gem::Version
23
- version: 2.0.2
24
- version:
27
+ segments:
28
+ - 1
29
+ - 5
30
+ - 0
31
+ version: 1.5.0
32
+ type: :runtime
33
+ version_requirements: *id001
25
34
  description: |
26
35
  Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.
27
36
 
@@ -67,18 +76,21 @@ required_ruby_version: !ruby/object:Gem::Requirement
67
76
  requirements:
68
77
  - - ">="
69
78
  - !ruby/object:Gem::Version
79
+ segments:
80
+ - 0
70
81
  version: "0"
71
- version:
72
82
  required_rubygems_version: !ruby/object:Gem::Requirement
73
83
  requirements:
74
84
  - - ">="
75
85
  - !ruby/object:Gem::Version
86
+ segments:
87
+ - 1
88
+ - 2
76
89
  version: "1.2"
77
- version:
78
90
  requirements: []
79
91
 
80
92
  rubyforge_project: morph
81
- rubygems_version: 1.3.5
93
+ rubygems_version: 1.3.6
82
94
  signing_key:
83
95
  specification_version: 3
84
96
  summary: Morph allows you to emerge class definitions via calling assignment methods; mix with Hpricot for screen scrapping fun.