liufengyun / hashdiff Goto Github PK
View Code? Open in Web Editor NEWHashdiff is a ruby library to to compute the smallest difference between two hashes
License: MIT License
Hashdiff is a ruby library to to compute the smallest difference between two hashes
License: MIT License
Similar to :strip
and :numeric_tolerance
I think it would be useful to have an option to make string comparisons case insensitive. I'm happy to provide a PR if there would be interest in adding this feature.
BetterHash = Class.new Hash
EvenBetterHash = Class.new Hash
HashDiff.diff(BetterHash.new, EvenBetterHash.new)
=> [["~", "", {}, {}]]
HashDiff.diff({}, BetterHash.new)
=> []
HashDiff.diff(BetterHash.new, {})
=> [["~", "", {}, {}]]
I wonder when we can compare two objects. When they inherit from the same class or they have to be instances of the same class?
Hello,
Actually Hashdiff can show +
, -
and ~
.
Is it possible to also show same counts in the diff as =
?
example output:
['-', 'b', 2], ['+', 'a', 4], ['=', 'c', 3, 3], ['~', 'd', 3, 8]
Thanks.
I have a use-case where I often need to compare 2 hashes where I don't care about the values of particular keys. For example ones which contain timestamps, or the following example which contain a 'duration' key (representing milliseconds):
h1 =
{
"fonts": {
"value": [],
"duration": 93
},
"domBlockers": {
"duration": 0
},
"fontPreferences": {
"value": {
"default": 146,
"apple": 146,
"serif": 146,
"sans": 144,
"mono": 119,
"min": 10,
"system": 144
},
"duration": 117
},
# ...
}
h2 =
{
"fonts": {
"value": [],
"duration": 78
},
"domBlockers": {
"duration": 0
},
"fontPreferences": {
"value": {
"default": 146,
"apple": 146,
"serif": 146,
"sans": 144,
"mono": 119,
"min": 10,
"system": 146
},
"duration": 134
},
# ...
}
I can ignore differences in the 'duration' values with a block:
diff = Hashdiff.diff(h1, h2) { |path, _e, _a| true if path.split('.').last == 'duration' }
#=> [['~', 'fontPreferences.value.system', 144, 146]]
But I'd love to be able to write something like this, where :ignore_keys can take a single value or array of keys to ignore:
diff = Hashdiff.diff(h1, h2, ignore_keys: 'duration')
The logic would be the same as above, i.e. ignored keys would have to be at the end of each path. My guess is this may be a common enough use-case that a shorthand option like this would be valuable to users.
I might have a stab at a PR if you like the idea. Awesome library btw.
bundler rake release
automatically adds them and would help building urls like v0.3.8...v0.3.9 to see changes
Hello. I find the following behavior (using Hashdiff 1.0.1) somewhat unexpected:
Hashdiff.diff(%w[a b c], %w[a b d])
=> [["-", "[2]", "c"], ["+", "[2]", "d"]]
Would not it be better to get
Hashdiff.diff(%w[a b c], %w[a b d])
=> [["~", "[2]", "c", "d"]]
?
Is there an option to get this behavior ?
Thanks for reading!
please change similarity: to :similarity =>
and prefix: to :prefix =>
/usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `gem_original_require': /usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:30: odd number list for Hash (SyntaxError)
opts = {similarity: 0.3}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:30: syntax error, unexpected ':', expecting '}'
opts = {similarity: 0.3}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:34: odd number list for Hash
opts = {similarity: 0.5}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:34: syntax error, unexpected ':', expecting '}'
opts = {similarity: 0.5}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:38: odd number list for Hash
opts = {similarity: 0.8}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:38: syntax error, unexpected ':', expecting '}'
opts = {similarity: 0.8}.merge!(options)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:107: syntax error, unexpected ':', expecting ')'
...2[pair[1]], opts.merge(prefix: "#{opts[:prefix]}[#{pair[0]}]...
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:107: syntax error, unexpected ')', expecting kEND
...#{opts[:prefix]}[#{pair[0]}]")))
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:118: syntax error, unexpected kELSIF, expecting kEND
elsif obj1.is_a?(Hash)
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:148: syntax error, unexpected ':', expecting ')'
...], obj2[k], opts.merge(prefix: "#{prefix}#{k}"))) }
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:148: syntax error, unexpected ')', expecting '}'
...merge(prefix: "#{prefix}#{k}"))) }
^
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:162: syntax error, unexpected kELSE, expecting '}'
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:165: syntax error, unexpected kEND, expecting '}'
/usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff/diff.rb:227: syntax error, unexpected kEND, expecting '}'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require'
from /usr/lib/ruby/gems/1.8/gems/hashdiff-0.2.1/lib/hashdiff.rb:3
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `gem_original_require'
from /usr/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:36:in `require'
from ./shema_diff.rb:6
require 'hashdiff'
Hashdiff::VERSION
#=> "1.1.0"
The following snippet fails to ignore top level key :a
a = { a: 4, b: {a: 5, c: 6} }
b = { b: {a: 7, c: 3} }
diff = Hashdiff.diff(a, b, ignore_keys: :a)
#=> [["-", "a", 4], ["~", "b.c", 6, 3]]
Shouldn't diff
be expected to be the below?
[["~", "b.c", 6, 3]]
The documentation on ignore_keys
states that:
The
:ignore_keys
option allows you to specify one or more keys to ignore, which defaults to[]
(none). Ignored keys are ignored at all levels.
Here line where :ignore_keys
happens (in Hashdiff::ComparableHashes::call
):
opts[:ignore_keys].each { |k| common_keys.delete k }
added_keys
and deleted_keys
don't seem to have the exclusion applied.
As you can see in the code snippet below, there is difference in the diff even if the similarity
value is changed from 0.0 to 0.000000001. And the result obtained using similarity: 0.0
seems unexpected.
hash1 = { a: "a", b_arr: [ { name: "b1" }, { name: "b2" } ] }
hash2 = { a: "a", b_arr: [ { name: "b1" } ] }
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.0)
=> [["~", "b_arr[0].name", "b1", "b2"], ["+", "b_arr[0]", {:name=>"b1"}]]
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.1)
=> [["+", "b_arr[1]", {:name=>"b2"}]]
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.01)
=> [["+", "b_arr[1]", {:name=>"b2"}]]
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.001)
=> [["+", "b_arr[1]", {:name=>"b2"}]]
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.0001)
=> [["+", "b_arr[1]", {:name=>"b2"}]]
diff1 = HashDiff.diff(hash2, hash1, similarity: 0.000000001)
=> [["+", "b_arr[1]", {:name=>"b2"}]]
I find this library to be extremely useful, however its missing a usecase that i badly need.
As you mentioned in the documentation, if we have array inside hash, it will need to be sorted before we can compare, else it will show them as diff.
Can we please add a option to sort arrays before comparing them? is there any specific reason why its not provided?
Thanks
Rajat JIndal
Thanks for great plugin, I find it's incredibly useful in my current project. I don't know if there is any internal API that I can get the index of changed diff items only ?
For example :
instead of showing this :
[["-", "[2]", {:kills=>3, :deaths=>3}], ["-", "[1]", {:kills=>4, :deaths=>3}], ["-", "[0]", {:kills=>8, :deaths=>5}], ["-", "[1]", {:kills=>1, :deaths=>4}], ["+", "[1]", {:kills=>3, :deaths=>1}], ["+", "[2]", {:kills=>2, :deaths=>1}], ["+", "[3]", {:kills=>0, :deaths=>2}], ["+", "[4]", {:kills=>4, :deaths=>2}]]
I just want to get this :
[1,2,3,4]
diffing anything that has nested arrays is ugly when using lcs ... and on top of that it is slow
how about making the linear solver the default ?
before:
-graphs[1]
{:definition=>{:viz=>"timeseries", :requests=>[{:q=>"sum:docker.mem.rss{kube_project:gcb-vulnerability-logger} by {kube_namespace}", :conditional_formats=>[], :type=>"area"}], :autoscale=>true}, :title=>"Memory"} ->
nil
+graphs[1]
nil ->
{:title=>"Memory", :definition=>{:viz=>"timeseries", :requests=>[{:q=>"sum:docker.mem.rss{kube_project:gcb-vulnerability-logger,$environment} by {kube_namespace}", :type=>"area", :conditional_formats=>[]}], :autoscale=>true}}
after:
~graphs[1].definition.requests[0].q
"sum:docker.mem.rss{kube_project:gcb-vulnerability-logger} by {kube_namespace}" ->
"sum:docker.mem.rss{kube_project:gcb-vulnerability-logger,$environment} by {kube_namespace}"
Very useful gem. However, in my case the hash is really nested with arrays and hashes inside.
hash1 = {
a: "a1",
b: {
b1: "b1",
b2: "b2",
b3: "b2"
},
c: [
{
c1: "c1",
c2: [
{
c21: "c21",
c22: "c22",
c23: "c23"
}
]
}
]
}
hash2 = {
a: "a1",
b: {
b1: "b1",
b2: "b2",
b3: "b2"
},
c: [
{
c1: "c1",
c2: [
{
c21: "c21",
c22: "c222",
c23: "c23"
}
]
}
]
}
diff = HashDiff.diff(hash1, hash2)
=> [["-", "c[0]", {:c1=>"c1", :c2=>[{:c21=>"c21", :c22=>"c22", :c23=>"c23"}]}], ["+", "c[0]", {:c1=>"c1", :c2=>[{:c21=>"c21", :c22=>"c222", :c23=>"c23"}]}]]
HashDiff.diff({a: 1, b: 3}, {b: 2}) do |path, obj1, obj2|
fail 'Will never see a' if path == 'a'
puts 'Will see b because it is shared' if path == 'b'
end
The reason I would like this is because I'd like to set up an 'ignore' in the unshared portion of the first object.
Hello there.
I've been using your gem to compare large hashes of arrays with a custom comparison method and it's been working great so far.
After upgrading to 0.3.4, custom comparison seems to be broken. If you test your own example from the README:
a = {a:'car', b:['boat', 'plane'] }
b = {a:'bus', b:['truck', ' plan'] }
diff = HashDiff.diff(a, b) do |path, obj1, obj2|
case path
when 'b[*]'
obj1.length == obj2.length
end
end
diff.should == [["~", "a", "car", "bus"], ["~", "b[1]", "plane", " plan"], ["-", "b[0]", "boat"], ["+", "b[0]", "truck"]]
you'll get a different output:
diff
#=> [["~", "a", "car", "bus"], ["-", "b[1]", "plane"], ["-", "b[0]", "boat"], ["+", "b[0]", "truck"], ["+", "b[1]", " plan"]]
Has the API for custom comparison changed in the meantime and the docs are outdated, or is this a bug?
Thanks for the great work so far :-)
I have 2 hashes, both with a "created_at" attribute, they are the same, however, that's the behavior I get from the gem when I try to compare both:
[1] > new[:created_at]
=> "2017-08-28T12:12:22.760Z"
[2] > current[:created_at]
=> "2017-08-28T12:12:22.760Z"
[3] > current[:created_at] == new[:created_at]
=> true
[4] > HashDiff.diff(new, current)
=> [["~", "created_at", "2017-08-28T12:12:22.760Z", "2017-08-28T12:12:22.760Z"]]
The timestamps are serialized the same way, but one is a string and the other is a time.
Eitherway, if the comparinson is not strict we should declare as equal.
just found that strangeness ... I think the default should "exact", if anyone wants "fuzzy" they can add that ...
xml_doc = Nokogiri::XML(response.body)
input_hash = Hash.from_xml(xml.to_s)
puts input_hash
puts "*" * 120
response_hash = Hash.from_xml(Nokogiri::XML(response.body).to_s)
puts response_hash.to_json
And I am sending input hash and response hash for diff = HashDiff.best_diffinput_hash, response_hash)
I am seeing that even there are elements present I could not get them compared
here I am giving input JSON and response JSON
{"requisition_header":{"requested_by":{"login":"coupasupport"},"department":{"name":"Marketing"},"pcard":null,"ship_to_address":{"name":"Address_1435124879"},"justification":null,"attachments":[],"requisition_lines":[{"item":{"name":"RISC Server with 4 CPU and 16Gb RAM"},"line_num":1,"need_by_date":"2010-09-23T07:00:00.000Z","account":{"code":"SF-Marketing-Indirect","account_type":{"name":"Ace Corporate"}}}]}}
{"requisition_header":{"id":5545,"created_at":"2015-06-24T05:48:19.000Z","updated_at":"2015-06-24T05:48:19.000Z","buyer_note":null,"justification":null,"need_by_date":null,"reject_reason_comment":null,"status":"draft","submitted_at":null,"exported":false,"ship_to_attention":"Coupa Support","total":"2049.0","mobile_total":"2049.0","custom_field_4":null,"custom_field_3":null,"zcust":null,"custom_field_1":null,"currency":{"id":1,"code":"USD","updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"department":{"id":1,"created_at":"2008-10-27T20:00:48.000Z","updated_at":"2015-05-15T12:35:14.000Z","name":"Marketing","active":true,"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"requested_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"ship_to_address":{"id":31420,"created_at":"2015-06-24T05:48:16.000Z","updated_at":"2015-06-24T05:48:16.000Z","name":"Address_1435124879","location_code":null,"street1":"2 W 5th Ave","street2":"Suite 300","city":"San Mateo","state":"CA","postal_code":"94404","attention":null,"active":true,"business_group_name":"Everyone","vat_number":null,"country":{"id":223,"code":"US","name":"United States"},"created_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"attachments":[],"requisition_lines":[{"id":8782,"created_at":"2015-06-24T05:48:19.000Z","updated_at":"2015-06-24T05:48:19.000Z","description":"RISC Server with 4 CPU and 16Gb RAM","line_num":1,"need_by_date":"2010-09-23T07:00:00.000Z","order_line_id":null,"receipt_required":null,"source_part_num":null,"status":null,"sub_line_num":null,"supp_aux_part_num":null,"total":"2049.0","source_type":"Catalog Item","line_type":"RequisitionAmountLine","unit_price":"2049.0","prepaid":null,"custom_field_5":null,"cf1":null,"custom_field_4":null,"user_1":null,"account":{"id":13,"created_at":"2008-10-27T20:20:36.000Z","updated_at":"2015-06-24T05:42:40.000Z","name":"San Francisco - Marketing, Indirect","code":"SF-Marketing-Indirect","active":true,"segment_1":"SF","segment_2":"Marketing","segment_3":"Indirect","segment_4":null,"segment_5":null,"segment_6":null,"segment_7":null,"segment_8":null,"segment_9":null,"segment_10":null,"segment_11":null,"segment_12":null,"segment_13":null,"segment_14":null,"segment_15":null,"segment_16":null,"segment_17":null,"segment_18":null,"segment_19":null,"segment_20":null,"account_type":{"id":1,"created_at":"2008-10-27T20:10:01.000Z","updated_at":"2015-06-18T08:06:26.000Z","name":"Ace Corporate","active":true,"currency":{"id":1,"code":"USD","updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"primary_contact":{"id":14,"created_at":"2008-10-27T20:10:01.000Z","updated_at":"2015-05-14T06:40:12.000Z","email":"[email protected]","name_prefix":null,"name_suffix":null,"name_additional":null,"name_given":"xxx","name_family":"yyy","name_fullname":null,"notes":null,"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"primary_address":{"id":31075,"created_at":"2008-10-27T20:10:01.000Z","updated_at":"2015-05-14T06:40:12.000Z","name":"test address name","location_code":null,"street1":"Line1","street2":"Line2","city":"City","state":"State","postal_code":"151515","attention":null,"active":true,"business_group_name":null,"vat_number":null,"country":{"id":22,"code":"BE","name":"Belgium"},"vat_country":{"id":22,"code":"BE","name":"Belgium"},"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"account_allocations":[],"item":{"id":230,"created_at":"2012-12-12T21:40:06.000Z","updated_at":"2015-06-24T05:44:30.000Z","description":"The minimum requirement for this bid is:A blade enclosure cost (to include all components for it to work).A server blade to the minimum specification: 2 x 72GB Hard Disk; 2 x 100mbps Ethernet ports.The server blade (preferable) or the blade enclosure must have a slot for either a 1GB Ethernet card with 2 ports or a fibre Channel (HBA) with 2 ports.The cost of the blade enclosure and server blade must include 1 years maintenance to the level indicated in the RFP.The optional cost for years 2 and 3 maintenance is to the level indicated in the RFP.The installation services cost must include, un-boxing of the product, complete installation to agreed standards, removal of boxes from site.","item_number":null,"name":"RISC Server with 4 CPU and 16Gb RAM","active":false,"storage_quantity":null,"consumption_quantity":null,"field":null,"commodity":{"id":10,"created_at":"2011-04-12T16:28:24.000Z","updated_at":"2011-04-12T21:58:19.000Z","active":true,"name":"Hardware","com_man":null,"custom_field_4":null,"custom_field_3":null,"gl_code":null,"acct":"Assets","parent":{"id":2,"created_at":"2008-10-27T20:04:00.000Z","updated_at":"2015-05-14T05:57:46.000Z","active":true,"name":"IT","com_man":null,"custom_field_4":null,"custom_field_3":null,"gl_code":null,"acct":"Indirect","created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"created_by":{"id":16,"login":"krhine","email":"[email protected]","employee_number":null,"firstname":"Kevin","lastname":"Rhine","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"uom":{"id":1,"created_at":null,"updated_at":"2015-06-18T08:06:27.000Z","code":"EA","name":"Each","allowable_precision":0,"active":true,"updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"reorder_alerts":[],"created_by":{"id":1,"login":"coupasupport","email":"[email protected]","employee_number":null,"firstname":"Coupa","lastname":"Support","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"currency":{"id":1,"code":"USD","updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"payment_term":{"id":1,"created_at":null,"updated_at":"2015-06-16T07:23:04.000Z","code":"Net 30","description":null,"days_for_net_payment":2,"days_for_discount_payment":null,"discount_rate":null,"active":true,"content_groups":[{"id":1,"created_at":"2006-08-28T02:40:04.000Z","updated_at":"2015-06-17T09:43:58.000Z","name":"Everyone","description":"All users can see documents assigned to this group","updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}}],"updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"shipping_term":{"id":1,"created_at":null,"updated_at":"2015-05-15T06:27:25.000Z","code":"Standard","description":null,"active":true,"content_groups":[{"id":1,"created_at":"2006-08-28T02:40:04.000Z","updated_at":"2015-06-17T09:43:58.000Z","name":"Everyone","description":"All users can see documents assigned to this group","updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}}],"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}},"asset_tags":[],"attachments":[],"created_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}}],"approvals":[],"created_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"updated_by":{"id":494,"login":"bhaskar","email":"[email protected]","employee_number":null,"firstname":"Bhaskar","lastname":"K","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null},"mobile_currency":{"id":1,"code":"USD","updated_by":{"id":1528,"login":"test","email":"[email protected]","employee_number":null,"firstname":"test","lastname":"key","salesforce_id":null,"custom_field_12":null,"custom_field_11":null,"date":null,"dropdown":null,"radio":null,"custom_field_8":null,"bussiness_unit":null,"purchase":null,"custom_field_3":null,"number_oksana":null,"gpo_entity":null}}}}
-requisition_header.pcard
+requisition_header.requested_by.id1
+requisition_header.requested_by.emailupgrade+coupasupport@coupa.com
+requisition_header.requested_by.employee_number
+requisition_header.requested_by.firstnameCoupa
+requisition_header.requested_by.lastnameSupport
+requisition_header.requested_by.salesforce_id
+requisition_header.requested_by.custom_field_12
+requisition_header.requested_by.custom_field_11
+requisition_header.requested_by.date
+requisition_header.requested_by.dropdown
+requisition_header.requested_by.radio
+requisition_header.requested_by.custom_field_8
+requisition_header.requested_by.bussiness_unit
+requisition_header.requested_by.purchase
+requisition_header.requested_by.custom_field_3
+requisition_header.requested_by.number_oksana
+requisition_header.requested_by.gpo_entity
+requisition_header.department.id1
+requisition_header.department.created_at2008-10-27T20:00:48.000Z
+requisition_header.department.updated_at2015-05-15T12:35:14.000Z
+requisition_header.department.activetrue
+requisition_header.department.created_by{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
+requisition_header.department.updated_by{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
+requisition_header.ship_to_address.id31420
+requisition_header.ship_to_address.created_at2015-06-24T05:48:16.000Z
+requisition_header.ship_to_address.updated_at2015-06-24T05:48:16.000Z
+requisition_header.ship_to_address.location_code
+requisition_header.ship_to_address.street12 W 5th Ave
+requisition_header.ship_to_address.street2Suite 300
+requisition_header.ship_to_address.citySan Mateo
+requisition_header.ship_to_address.stateCA
+requisition_header.ship_to_address.postal_code94404
+requisition_header.ship_to_address.attention
+requisition_header.ship_to_address.activetrue
+requisition_header.ship_to_address.business_group_nameEveryone
+requisition_header.ship_to_address.vat_number
+requisition_header.ship_to_address.country{"id"=>223, "code"=>"US", "name"=>"United States"}
+requisition_header.ship_to_address.created_by{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
+requisition_header.ship_to_address.updated_by{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
-requisition_header.requisition_lines[0]{"item"=>{"name"=>"RISC Server with 4 CPU and 16Gb RAM"}, "line_num"=>1, "need_by_date"=>"2010-09-23T07:00:00.000Z", "account"=>{"code"=>"SF-Marketing-Indirect", "account_type"=>{"name"=>"Ace Corporate"}}}
+requisition_header.requisition_lines[0]{"id"=>8782, "created_at"=>"2015-06-24T05:48:19.000Z", "updated_at"=>"2015-06-24T05:48:19.000Z", "description"=>"RISC Server with 4 CPU and 16Gb RAM", "line_num"=>1, "need_by_date"=>"2010-09-23T07:00:00.000Z", "order_line_id"=>nil, "receipt_required"=>nil, "source_part_num"=>nil, "status"=>nil, "sub_line_num"=>nil, "supp_aux_part_num"=>nil, "total"=>"2049.0", "source_type"=>"Catalog Item", "line_type"=>"RequisitionAmountLine", "unit_price"=>"2049.0", "prepaid"=>nil, "custom_field_5"=>nil, "cf1"=>nil, "custom_field_4"=>nil, "user_1"=>nil, "account"=>{"id"=>13, "created_at"=>"2008-10-27T20:20:36.000Z", "updated_at"=>"2015-06-24T05:42:40.000Z", "name"=>"San Francisco - Marketing, Indirect", "code"=>"SF-Marketing-Indirect", "active"=>true, "segment_1"=>"SF", "segment_2"=>"Marketing", "segment_3"=>"Indirect", "segment_4"=>nil, "segment_5"=>nil, "segment_6"=>nil, "segment_7"=>nil, "segment_8"=>nil, "segment_9"=>nil, "segment_10"=>nil, "segment_11"=>nil, "segment_12"=>nil, "segment_13"=>nil, "segment_14"=>nil, "segment_15"=>nil, "segment_16"=>nil, "segment_17"=>nil, "segment_18"=>nil, "segment_19"=>nil, "segment_20"=>nil, "account_type"=>{"id"=>1, "created_at"=>"2008-10-27T20:10:01.000Z", "updated_at"=>"2015-06-18T08:06:26.000Z", "name"=>"Ace Corporate", "active"=>true, "currency"=>{"id"=>1, "code"=>"USD", "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "primary_contact"=>{"id"=>14, "created_at"=>"2008-10-27T20:10:01.000Z", "updated_at"=>"2015-05-14T06:40:12.000Z", "email"=>"[email protected]", "name_prefix"=>nil, "name_suffix"=>nil, "name_additional"=>nil, "name_given"=>"xxx", "name_family"=>"yyy", "name_fullname"=>nil, "notes"=>nil, "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "primary_address"=>{"id"=>31075, "created_at"=>"2008-10-27T20:10:01.000Z", "updated_at"=>"2015-05-14T06:40:12.000Z", "name"=>"test address name", "location_code"=>nil, "street1"=>"Line1", "street2"=>"Line2", "city"=>"City", "state"=>"State", "postal_code"=>"151515", "attention"=>nil, "active"=>true, "business_group_name"=>nil, "vat_number"=>nil, "country"=>{"id"=>22, "code"=>"BE", "name"=>"Belgium"}, "vat_country"=>{"id"=>22, "code"=>"BE", "name"=>"Belgium"}, "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "account_allocations"=>[], "item"=>{"id"=>230, "created_at"=>"2012-12-12T21:40:06.000Z", "updated_at"=>"2015-06-24T05:44:30.000Z", "description"=>"The minimum requirement for this bid is:A blade enclosure cost (to include all components for it to work).A server blade to the minimum specification: 2 x 72GB Hard Disk; 2 x 100mbps Ethernet ports.The server blade (preferable) or the blade enclosure must have a slot for either a 1GB Ethernet card with 2 ports or a fibre Channel (HBA) with 2 ports.The cost of the blade enclosure and server blade must include 1 years maintenance to the level indicated in the RFP.The optional cost for years 2 and 3 maintenance is to the level indicated in the RFP.The installation services cost must include, un-boxing of the product, complete installation to agreed standards, removal of boxes from site.", "item_number"=>nil, "name"=>"RISC Server with 4 CPU and 16Gb RAM", "active"=>false, "storage_quantity"=>nil, "consumption_quantity"=>nil, "field"=>nil, "commodity"=>{"id"=>10, "created_at"=>"2011-04-12T16:28:24.000Z", "updated_at"=>"2011-04-12T21:58:19.000Z", "active"=>true, "name"=>"Hardware", "com_man"=>nil, "custom_field_4"=>nil, "custom_field_3"=>nil, "gl_code"=>nil, "acct"=>"Assets", "parent"=>{"id"=>2, "created_at"=>"2008-10-27T20:04:00.000Z", "updated_at"=>"2015-05-14T05:57:46.000Z", "active"=>true, "name"=>"IT", "com_man"=>nil, "custom_field_4"=>nil, "custom_field_3"=>nil, "gl_code"=>nil, "acct"=>"Indirect", "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "created_by"=>{"id"=>16, "login"=>"krhine", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Kevin", "lastname"=>"Rhine", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "uom"=>{"id"=>1, "created_at"=>nil, "updated_at"=>"2015-06-18T08:06:27.000Z", "code"=>"EA", "name"=>"Each", "allowable_precision"=>0, "active"=>true, "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "reorder_alerts"=>[], "created_by"=>{"id"=>1, "login"=>"coupasupport", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Coupa", "lastname"=>"Support", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "currency"=>{"id"=>1, "code"=>"USD", "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "payment_term"=>{"id"=>1, "created_at"=>nil, "updated_at"=>"2015-06-16T07:23:04.000Z", "code"=>"Net 30", "description"=>nil, "days_for_net_payment"=>2, "days_for_discount_payment"=>nil, "discount_rate"=>nil, "active"=>true, "content_groups"=>[{"id"=>1, "created_at"=>"2006-08-28T02:40:04.000Z", "updated_at"=>"2015-06-17T09:43:58.000Z", "name"=>"Everyone", "description"=>"All users can see documents assigned to this group", "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}], "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "shipping_term"=>{"id"=>1, "created_at"=>nil, "updated_at"=>"2015-05-15T06:27:25.000Z", "code"=>"Standard", "description"=>nil, "active"=>true, "content_groups"=>[{"id"=>1, "created_at"=>"2006-08-28T02:40:04.000Z", "updated_at"=>"2015-06-17T09:43:58.000Z", "name"=>"Everyone", "description"=>"All users can see documents assigned to this group", "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}], "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}, "asset_tags"=>[], "attachments"=>[], "created_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}, "updated_by"=>{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}
+requisition_header.id5545
+requisition_header.created_at2015-06-24T05:48:19.000Z
+requisition_header.updated_at2015-06-24T05:48:19.000Z
+requisition_header.buyer_note
+requisition_header.need_by_date
+requisition_header.reject_reason_comment
+requisition_header.statusdraft
+requisition_header.submitted_at
+requisition_header.exportedfalse
+requisition_header.ship_to_attentionCoupa Support
+requisition_header.total2049.0
+requisition_header.mobile_total2049.0
+requisition_header.custom_field_4
+requisition_header.custom_field_3
+requisition_header.zcust
+requisition_header.custom_field_1
+requisition_header.currency{"id"=>1, "code"=>"USD", "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}
+requisition_header.approvals[]
+requisition_header.created_by{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
+requisition_header.updated_by{"id"=>494, "login"=>"bhaskar", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"Bhaskar", "lastname"=>"K", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}
+requisition_header.mobile_currency{"id"=>1, "code"=>"USD", "updated_by"=>{"id"=>1528, "login"=>"test", "email"=>"[email protected]", "employee_number"=>nil, "firstname"=>"test", "lastname"=>"key", "salesforce_id"=>nil, "custom_field_12"=>nil, "custom_field_11"=>nil, "date"=>nil, "dropdown"=>nil, "radio"=>nil, "custom_field_8"=>nil, "bussiness_unit"=>nil, "purchase"=>nil, "custom_field_3"=>nil, "number_oksana"=>nil, "gpo_entity"=>nil}}
Note:
-requisition_header.requisition_lines[0]
present in both input and output JSON but not compared..
Please revert me with solution asap.
Thanks,
Bhaskar
This is taken straight from the official documentation: https://rubydoc.info/gems/hashdiff#ignore_keys
a = { a: 1, b: { d: 2, a: 3 }, c: 4 }
b = { a: 2, b: { d: 2, a: 7 }, c: 5 }
Hashdiff.diff(a, b, ignore_keys: :a)
Thanks to the warning from #65, I am now aware of the HashDiff
/Hashdiff
conflict. I have updated my code to use the correct Hashdiff
constant.
Now that Iโve done the work to fix the issue, how do I turn the warning off?
I'm not sure if I understand properly your code, but I feel it would be great to allow to define custom similar?
method.
And that's my use case: I have an array of serialized Rails models. In this case similar?
should use object's ids rather than comparing their content.
before = [{id: 1, name: "Peter"}, {id: 2, name: "John"}]
after = [{id: 2, name: "Peter"}, {id: 3, name: "Ann"}]
# current behavior
HashDiff.diff(before, after, similarity: 0.5)
# [
# ["~", "[0].id", 1, 2], ["-", "[1]", {:id=>2, :name=>"John"}],
# ["+", "[1]", {:id=>3, :name=>"Ann"}]
# ]
# my proposition
HashDiff.diff(before, after, similar: -> (a, b, options) { a[:id] === b[:id] })
# [
# ["~", "[1].name", "John", "Peter"],
# ["-", "[0]", {:id=>1, :name=>"Peter"}],
# ["+", "[1]", {:id=>3, :name=>"Ann"}]
# ]
Of course I can get the same results by modifying HashDiff.similar?
method (which I did), but it would be nice if I could do the same thing locally โ just like custom comparison method.
It's just an idea, maybe a bad one, since I don't really understand this whole similarity mechanism. ๐ Thank you for this gem, it's elegant and very useful!
a = {a:'car', b:['truck', 'plane'] }
b = {a:'bus', b:['truck', ' plan'] }
diff = HashDiff.diff(a, b) do |path, obj1, obj2|
case path
when 'b[*]'
true
end
end
=> [["~", "a", "car", "bus"], ["-", "b[1]", "plane"], ["+", "b[1]", " plan"]]
vs.
a = {a:'car', b:['truck', 'plane'] }
b = {a:'bus', b:['truck', ' plan'] }
diff = HashDiff.diff(a, b) do |path, obj1, obj2|
case path
when 'b[*]'
false
end
end
=> [["~", "a", "car", "bus"], ["-", "b[1]", "plane"], ["+", "b[1]", " plan"]]
vs.
a = {a:'car', b:['truck', 'plane'] }
b = {a:'bus', b:['truck', ' plan'] }
diff = HashDiff.diff(a, b) do |path, obj1, obj2|
case path
when 'b[*]'
raise "#{obj1} == #{obj2}"
end
end
=> [["~", "a", "car", "bus"], ["-", "b[1]", "plane"], ["+", "b[1]", " plan"]]
It doesn't seem to make any difference what goes in the block as a custom comparator.
Also, when I do a puts
inside the block to try and figure out what is going on there is no output?!
The raise
is silently rescued?!
RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.
via e.g.
spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']
Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.
There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.
I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!
Appendix:
If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies
p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.
Note: If you've upgraded hashdiff
, made sure that you've upgraded your references, and want to silence the warnings, you can manually opt-in to the 1.0
beta by following the instructions here
The name of this gem is hashdiff
. There is another gem named hash_diff
. When they are both required via dependencies, errors are thrown. Additionally since the behavior is different, gems that are relying on hashdiff
's behavior, may get hash_diff
's behavior and vice versa.
The reason for the conflict is because, based on ruby conventions, _
's are the separators for camel case.
So:
'hashdiff'.camelize # => "Hashdiff"
'hash_diff'.camelize # => "HashDiff"
Unfortunately instead of making the base module of this gem Hashdiff
, it's called HashDiff
and it conflicts.
From Readme
require 'hashdiff' #=> true
a = {a: 3} #=> {:a=>3}
b = {a: {a1: 1, a2: 2}} #=> {:a=>{:a1=>1, :a2=>2}}
diff = HashDiff.diff(a, b) #=> [["~", "a", 3, {:a1=>1, :a2=>2}]]
HashDiff.patch!(a, diff) #=> {:a=>3, "a"=>{:a1=>1, :a2=>2}}
rspec files check only string keys
Tried with 1.0.0
release and below is against master
branch:
script
require 'awesome_print'
require 'hashdiff'
a = {"order"=>1, "index_patterns"=>["logstash-cloudtrail*"], "settings"=>{"index"=>{"lifecycle"=>{"name"=>"logging_policy", "rollover_alias"=>"logstash-cloudtrail-write"}, "routing"=>{"allocation"=>{"require"=>{"node_type"=>"hot"}}}, "mapping"=>{"total_fields"=>{"limit"=>8000}, "ignore_malformed"=>"true"}, "refresh_interval"=>"30s", "number_of_shards"=>2, "merge"=>{"scheduler"=>{"max_thread_count"=>1}}, "number_of_replicas"=>2}}, "mappings"=>{"_doc"=>{"dynamic_templates"=>[{"message_field"=>{"path_match"=>"message", "mapping"=>{"norms"=>false, "type"=>"text"}, "match_mapping_type"=>"string"}}, {"string_fields"=>{"mapping"=>{"norms"=>false, "type"=>"text", "fields"=>{"keyword"=>{"ignore_above"=>256, "type"=>"keyword"}}}, "match_mapping_type"=>"string", "match"=>"*"}}], "properties"=>{"@timestamp"=>{"type"=>"date"}, "@version"=>{"type"=>"keyword"}}}}, "aliases"=>{"all_logs"=>{}}}
b = {"order"=>1, "aliases"=>{"all_logs"=>{}}, "mappings"=>{"_doc"=>{"dynamic_templates"=>[{"message_field"=>{"path_match"=>"message", "match_mapping_type"=>"string", "mapping"=>{"norms"=>false, "type"=>"text"}}}, {"string_fields"=>{"match"=>"*", "match_mapping_type"=>"string", "mapping"=>{"type"=>"text", "norms"=>false, "fields"=>{"keyword"=>{"type"=>"keyword", "ignore_above"=>256}}}}}], "properties"=>{"@timestamp"=>{"type"=>"date"}, "@version"=>{"type"=>"keyword"}}}}, "index_patterns"=>["logstash-cloudtrail*"], "settings"=>{"index"=>{"mapping.total_fields.limit"=>8000, "mapping.ignore_malformed"=>true, "routing.allocation.require.node_type"=>"hot", "refresh_interval"=>"30s", "merge.scheduler.max_thread_count"=>1, "number_of_replicas"=>2, "number_of_shards"=>2, "lifecycle.rollover_alias"=>"logstash-cloudtrail-write", "lifecycle.name"=>"logging_policy"}}}
ap Hashdiff.diff(a,b)
result
[
[0] [
[0] "-",
[1] "settings.index.lifecycle",
[2] {
"name" => "logging_policy",
"rollover_alias" => "logstash-cloudtrail-write"
}
],
[1] [
[0] "-",
[1] "settings.index.mapping",
[2] {
"ignore_malformed" => "true",
"total_fields" => {
"limit" => 8000
}
}
],
[2] [
[0] "-",
[1] "settings.index.merge",
[2] {
"scheduler" => {
"max_thread_count" => 1
}
}
],
[3] [
[0] "-",
[1] "settings.index.routing",
[2] {
"allocation" => {
"require" => {
"node_type" => "hot"
}
}
}
],
[4] [
[0] "+",
[1] "settings.index.lifecycle.name",
[2] "logging_policy"
],
[5] [
[0] "+",
[1] "settings.index.lifecycle.rollover_alias",
[2] "logstash-cloudtrail-write"
],
[6] [
[0] "+",
[1] "settings.index.mapping.ignore_malformed",
[2] true
],
[7] [
[0] "+",
[1] "settings.index.mapping.total_fields.limit",
[2] 8000
],
[8] [
[0] "+",
[1] "settings.index.merge.scheduler.max_thread_count",
[2] 1
],
[9] [
[0] "+",
[1] "settings.index.routing.allocation.require.node_type",
[2] "hot"
]
]
If you look at the source the true
is already a Boolean
so not sure why the diff for ignore_malformed
shows a "true"
to true
.
The rest of the diff appears to be exactly the same so not sure why this is causing a diff.
a={"x"=>1}
b = {"x.y"=>2}
diff = HashDiff.diff(a, b)
HashDiff.patch!(a, diff)
=>
NoMethodError: undefined method `[]=' for nil:NilClass
It would be great if there where a way to use RegEx to ignore certain nodes.
I am using this to compare the results of an API call, and need it to ignore the guid and dates
In documentation it stated use_lcs: false
should behave like below:
a = {x: [0, 1, 2]}
b = {x: [0, 2, 2, 3]}
diff = HashDiff.diff(a, b, :use_lcs => false)
diff.should == [["~", "x[1]", 1, 2], ["+", "x[3]", 3]]
But it got below instead for diff
[["-", "x[1]", 1], ["+", "x[1]", 2], ["+", "x[3]", 3]]
Hi,
When debugging an issue with octocatalog-diff we narrowed down the problem to HashDiff unable to compare a couple of arrays when LCS is used. Here's a reproducer:
require 'hashdiff'
a = {x: []}
(0...17000).each{ |x|
a[:x].push((0...11).map { ('a'..'z').to_a[rand(26)] }.push('.example.org').join)
}
b = {x: a[:x]}
puts "Without LCS"
diff = HashDiff.diff(a, b, :use_lcs => false)
puts diff
puts "Done!"
puts "With LCS"
diff = HashDiff.diff(a, b)
puts diff
puts "This should never be printed"
When comparing those two arrays using LCS (which is on by default) the application starts to eat up memory very quickly with 100% CPU usage until, in our case, the process is killed by the kernel's OOM killer as the test machine does not have any swap:
$ ruby reproducer_synth.rb
Without LCS
Done!
With LCS
...
Killed
kernel: Out of memory: Kill process 18783 (ruby) score 526 or sacrifice child
kernel: Killed process 18783 (ruby) total-vm:1192540kB, anon-rss:1013324kB, file-rss:0kB, shmem-rss:0kB
Yes, that's >1GiB!
# ruby --version
ruby 2.0.0p648 (2015-12-16) [x86_64-linux]
# gem list hashdiff
*** LOCAL GEMS ***
hashdiff (0.3.8)
#
I understand that LCS is O(n2) but not sure this is the expected behaviour as the array size is moderate.
Hi there,
I'm a bit confused. I'm following the QuickStart, which says to compare in the following way (eg.):
diff = HashDiff.diff(a, b)
diff.should == [['-', 'a', 3], ['-', 'b', 2]]
However, variable diff is set to a standard array type and does not have a method 'should'. Am I missing something? Or did the specs change in the meantime?
I'm using ruby v 1.9.2. My intention is two compare two complex hash to see if they're identical. So in the code above, I just want to check if diff.should == [].
Thanks,
Wim.
So looking at compare_hashes.rb there's some sorting to make sure the results are stable and deterministic independent of the ordering of the keys. Is that true for all of the diff functions? If so it would be helpful to document this (either way) so that users of the library know if they need to sort results if they need stability (for unit tests, etc)
I encountered an ArgumentError while comparing hashes which has keys of mixed string and symbols. While this practice is without doubt questionable, I do expect HashDiff to be able to handle this gracefully.
The following small example shows this:
require 'hashdiff'
a = { "c" => "testing", :b => "one two" }
b = { :a => "testing", "b" => "three four" }
HashDiff.diff a, b
Results in:
ArgumentError: comparison of String with :b failed
from /home/user/.rvm/gems/ruby-2.1.5/gems/hashdiff-0.3.0/lib/hashdiff/diff.rb:130:in `sort'
from /home/user/.rvm/gems/ruby-2.1.5/gems/hashdiff-0.3.0/lib/hashdiff/diff.rb:130:in `diff'
from (irb):14
from /home/user/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'
This occurs in method self.diff
in file diff.rb, and can happen at the places where key arrays are sorted, e.g.:
deleted_keys.sort.each do |k|
...
added_keys.sort.each do |k|
The best solution I can think of, is to replace the default sort with something that can handle any type, as long as that type implements to_s:
deleted_keys.sort {|x,y| x.to_s <=> y.to_s}.each do |k|
...
end
This works but is ugly, it could use some refactoring. The weak part of this solution is that it depends on the presence of method to_s to work. But this at least is a step toward a more generic usage.
now every test run/boot has a warning :(
Just remove the constant and be done with it ... or do whatever activesupport does but only when a deprecated constant is used
caused by #65
I have inputs
a = [1, 2, 3]
b = [1, 10, 3]
and expect a diff of
[["~", "[1]", 2, 10 ]]
but instead I get
[["-", "[1]", 2], ["+", "[1]", 10]]
I expect that because of how hash comparisons work
a = {a:1, b:2, c:3}
b = {a:1, b:10, c:3}
[["~", "b", 2, 10]]
and
a = [1, {a: 0, b: 1}, 3]
b = [1, {a: 0, b: 2}, 3]
[["~", "[1].b", 1, 2]]
Is it possible to get the results I expect?
The gemspec file indicates compatibility from ruby 1.8.7 while travis.yml sets up testing from ruby 1.9.3. If compatibility is supposed to be from ruby 1.8.7, then this line breaks that compatibility by using the ruby 1.9 hash syntax.
Hello,
The override of the ==/equal? methods seem to be used when checking values of an Hash, but not when checking the keys. Here is an example with a simple class :
class Color
attr_reader :value
def initialize(value)
@value = value
end
def equal?(other)
return false unless other.is_a?(Color)
@value == other.value
end
def ==(other)
return false unless other.is_a?(Color)
@value == other.value
end
end
Hashdiff.best_diff({1 => Color.new('red')}, {1 => Color.new('red')})
returns []
as expected
but
Hashdiff.best_diff({Color.new('red') => 1}, {Color.new('red') => 1})
returns [["~", "#<Color:0x000014ac0e49d418>", 1, nil]]
Thanks!
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.