icss 0.0.4 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. data/.rspec +2 -0
  2. data/.watchr +20 -0
  3. data/CHANGELOG.textile +9 -0
  4. data/Gemfile +1 -1
  5. data/Gemfile.lock +2 -2
  6. data/VERSION +1 -1
  7. data/examples/{BulkData.avpr → avro_examples/BulkData.avpr} +0 -0
  8. data/examples/{complicated.icss.yaml → avro_examples/complicated.icss.yaml} +0 -0
  9. data/examples/{interop.avsc → avro_examples/interop.avsc} +0 -0
  10. data/examples/{mail.avpr → avro_examples/mail.avpr} +0 -0
  11. data/examples/{namespace.avpr → avro_examples/namespace.avpr} +0 -0
  12. data/examples/{org → avro_examples/org}/apache/avro/ipc/HandshakeRequest.avsc +0 -0
  13. data/examples/{org → avro_examples/org}/apache/avro/ipc/HandshakeResponse.avsc +0 -0
  14. data/examples/{org → avro_examples/org}/apache/avro/ipc/trace/avroTrace.avdl +0 -0
  15. data/examples/{org → avro_examples/org}/apache/avro/ipc/trace/avroTrace.avpr +0 -0
  16. data/examples/{org → avro_examples/org}/apache/avro/mapred/tether/InputProtocol.avpr +0 -0
  17. data/examples/{org → avro_examples/org}/apache/avro/mapred/tether/OutputProtocol.avpr +0 -0
  18. data/examples/{simple.avpr → avro_examples/simple.avpr} +0 -0
  19. data/examples/{weather.avsc → avro_examples/weather.avsc} +0 -0
  20. data/icss.gemspec +20 -22
  21. data/lib/icss.rb +7 -7
  22. data/lib/icss/core_ext.rb +6 -0
  23. data/lib/icss/protocol.rb +1 -1
  24. data/lib/icss/target.rb +2 -2
  25. data/lib/icss/type.rb +4 -3
  26. data/spec/icss_spec.rb +6 -3
  27. data/spec/spec_helper.rb +18 -8
  28. metadata +19 -21
  29. data/lib/icss/receiver.rb +0 -289
  30. data/lib/icss/receiver/acts_as_hash.rb +0 -436
  31. data/lib/icss/receiver/acts_as_loadable.rb +0 -42
  32. data/lib/icss/receiver/tree_diff.rb +0 -74
  33. data/lib/icss/receiver/validations.rb +0 -30
data/.rspec CHANGED
@@ -1 +1,3 @@
1
1
  --color
2
+ --format documentation
3
+ --drb
data/.watchr ADDED
@@ -0,0 +1,20 @@
1
+ # -*- ruby -*-
2
+
3
+ def run_spec(file)
4
+ unless File.exist?(file)
5
+ puts "#{file} does not exist"
6
+ return
7
+ end
8
+
9
+ puts "Running #{file}"
10
+ system "bundle exec rspec #{file}"
11
+ puts
12
+ end
13
+
14
+ watch("spec/.*/*_spec\.rb") do |match|
15
+ run_spec match[0]
16
+ end
17
+
18
+ watch("app/(.*)\.rb") do |match|
19
+ run_spec %{spec/#{match[1]}_spec.rb}
20
+ end
data/CHANGELOG.textile ADDED
@@ -0,0 +1,9 @@
1
+ h3. v0.1.0 June 7 2011
2
+
3
+ * New receiver from gorillib
4
+
5
+ h3. v0.0.3 May 22 2011
6
+
7
+ * used gorillib, got rid of active_support, extlib HOORAY
8
+ * moved Receiver to gorillib.
9
+ * *breaking change*. Gorillib changed the signature of receive() to be @receive(*constructor_args, hsh)@ (formerly, the hsh was first).
data/Gemfile CHANGED
@@ -4,7 +4,7 @@ source "http://rubygems.org"
4
4
  # gem "activesupport", ">= 2.3.5"
5
5
 
6
6
  gem 'yajl-ruby', "~> 0.8.2"
7
- gem 'gorillib', "~> 0.0.4"
7
+ gem 'gorillib', "~> 0.1.0"
8
8
 
9
9
  # Add dependencies to develop your gem here.
10
10
  # Include everything needed to run rake, tests, features, etc.
data/Gemfile.lock CHANGED
@@ -3,7 +3,7 @@ GEM
3
3
  specs:
4
4
  diff-lcs (1.1.2)
5
5
  git (1.2.5)
6
- gorillib (0.0.7)
6
+ gorillib (0.1.0)
7
7
  jeweler (1.5.2)
8
8
  bundler (~> 1.0.0)
9
9
  git (>= 1.2.5)
@@ -26,7 +26,7 @@ PLATFORMS
26
26
 
27
27
  DEPENDENCIES
28
28
  bundler (~> 1.0.0)
29
- gorillib (~> 0.0.4)
29
+ gorillib (~> 0.1.0)
30
30
  jeweler (~> 1.5.2)
31
31
  rcov
32
32
  rspec (~> 2.3.0)
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.4
1
+ 0.1.0
File without changes
File without changes
data/icss.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{icss}
8
- s.version = "0.0.4"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Philip (flip) Kromer for Infochimps"]
@@ -19,41 +19,39 @@ Gem::Specification.new do |s|
19
19
  s.files = [
20
20
  ".document",
21
21
  ".rspec",
22
+ ".watchr",
23
+ "CHANGELOG.textile",
22
24
  "Gemfile",
23
25
  "Gemfile.lock",
24
26
  "LICENSE.textile",
25
27
  "README.textile",
26
28
  "Rakefile",
27
29
  "VERSION",
28
- "examples/BulkData.avpr",
29
- "examples/complicated.icss.yaml",
30
- "examples/interop.avsc",
31
- "examples/mail.avpr",
32
- "examples/namespace.avpr",
33
- "examples/org/apache/avro/ipc/HandshakeRequest.avsc",
34
- "examples/org/apache/avro/ipc/HandshakeResponse.avsc",
35
- "examples/org/apache/avro/ipc/trace/avroTrace.avdl",
36
- "examples/org/apache/avro/ipc/trace/avroTrace.avpr",
37
- "examples/org/apache/avro/mapred/tether/InputProtocol.avpr",
38
- "examples/org/apache/avro/mapred/tether/OutputProtocol.avpr",
39
- "examples/simple.avpr",
40
- "examples/weather.avsc",
30
+ "examples/avro_examples/BulkData.avpr",
31
+ "examples/avro_examples/complicated.icss.yaml",
32
+ "examples/avro_examples/interop.avsc",
33
+ "examples/avro_examples/mail.avpr",
34
+ "examples/avro_examples/namespace.avpr",
35
+ "examples/avro_examples/org/apache/avro/ipc/HandshakeRequest.avsc",
36
+ "examples/avro_examples/org/apache/avro/ipc/HandshakeResponse.avsc",
37
+ "examples/avro_examples/org/apache/avro/ipc/trace/avroTrace.avdl",
38
+ "examples/avro_examples/org/apache/avro/ipc/trace/avroTrace.avpr",
39
+ "examples/avro_examples/org/apache/avro/mapred/tether/InputProtocol.avpr",
40
+ "examples/avro_examples/org/apache/avro/mapred/tether/OutputProtocol.avpr",
41
+ "examples/avro_examples/simple.avpr",
42
+ "examples/avro_examples/weather.avsc",
41
43
  "icss.gemspec",
42
44
  "icss_specification.textile",
43
45
  "init.rb",
44
46
  "lib/icss.rb",
45
47
  "lib/icss/brevity.rb",
46
48
  "lib/icss/code_asset.rb",
49
+ "lib/icss/core_ext.rb",
47
50
  "lib/icss/data_asset.rb",
48
51
  "lib/icss/message.rb",
49
52
  "lib/icss/old.rb",
50
53
  "lib/icss/protocol.rb",
51
54
  "lib/icss/protocol_set.rb",
52
- "lib/icss/receiver.rb",
53
- "lib/icss/receiver/acts_as_hash.rb",
54
- "lib/icss/receiver/acts_as_loadable.rb",
55
- "lib/icss/receiver/tree_diff.rb",
56
- "lib/icss/receiver/validations.rb",
57
55
  "lib/icss/sample_message_call.rb",
58
56
  "lib/icss/target.rb",
59
57
  "lib/icss/type.rb",
@@ -78,7 +76,7 @@ Gem::Specification.new do |s|
78
76
 
79
77
  if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
80
78
  s.add_runtime_dependency(%q<yajl-ruby>, ["~> 0.8.2"])
81
- s.add_runtime_dependency(%q<gorillib>, ["~> 0.0.4"])
79
+ s.add_runtime_dependency(%q<gorillib>, ["~> 0.1.0"])
82
80
  s.add_development_dependency(%q<rspec>, ["~> 2.3.0"])
83
81
  s.add_development_dependency(%q<yard>, ["~> 0.6.0"])
84
82
  s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -86,7 +84,7 @@ Gem::Specification.new do |s|
86
84
  s.add_development_dependency(%q<rcov>, [">= 0"])
87
85
  else
88
86
  s.add_dependency(%q<yajl-ruby>, ["~> 0.8.2"])
89
- s.add_dependency(%q<gorillib>, ["~> 0.0.4"])
87
+ s.add_dependency(%q<gorillib>, ["~> 0.1.0"])
90
88
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
91
89
  s.add_dependency(%q<yard>, ["~> 0.6.0"])
92
90
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
@@ -95,7 +93,7 @@ Gem::Specification.new do |s|
95
93
  end
96
94
  else
97
95
  s.add_dependency(%q<yajl-ruby>, ["~> 0.8.2"])
98
- s.add_dependency(%q<gorillib>, ["~> 0.0.4"])
96
+ s.add_dependency(%q<gorillib>, ["~> 0.1.0"])
99
97
  s.add_dependency(%q<rspec>, ["~> 2.3.0"])
100
98
  s.add_dependency(%q<yard>, ["~> 0.6.0"])
101
99
  s.add_dependency(%q<bundler>, ["~> 1.0.0"])
data/lib/icss.rb CHANGED
@@ -1,12 +1,12 @@
1
- require 'active_support/inflector' # for classify and constantize
2
- require 'active_support/core_ext/hash/keys'
3
- # require 'active_support/core_ext/hash/deep_merge'
1
+ require 'icss/core_ext' unless Object.respond_to?(:class_attribute)
2
+
3
+ require 'gorillib/receiver'
4
+ require 'gorillib/receiver/acts_as_hash'
5
+ require 'gorillib/receiver/acts_as_loadable'
6
+ require 'gorillib/receiver/validations'
7
+ require 'time' # ain't that always the way
4
8
 
5
9
  $: << File.dirname(__FILE__)
6
- require 'icss/receiver'
7
- require 'icss/receiver/acts_as_hash'
8
- require 'icss/receiver/acts_as_loadable'
9
- #
10
10
  require 'icss/validations'
11
11
  require 'icss/type'
12
12
  require 'icss/message'
@@ -0,0 +1,6 @@
1
+ require 'gorillib/object/blank'
2
+ require 'gorillib/string/inflections'
3
+ require 'gorillib/string/constantize'
4
+ require 'gorillib/array/extract_options'
5
+ require 'gorillib/hash/keys'
6
+ require 'gorillib/metaprogramming/class_attribute'
data/lib/icss/protocol.rb CHANGED
@@ -105,7 +105,7 @@ module Icss
105
105
 
106
106
  def receive_targets hsh
107
107
  self.targets = hsh.inject({}) do |target_obj_hsh, (target_name, target_info_list)|
108
- target_obj_hsh[target_name] = TargetListFactory.receive(target_name, target_info_list) # returns an arry of targets
108
+ target_obj_hsh[target_name] = TargetListFactory.receive(target_info_list, target_name) # returns an arry of targets
109
109
  target_obj_hsh
110
110
  end
111
111
  end
data/lib/icss/target.rb CHANGED
@@ -4,8 +4,8 @@ module Icss
4
4
  # Instantiates an array of target objects
5
5
  #
6
6
  class TargetListFactory
7
- def self.receive target_name, target_info_list
8
- klass = ("Icss::"+target_name.camelize+"Target").constantize
7
+ def self.receive target_info_list, target_name
8
+ klass = "Icss::#{target_name.camelize}Target".constantize
9
9
  target_info_list.map{|target_info| klass.receive(target_info)}
10
10
  end
11
11
  end
data/lib/icss/type.rb CHANGED
@@ -128,7 +128,7 @@ module Icss
128
128
  rcvr_accessor :namespace, String
129
129
  attr_accessor :parent
130
130
  # the avro base type name
131
- class_inheritable_accessor :type
131
+ class_attribute :type
132
132
  include Icss::Validations
133
133
 
134
134
  # In named types, the namespace and name are determined in one of the following ways:
@@ -385,7 +385,8 @@ module Icss
385
385
  # (do not confuse with EnumType, which is not an EnumerableType. sigh).
386
386
  #
387
387
  class EnumerableType < Type
388
- class_inheritable_accessor :type, :ruby_klass
388
+ class_attribute :type
389
+ class_attribute :ruby_klass
389
390
  def to_hash
390
391
  super.merge( :type => type.to_s )
391
392
  end
@@ -488,7 +489,7 @@ module Icss
488
489
  #
489
490
  class FixedType < NamedType
490
491
  rcvr_accessor :size, Integer, :required => true
491
- class_inheritable_accessor :ruby_klass
492
+ class_attribute :ruby_klass
492
493
  self.type = :fixed
493
494
  self.ruby_klass = String
494
495
 
data/spec/icss_spec.rb CHANGED
@@ -1,7 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'icss'
2
3
 
3
- describe "Icss" do
4
- it "fails" do
5
- fail "hey buddy, you should probably rename this file and start specing for real"
4
+ describe Icss do
5
+ Dir[ICSS_ROOT_DIR('examples/apeyeye_endpoints/**/*.icss.yaml')][0..6].each do |icss_filename|
6
+ it "loads ICSS file #{icss_filename}" do
7
+ p Icss::Protocol.receive_from_file(icss_filename)
8
+ end
6
9
  end
7
10
  end
data/spec/spec_helper.rb CHANGED
@@ -1,12 +1,22 @@
1
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__))
1
+ require 'rubygems' unless defined?(Gem)
2
+ require 'spork'
3
3
  require 'rspec'
4
- require 'icss'
5
4
 
6
- # Requires supporting files with custom matchers and macros, etc,
7
- # in ./support/ and its subdirectories.
8
- Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
5
+ ICSS_ROOT_DIR = File.expand_path(File.join(File.dirname(__FILE__),'..'))
6
+ def ICSS_ROOT_DIR *paths
7
+ File.join(::ICSS_ROOT_DIR, *paths)
8
+ end
9
+
10
+ Spork.prefork do # Must restart for changes to config / code from libraries loaded here
11
+ $LOAD_PATH.unshift(ICSS_ROOT_DIR('lib'))
12
+ $LOAD_PATH.unshift(ICSS_ROOT_DIR('spec/support'))
13
+ Dir[ICSS_ROOT_DIR('spec/support/matchers/*.rb')].each {|f| require f}
14
+
15
+ # Configure rspec
16
+ RSpec.configure do |config|
17
+ end
18
+ end
9
19
 
10
- RSpec.configure do |config|
11
-
20
+ Spork.each_run do
21
+ # This code will be run each time you run your specs.
12
22
  end
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: icss
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.4
5
+ version: 0.1.0
6
6
  platform: ruby
7
7
  authors:
8
8
  - Philip (flip) Kromer for Infochimps
@@ -31,7 +31,7 @@ dependencies:
31
31
  requirements:
32
32
  - - ~>
33
33
  - !ruby/object:Gem::Version
34
- version: 0.0.4
34
+ version: 0.1.0
35
35
  type: :runtime
36
36
  prerelease: false
37
37
  version_requirements: *id002
@@ -102,41 +102,39 @@ extra_rdoc_files:
102
102
  files:
103
103
  - .document
104
104
  - .rspec
105
+ - .watchr
106
+ - CHANGELOG.textile
105
107
  - Gemfile
106
108
  - Gemfile.lock
107
109
  - LICENSE.textile
108
110
  - README.textile
109
111
  - Rakefile
110
112
  - VERSION
111
- - examples/BulkData.avpr
112
- - examples/complicated.icss.yaml
113
- - examples/interop.avsc
114
- - examples/mail.avpr
115
- - examples/namespace.avpr
116
- - examples/org/apache/avro/ipc/HandshakeRequest.avsc
117
- - examples/org/apache/avro/ipc/HandshakeResponse.avsc
118
- - examples/org/apache/avro/ipc/trace/avroTrace.avdl
119
- - examples/org/apache/avro/ipc/trace/avroTrace.avpr
120
- - examples/org/apache/avro/mapred/tether/InputProtocol.avpr
121
- - examples/org/apache/avro/mapred/tether/OutputProtocol.avpr
122
- - examples/simple.avpr
123
- - examples/weather.avsc
113
+ - examples/avro_examples/BulkData.avpr
114
+ - examples/avro_examples/complicated.icss.yaml
115
+ - examples/avro_examples/interop.avsc
116
+ - examples/avro_examples/mail.avpr
117
+ - examples/avro_examples/namespace.avpr
118
+ - examples/avro_examples/org/apache/avro/ipc/HandshakeRequest.avsc
119
+ - examples/avro_examples/org/apache/avro/ipc/HandshakeResponse.avsc
120
+ - examples/avro_examples/org/apache/avro/ipc/trace/avroTrace.avdl
121
+ - examples/avro_examples/org/apache/avro/ipc/trace/avroTrace.avpr
122
+ - examples/avro_examples/org/apache/avro/mapred/tether/InputProtocol.avpr
123
+ - examples/avro_examples/org/apache/avro/mapred/tether/OutputProtocol.avpr
124
+ - examples/avro_examples/simple.avpr
125
+ - examples/avro_examples/weather.avsc
124
126
  - icss.gemspec
125
127
  - icss_specification.textile
126
128
  - init.rb
127
129
  - lib/icss.rb
128
130
  - lib/icss/brevity.rb
129
131
  - lib/icss/code_asset.rb
132
+ - lib/icss/core_ext.rb
130
133
  - lib/icss/data_asset.rb
131
134
  - lib/icss/message.rb
132
135
  - lib/icss/old.rb
133
136
  - lib/icss/protocol.rb
134
137
  - lib/icss/protocol_set.rb
135
- - lib/icss/receiver.rb
136
- - lib/icss/receiver/acts_as_hash.rb
137
- - lib/icss/receiver/acts_as_loadable.rb
138
- - lib/icss/receiver/tree_diff.rb
139
- - lib/icss/receiver/validations.rb
140
138
  - lib/icss/sample_message_call.rb
141
139
  - lib/icss/target.rb
142
140
  - lib/icss/type.rb
@@ -159,7 +157,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
159
157
  requirements:
160
158
  - - ">="
161
159
  - !ruby/object:Gem::Version
162
- hash: -803655258578775756
160
+ hash: 3851983979906065697
163
161
  segments:
164
162
  - 0
165
163
  version: "0"
data/lib/icss/receiver.rb DELETED
@@ -1,289 +0,0 @@
1
- require 'active_support/core_ext/class'
2
- require 'time'
3
- require 'icss/receiver/validations'
4
-
5
- # dummy type for receiving True or False
6
- class Boolean ; end unless defined?(Boolean)
7
-
8
- # Receiver lets you describe complex (even recursive!) actively-typed data models that
9
- # * are creatable or assignable from static data structures
10
- # * perform efficient type conversion when assigning from a data structure,
11
- # * but with nothing in the way of normal assignment or instantiation
12
- # * and no requirements on the initializer
13
- #
14
- # class Tweet
15
- # include Receiver
16
- # rcvr_accessor :id, Integer
17
- # rcvr_accessor :user_id, Integer
18
- # rcvr_accessor :created_at, Time
19
- # end
20
- # p Tweet.receive(:id => "7", :user_id => 9, :created_at => "20101231010203" )
21
- # # => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
22
- #
23
- # You can override receive behavior in a straightforward and predictable way:
24
- #
25
- # class TwitterUser
26
- # include Receiver
27
- # rcvr_accessor :id, Integer
28
- # rcvr_accessor :screen_name, String
29
- # rcvr_accessor :follower_ids, Array, :of => Integer
30
- # # accumulate unique follower ids
31
- # def receive_follower_ids(arr)
32
- # @follower_ids = (@follower_ids||[]) + arr.map(&:to_i)
33
- # @follower_ids.uniq!
34
- # end
35
- # end
36
- #
37
- # The receiver pattern works naturally with inheritance:
38
- #
39
- # class TweetWithUser < Tweet
40
- # rcvr_accessor :user, TwitterUser
41
- # after_receive do |hsh|
42
- # self.user_id = self.user.id if self.user
43
- # end
44
- # end
45
- # p TweetWithUser.receive(:id => 8675309, :created_at => "20101231010203", :user => { :id => 24601, :screen_name => 'bob', :follower_ids => [1, 8, 3, 4] })
46
- # => #<TweetWithUser @id=8675309, @created_at=2010-12-31 07:02:03 UTC, @user=#<TwitterUser @id=24601, @screen_name="bob", @follower_ids=[1, 8, 3, 4]>, @user_id=24601>
47
- #
48
- # TweetWithUser was able to add another receiver, applicable only to itself and its subclasses.
49
- #
50
- # The receive method works well with sparse data -- you can accumulate
51
- # attributes without trampling formerly set values:
52
- #
53
- # tw = Tweet.receive(:id => "7", :user_id => 9 )
54
- # p tw
55
- # # => #<Tweet @id=7, @user_id=9>
56
- #
57
- # tw.receive!(:created_at => "20101231010203" )
58
- # p tw
59
- # # => #<Tweet @id=7, @user_id=9, @created_at=2010-12-31 07:02:03 UTC>
60
- #
61
- # Note the distinction between an explicit nil field and a missing field:
62
- #
63
- # tw.receive!(:user_id => nil, :created_at => "20090506070809" )
64
- # p tw
65
- # # => #<Tweet @id=7, @user_id=nil, @created_at=2009-05-06 12:08:09 UTC>
66
- #
67
- # There are helpers for default and required attributes:
68
- #
69
- # class Foo
70
- # include Receiver
71
- # rcvr_accessor :is_reqd, String, :required => true
72
- # rcvr_accessor :also_reqd, String, :required => true
73
- # rcvr_accessor :has_default, String, :default => 'hello'
74
- # end
75
- # foo_obj = Foo.receive(:is_reqd => "hi")
76
- # # => #<Foo:0x00000100bd9740 @is_reqd="hi" @has_default="hello">
77
- # foo_obj.missing_attrs
78
- # # => [:also_reqd]
79
- #
80
- module Receiver
81
-
82
- RECEIVER_BODIES = {} unless defined?(RECEIVER_BODIES)
83
- RECEIVER_BODIES[Symbol] = %q{ v.nil? ? nil : v.to_sym }
84
- RECEIVER_BODIES[String] = %q{ v.to_s }
85
- RECEIVER_BODIES[Integer] = %q{ v.nil? ? nil : v.to_i }
86
- RECEIVER_BODIES[Float] = %q{ v.nil? ? nil : v.to_f }
87
- RECEIVER_BODIES[Time] = %q{ v.nil? ? nil : Time.parse(v.to_s).utc }
88
- RECEIVER_BODIES[Date] = %q{ v.nil? ? nil : Date.parse(v.to_s) }
89
- RECEIVER_BODIES[Array] = %q{ v.nil? ? nil : v }
90
- RECEIVER_BODIES[Hash] = %q{ v.nil? ? nil : v }
91
- RECEIVER_BODIES[Boolean] = %q{ v.nil? ? nil : (v.to_s.strip != "false") }
92
- RECEIVER_BODIES[NilClass] = %q{ raise "This field must be nil, but #{v} was given" unless (v.nil?) ; nil }
93
- RECEIVER_BODIES[Object] = %q{ v } # accept and love the object just as it is
94
- RECEIVER_BODIES.each do |k,b|
95
- if k.is_a?(Class)
96
- k.class_eval <<-STR, __FILE__, __LINE__ + 1
97
- def self.receive(v)
98
- #{b}
99
- end
100
- STR
101
- end
102
- end
103
-
104
- TYPE_ALIASES = {
105
- :null => NilClass,
106
- :boolean => Boolean,
107
- :string => String, :bytes => String,
108
- :symbol => Symbol,
109
- :int => Integer, :integer => Integer, :long => Integer,
110
- :time => Time, :date => Date,
111
- :float => Float, :double => Float,
112
- :hash => Hash, :map => Hash,
113
- :array => Array,
114
- } unless defined?(TYPE_ALIASES)
115
-
116
- #
117
- # modify object in place with new typecast values.
118
- #
119
- def receive! hsh
120
- raise "Can't receive (it isn't hashlike): #{hsh.inspect}" unless hsh.respond_to?(:[])
121
- self.class.receiver_attr_names.each do |attr|
122
- if hsh.has_key?(attr.to_sym) then val = hsh[attr.to_sym]
123
- elsif hsh.has_key?(attr.to_s) then val = hsh[attr.to_s]
124
- else next ; end
125
- _receive_attr attr, val
126
- end
127
- impose_defaults!(hsh)
128
- run_after_receivers(hsh)
129
- self
130
- end
131
-
132
- def unset!(attr)
133
- self.send(:remove_instance_variable, "@#{attr}") if self.instance_variable_defined?("@#{attr}")
134
- end
135
-
136
- # true if the attr is a receiver variable and it has been set
137
- def attr_set?(attr)
138
- receiver_attrs.has_key?(attr) && self.instance_variable_defined?("@#{attr}")
139
- end
140
-
141
- protected
142
- def _receive_attr attr, val
143
- self.send("receive_#{attr}", val)
144
- end
145
-
146
- def impose_defaults!(hsh)
147
- self.class.receiver_defaults.each do |attr, val|
148
- next if attr_set?(attr)
149
- self.instance_variable_set "@#{attr}", val
150
- end
151
- end
152
-
153
- def run_after_receivers(hsh)
154
- self.class.after_receivers.each do |after_receiver|
155
- self.instance_exec(hsh, &after_receiver)
156
- end
157
- end
158
- public
159
-
160
- module ClassMethods
161
-
162
- #
163
- # Returns a new instance with the given hash used to set all rcvrs.
164
- #
165
- # All args after the first are passed to the initializer.
166
- #
167
- def receive hsh, *args
168
- hsh ||= {}
169
- obj = self.new(*args)
170
- obj.receive!(hsh)
171
- end
172
-
173
- #
174
- # define a receiver attribute.
175
- # automatically generates an attr_accessor on the class if none exists
176
- #
177
- # @option [Boolean] :required - Adds an error on validation if the attribute is never set
178
- # @option [Object] :default - After any receive! operation, attribute is set to this value unless attr_set? is true
179
- # @option [Class] :of - For collections (Array, Hash, etc), the type of the collection's items
180
- #
181
- def rcvr name, type, info={}
182
- name = name.to_sym
183
- type = type_to_klass(type)
184
- class_eval <<-STR, __FILE__, __LINE__ + 1
185
- def receive_#{name}(v)
186
- self.#{name} = #{receiver_body_for(type, info)}
187
- end
188
- STR
189
- receiver_attr_names << name unless receiver_attr_names.include?(name)
190
- receiver_attrs[name] = info.merge({ :name => name, :type => type })
191
- end
192
-
193
- def after_receive &block
194
- self.after_receivers << block
195
- end
196
-
197
- def type_to_klass(type)
198
- case
199
- when type.is_a?(Class) then return type
200
- when TYPE_ALIASES.has_key?(type) then TYPE_ALIASES[type]
201
- # when (type.is_a?(Symbol) && type.to_s =~ /^[A-Z]/) then type.to_s.constantize
202
- else raise "Can\'t handle type #{type}: is it a Class or one of the TYPE_ALIASES? "
203
- end
204
- end
205
-
206
- # defines a receiver attribute, an attr_reader and an attr_writer
207
- # attr_reader is skipped if the getter method is already defined;
208
- # attr_writer is skipped if the setter method is already defined;
209
- def rcvr_accessor name, type, info={}
210
- attr_reader(name) unless method_defined?(name)
211
- attr_writer(name) unless method_defined?("#{name}=")
212
- rcvr name, type, info
213
- end
214
- # defines a receiver attribute and an attr_reader
215
- # attr_reader is skipped if the getter method is already defined.
216
- def rcvr_reader name, type, info={}
217
- attr_reader(name) unless method_defined?(name)
218
- rcvr name, type, info
219
- end
220
- # defines a receiver attribute and an attr_writer
221
- # attr_writer is skipped if the setter method is already defined.
222
- def rcvr_writer name, type, info={}
223
- attr_writer(name) unless method_defined?("#{name}=")
224
- rcvr name, type, info
225
- end
226
-
227
- #
228
- # Defines a receiver for attributes sent to receive! that are
229
- # * not defined as receivers
230
- # * attribute name does not start with '_'
231
- #
232
- # @example
233
- # class Foo ; include Receiver
234
- # rcvr_accessor :bob, String
235
- # rcvr_remaining :other_params
236
- # end
237
- # foo_obj = Foo.receive(:bob => 'hi, bob", :joe => 'hi, joe')
238
- # # => <Foo @bob='hi, bob' @other_params={ :joe => 'hi, joe' }>
239
- def rcvr_remaining name, info={}
240
- rcvr_reader name, Hash, info
241
- after_receive do |hsh|
242
- remaining_vals_hsh = hsh.except(* keys).reject!{|k,v| k.to_s =~ /^_/}
243
- self._receive_attr name, remaining_vals_hsh
244
- end
245
- end
246
-
247
- # a hash from attribute names to their default values if given
248
- def receiver_defaults
249
- defs = {}
250
- receiver_attrs.each do |name, info|
251
- defs[name] = info[:default] if info.has_key?(:default)
252
- end
253
- defs
254
- end
255
-
256
- private
257
- def receiver_body_for type, info
258
- type = type_to_klass(type)
259
- # Note that Array and Hash only need (and only get) special treatment when
260
- # they have an :of => SomeType option.
261
- case
262
- when info[:of] && (type == Array)
263
- %Q{ v.nil? ? nil : v.map{|el| #{info[:of]}.receive(el) } }
264
- when info[:of] && (type == Hash)
265
- %Q{ v.nil? ? nil : v.inject({}){|h, (el,val)| h[el] = #{info[:of]}.receive(val); h } }
266
- when Receiver::RECEIVER_BODIES.include?(type)
267
- Receiver::RECEIVER_BODIES[type]
268
- when type.is_a?(Class)
269
- %Q{v.blank? ? nil : #{type}.receive(v) }
270
- # when (type.is_a?(Symbol) && type.to_s =~ /^[A-Z]/)
271
- # # a hack so you can use a class not defined yet
272
- # %Q{v.blank? ? nil : #{type}.receive(v) }
273
- else
274
- raise("Can't receive #{type} #{info}")
275
- end
276
- end
277
- end
278
-
279
- # set up receiver attributes, and bring in methods from the ClassMethods module at class-level
280
- def self.included base
281
- base.class_eval do
282
- class_inheritable_accessor :receiver_attrs, :receiver_attr_names, :after_receivers
283
- self.receiver_attrs = {} # info about the attr
284
- self.receiver_attr_names = [] # ordered set of attr names
285
- self.after_receivers = [] # blocks to execute following receive!
286
- extend ClassMethods
287
- end
288
- end
289
- end
@@ -1,436 +0,0 @@
1
- module Receiver
2
-
3
- #
4
- # Makes a Receiver thingie behave mostly like a hash.
5
- #
6
- # By default, the hashlike methods iterate over the receiver attributes:
7
- # instance #keys delegates to self.class.keys which calls
8
- # receiver_attr_names. If you want to filter our add to the keys list, you
9
- # can just override the class-level keys method (and call super, or not):
10
- #
11
- # def self.keys
12
- # super + [:firstname, :lastname] - [:fullname]
13
- # end
14
- #
15
- # All methods are defined naturally on [], []= and has_key? -- if you enjoy
16
- #
17
- #
18
- # in addition to the below, by including Enumerable, this also adds
19
- #
20
- # #all?, #any?, #chunk, #collect, #collect_concat, #count, #cycle, #detect,
21
- # #drop, #drop_while, #each_cons, #each_entry, #each_slice,
22
- # #each_with_index, #each_with_object, #entries, #find, #find_all,
23
- # #find_index, #first, #flat_map, #grep, #group_by, #inject, #map, #max,
24
- # #max_by, #min, #min_by, #minmax, #minmax_by, #none?, #one?, #partition,
25
- # #reduce, #reverse_each, #slice_before, #sort, #sort_by, #take,
26
- # #take_while, #zip
27
- #
28
- # As opposed to hash, does *not* define
29
- #
30
- # default, default=, default_proc, default_proc=, shift
31
- # length, size, empty?, flatten, replace, keep_if, key(value)
32
- # compare_by_identity compare_by_identity? rehash, select!
33
- #
34
- # assoc rassoc
35
- #
36
- module ActsAsHash
37
-
38
- # Fake hash reader semantics: delegates to self.send(key)
39
- #
40
- # Note: indifferent access -- either of :foo or "foo" will work
41
- #
42
- def [](name)
43
- self.send(name) if keys.include?(name.to_sym)
44
- end
45
-
46
- # Fake hash writer semantics: delegates to self.send("key=", val)
47
- #
48
- # NOTE: this calls self.foo= 5, not self.receive_foo(5)
49
- # NOTE: indifferent access -- either of :foo or "foo" will work
50
- #
51
- def []=(name, val)
52
- self.send("#{name}=", val) if keys.include?(name)
53
- end
54
- alias_method(:store, :[]=)
55
-
56
- # @param key<Object> The key to check for.
57
- #
58
- # @return [Boolean] True if
59
- # * the attribute is one of this object's keys, and
60
- # * its value is non-nil OR the corresponding instance_variable is defined.
61
- #
62
- # For attributes that are virtual accessors, if its value is explicitly set
63
- # to nil then has_key? is true.
64
- #
65
- # @example
66
- # class Foo
67
- # include Receiver
68
- # include Receiver::ActsAsHash
69
- # rcvr_accessor :a, Integer
70
- # rcvr_accessor :b, String
71
- # end
72
- # foo = Foo.receive({:a => 1})
73
- # foo.has_key?(:b) # false
74
- # foo[:b] # nil
75
- # foo.b = nil
76
- # foo.has_key?(:b) # true
77
- # foo[:b] # nil
78
- #
79
- def has_key?(key)
80
- keys.include?(key) && ((not self[key].nil?) || attr_set?(key))
81
- end
82
-
83
- # @param key<Object> The key to remove
84
- #
85
- # @return [Object]
86
- # returns the value of the given attribute, and sets its new value to nil.
87
- # If there is a corresponding instance_variable, it is subsequently removed.
88
- def delete(key)
89
- val = self[key]
90
- self[key]= nil
91
- unset!(key)
92
- val
93
- end
94
-
95
- #
96
- # Convert to a hash
97
- #
98
- # Each key in #keys becomes an element in the new array if the value of its
99
- # attribute is non-nil OR the corresponding instance_variable is defined.
100
- def to_hash
101
- keys.inject({}) do |hsh, key|
102
- val = self[key]
103
- hsh[key] = val if (val || self.instance_variable_defined?("@#{key}"))
104
- hsh
105
- end
106
- end
107
-
108
- module ClassMethods
109
- # By default, the hashlike methods iterate over the receiver attributes.
110
- # If you want to filter our add to the keys list, override this method
111
- #
112
- # @example
113
- # def self.keys
114
- # super + [:firstname, :lastname] - [:fullname]
115
- # end
116
- #
117
- def keys
118
- receiver_attr_names
119
- end
120
- end
121
-
122
- # ===========================================================================
123
- #
124
- # The below methods are natural extensions of the above
125
- #
126
-
127
- # delegates to the class method. Typically you'll want to override that one,
128
- # not the instance keys
129
- def keys
130
- self.class.keys
131
- end
132
-
133
- # Returns an array consisting of the value for each attribute in
134
- # #keys, guaranteed in same order
135
- def values
136
- values_at *keys
137
- end unless method_defined?(:values)
138
-
139
- # Returns an array consisting of the value for each attribute in
140
- # allowed_keys, guaranteed in same order
141
- def values_at *allowed_keys
142
- allowed_keys.map do |k|
143
- self[k]
144
- end
145
- end
146
-
147
- # a nested array of [ key, value ] pairs. Delegates to to_hash.to_a
148
- def to_a
149
- to_hash.to_a
150
- end
151
-
152
- # @return [Hash] the object as a Hash with symbolized keys.
153
- def symbolize_keys() to_hash ; end
154
- # @return [Hash] the object as a Hash with string keys.
155
- def stringify_keys() to_hash.stringify_keys ; end
156
-
157
- # Used to provide the same interface as Hash.
158
- # @return This object unchanged.
159
- def symbolize_keys!; self end
160
-
161
- # Used to provide the same interface as Hash.
162
- # @return This object unchanged.
163
- def stringify_keys!; self end
164
-
165
- #
166
- # Return a Hash containing only values for the given keys where self.has_key?(k)
167
- #
168
- def slice *allowed_keys
169
- allowed_keys.inject({}){|h,k| h[k] = self[k] if self.has_key?(k) ; h }
170
- end
171
-
172
- # Calls block once for each key in #keys in order, passing the key and value as parameters.
173
- def each &block
174
- keys.each do |key|
175
- yield(key, self[key])
176
- end
177
- end
178
- alias_method :each_pair, :each
179
-
180
- # Calls block once for each key in #keys in order, passing the key as parameter.
181
- def each_key &block
182
- keys.each(&block)
183
- end
184
-
185
- # Calls block once for each key in #keys in order, passing the value as parameter.
186
- def each_value &block
187
- keys.each do |key|
188
- yield self[key]
189
- end
190
- end
191
-
192
- #
193
- # Analogous to Hash#merge: returns a duplicate of self where for each
194
- # element of self.keys, adopts the corresponding element of hsh if that key
195
- # is set in hsh.
196
- #
197
- # Returns a duplicate of self, but adopting the corresponding element of hsh
198
- # if that key is set in hsh. Only keys in self.keys are candidates for merging.
199
- #
200
- # With no block parameter, overwrites entries in hsh with duplicate keys
201
- # with those from other_hash.
202
- #
203
- # The block parameter semantics aren't implemented yet. If a block is
204
- # specified, it is called with each duplicate key and the values from the
205
- # two hashes. The value returned by the block is stored in the new hash.
206
- #
207
- # @example
208
- # h1 = { "a" => 100, "b" => 200 }
209
- # h2 = { "b" => 254, "c" => 300 }
210
- # h1.merge(h2) -> {"a"=>100, "b"=>254, "c"=>300}
211
- # h1.merge(h2){|k,o,n| o} -> {"a"=>100, "b"=>200, "c"=>300}
212
- # h1 -> {"a"=>100, "b"=>200}
213
- #
214
- def merge *args, &block
215
- self.dup.merge!(*args, &block)
216
- end
217
-
218
- # For all keys that are in self.keys *and* other_hash.has_key?(key),
219
- # sets the value to that from other_hash
220
- #
221
- def update other_hash, &block
222
- raise "can't handle block arg yet" if block
223
- keys.each do |key|
224
- self[key] = other_hash[key] if other_hash.has_key?(key)
225
- end
226
- self
227
- end
228
- alias_method :merge!, :update
229
-
230
- # # Returns a new hash with +self+ and +other_hash+ merged recursively.
231
- # def deep_merge(other_hash)
232
- # dup.deep_merge!(other_hash)
233
- # end
234
-
235
- # Recursively merges using receive
236
- #
237
- # Modifies the full receiver chain in-place.
238
- #
239
- # For each key in keys,
240
- # * if self's value is nil, receive the attribute.
241
- # * if self's attribute is an Array, append to it.
242
- # * if self's value responds to tree_merge!, tree merge it.
243
- # * if self's value responds_to merge!, merge! it.
244
- # * otherwise, receive the value from other_hash
245
- #
246
- def tree_merge!(other_hash)
247
- keys.each do |key|
248
- # get other's val if any
249
- if other_hash.has_key?(key.to_sym) then other_val = other_hash[key.to_sym]
250
- elsif other_hash.has_key?(key.to_s) then other_val = other_hash[key.to_s]
251
- else next ; end
252
- #
253
- self_val = self[key]
254
- # p ['receiver tree_merge', key, self_val.respond_to?(:tree_merge!), self[key], other_val]
255
- case
256
- when other_val.nil? then next
257
- when (not has_key?(key)) then _receive_attr(key, other_val)
258
- when receiver_attrs[key][:merge_as] == :hash_of_arrays
259
- self_val.merge!(other_val) do |k, v1, v2| case when v1.blank? then v2 when v2.blank? then v1 else v1 + v2 end end
260
- when self_val.is_a?(Array) then self[key] += other_val
261
- when self_val.respond_to?(:tree_merge!) then self[key] = self_val.tree_merge!(other_val)
262
- when self_val.respond_to?(:merge!) then self[key] = self_val.merge!(other_val)
263
- else _receive_attr(key, other_val)
264
- end
265
- end
266
- run_after_receivers(other_hash)
267
- self
268
- end
269
-
270
- # Searches the hash for an entry whose value == value, returning the
271
- # corresponding key. If multiple entries has this value, the key returned
272
- # will be that on one of the entries. If not found,returns nil.
273
- #
274
- # You are guaranteed that the first matching key in #keys will be the one
275
- # returned.
276
- #
277
- # @example
278
- # foo = Foo.receive( "a" => 100, "b" => 200, "c" => 100 )
279
- # foo.index(100) -> "a"
280
- # foo.index(999) -> nil
281
- #
282
- def index val
283
- keys.find{|key| self[key] == val }
284
- end
285
-
286
- # Returns a new hash created by using inverting self.to_hash. If this new
287
- # hash has duplicate values, the result will contain only one of them as a
288
- # key -- which one is not predictable.
289
- def invert
290
- to_hash.invert
291
- end
292
-
293
- # Returns true if the given value is present for some attribute in #keys
294
- def has_value? val
295
- !! index(val)
296
- end
297
- alias_method :value?, :has_value?
298
-
299
- # def include? def key? def member?
300
- alias_method :include?, :has_key?
301
- alias_method :key?, :has_key?
302
- alias_method :member?, :has_key?
303
-
304
- # Deletes every attribute for which block is true.
305
- # Returns nil if no changes were made, self otherwise.
306
- def reject!(&block)
307
- changed = false
308
- each do |key, val|
309
- if yield(key, val)
310
- changed = true
311
- delete(key)
312
- end
313
- end
314
- changed ? self : nil
315
- end
316
-
317
- # Deletes every attribute for which block is true.
318
- # Similar to reject! but returns self.
319
- def delete_if(&block)
320
- reject!(&block)
321
- self
322
- end
323
-
324
- # Deletes every attribute for which block is true.
325
- # Equivalent to self.dup.delete_if.
326
- def reject(&block)
327
- self.dup.delete_if(&block)
328
- end
329
-
330
- # deletes all attributes
331
- def clear
332
- each_key{|k| delete(k) }
333
- end
334
-
335
- # delete all attributes where the value is blank?, and return self. Contrast with compact!
336
- def compact_blank!
337
- delete_if{|k,v| v.blank? }
338
- end
339
- # delete all attributes where the value is nil?, and return self. Contrast with compact_blank!
340
- def compact!
341
- delete_if{|k,v| v.nil? }
342
- end
343
- # returns a hash with key/value pairs having nil? values removed
344
- def compact
345
- to_hash.delete_if{|k,v| v.nil? }
346
- end
347
- # returns a hash with key/value pairs having blank? values removed
348
- def compact_blank
349
- to_hash.delete_if{|k,v| v.blank? }
350
- end
351
-
352
- def self.included base
353
- base.class_eval do
354
- extend ClassMethods
355
- include Enumerable
356
- end
357
- end
358
-
359
- #
360
- # Not yet implemented
361
- #
362
-
363
- # # Returns true if has_key? is false for all attributes in #keys
364
- # def empty?
365
- # keys.all?{|key| not has_key?(key) }
366
- # end
367
- #
368
- # # The number of keys where #has_key is true
369
- # def length
370
- # keys.select{|key| has_key?(key) }.length
371
- # end
372
- # alias_method :size, :length
373
-
374
- # # @param key<Object> The key to fetch.
375
- # # @param *extras<Array> Default value.
376
- # #
377
- # # Returns a value for the given key. If the object doesn't has_key?(key),
378
- # # several options exist:
379
- # #
380
- # # * With no other arguments, it will raise an IndexError exception;
381
- # # * if default is given, then that will be returned;
382
- # # * if the optional code block is specified, then that will be run and its
383
- # # result returned.
384
- # #
385
- # # fetch does not evaluate any default values supplied when
386
- # # the hash was created -- it only looks for keys in the hash.
387
- # #
388
- # # @return [Object] The value at key or the default value.
389
- # def fetch(key, default=nil, &block)
390
- # raise ""
391
- # end
392
-
393
-
394
- end
395
- end
396
-
397
-
398
- class Hash
399
-
400
- # Recursively merges using receive
401
- #
402
- # Modifies the full receiver chain in-place.
403
- #
404
- # For each key in keys,
405
- # * if self's value is nil, receive the attribute.
406
- # * if self's attribute is an Array, append to it.
407
- # * if self's value responds to tree_merge!, deep merge it.
408
- # * if self's value responds_to merge!, merge! it.
409
- # * otherwise, receive the value from other_hash
410
- #
411
- def tree_merge!(other_hash)
412
- [self.keys, other_hash.keys].flatten.uniq.each do |key|
413
- # get other's val if any
414
- if other_hash.has_key?(key.to_sym) then other_val = other_hash[key.to_sym]
415
- elsif other_hash.has_key?(key.to_s) then other_val = other_hash[key.to_s]
416
- else next ; end
417
- #
418
- self_val = self[key]
419
- # p ['hash tree_merge', key, self_val.respond_to?(:tree_merge!), self_val, other_val]
420
- case
421
- when other_val.nil? then next
422
- when (not has_key?(key)) then self[key] = other_val
423
- when self_val.is_a?(Array) then self[key] += other_val
424
- when self_val.respond_to?(:tree_merge!) then self[key] = self_val.tree_merge!(other_val)
425
- when self_val.respond_to?(:merge!) then self[key] = self_val.merge!(other_val)
426
- else self[key] = other_val
427
- end
428
- end
429
- self
430
- end
431
-
432
- def compact_blank!
433
- reject!{|k,v| v.blank? } ; self
434
- end
435
-
436
- end
@@ -1,42 +0,0 @@
1
- module Receiver
2
- #
3
- # adds methods to load and store from json, yaml or magic
4
- #
5
- # This will require 'json' UNLESS you have already included something (so if
6
- # you want to say require 'yajl' then do that first).
7
- #
8
- module ActsAsLoadable
9
-
10
- module ClassMethods
11
- def receive_json stream
12
- receive(JSON.load(stream))
13
- end
14
-
15
- def receive_yaml stream
16
- receive(YAML.load(stream))
17
- end
18
-
19
- #
20
- # The file is loaded with
21
- # * YAML if the filename ends in .yaml or .yml
22
- # * JSON otherwise
23
- #
24
- def receive_from_file filename
25
- stream = File.open(filename)
26
- (filename =~ /.ya?ml$/) ? receive_yaml(stream) : receive_json(stream)
27
- end
28
- end
29
-
30
- def merge_from_file! filename
31
- other_obj = self.class.receive_from_file(filename)
32
- tree_merge! other_obj
33
- end
34
-
35
- # put all the things in ClassMethods at class level
36
- def self.included base
37
- require 'yaml'
38
- require 'json' unless defined?(JSON)
39
- base.extend ClassMethods
40
- end
41
- end
42
- end
@@ -1,74 +0,0 @@
1
- module Receiver
2
- def tree_diff(other)
3
- diff_hsh = {}
4
- other = other.symbolize_keys if other.respond_to?(:symbolize_keys)
5
- each do |k, v|
6
- case
7
- when v.is_a?(Array) && other[k].is_a?(Array)
8
- val = v.tree_diff(other[k])
9
- diff_hsh[k] = val unless val.blank?
10
- when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
11
- val = v.tree_diff(other[k])
12
- diff_hsh[k] = val unless val.blank?
13
- else
14
- diff_hsh[k] = v unless v == other[k]
15
- end
16
- end
17
- other_hsh = other.dup.delete_if{|k, v| has_key?(k) }
18
- diff_hsh.merge!(other_hsh)
19
- end
20
-
21
- module ActsAsHash
22
- def <=>(other)
23
- return 1 if other.blank?
24
- each_key do |k|
25
- if has_key?(k) && other.has_key?(k)
26
- cmp = self[k] <=> other[k]
27
- return cmp unless cmp == 0
28
- end
29
- end
30
- 0
31
- end
32
- end
33
- end
34
-
35
- class Array
36
- def tree_diff(other)
37
- arr = dup
38
- if other.length > arr.length then arr = arr + ([nil] * (other.length - arr.length)) end
39
- diff_ary = arr.zip(other).map do |arr_el, other_el|
40
- if arr_el.respond_to?(:tree_diff) && other_el.respond_to?(:to_hash)
41
- arr_el.tree_diff(other_el)
42
- else
43
- (arr_el == other_el) ? nil : [arr_el, other_el]
44
- end
45
- end.reject(&:blank?)
46
- end
47
- end
48
-
49
- class Hash
50
- # Returns a hash that represents the difference between two hashes.
51
- #
52
- # Examples:
53
- #
54
- # {1 => 2}.tree_diff(1 => 2) # => {}
55
- # {1 => 2}.tree_diff(1 => 3) # => {1 => 2}
56
- # {}.tree_diff(1 => 2) # => {1 => 2}
57
- # {1 => 2, 3 => 4}.tree_diff(1 => 2) # => {3 => 4}
58
- def tree_diff(other)
59
- diff_hsh = self.dup
60
- each do |k, v|
61
- case
62
- when v.is_a?(Array) && other[k].is_a?(Array)
63
- diff_hsh[k] = v.tree_diff(other[k])
64
- diff_hsh.delete(k) if diff_hsh[k].blank?
65
- when v.respond_to?(:tree_diff) && other[k].respond_to?(:to_hash)
66
- diff_hsh[k] = v.tree_diff(other[k])
67
- diff_hsh.delete(k) if diff_hsh[k].blank?
68
- else diff_hsh.delete(k) if v == other[k]
69
- end
70
- end
71
- other_hsh = other.dup.delete_if{|k, v| has_key?(k) || has_key?(k.to_s) }
72
- diff_hsh.merge!(other_hsh)
73
- end
74
- end
@@ -1,30 +0,0 @@
1
- module Receiver
2
-
3
- # An array of strings describing any ways this fails validation
4
- def validation_errors
5
- errors = []
6
- if (ma = missing_attrs).present?
7
- errors << "Missing values for {#{ma.join(",")}}"
8
- end
9
- errors
10
- end
11
-
12
- # returns a list of required but missing attributes
13
- def missing_attrs
14
- missing = []
15
- self.class.required_rcvrs.each do |name, info|
16
- missing << name if (not attr_set?(name))
17
- end
18
- missing
19
- end
20
-
21
- # methods become class-level
22
- module ClassMethods
23
-
24
- # class method gives info for all receiver attributes with required => true
25
- def required_rcvrs
26
- receiver_attrs.select{|name, info| info[:required] }
27
- end
28
- end
29
-
30
- end