├── .gitignore ├── LICENSE ├── README.md ├── appendix-a └── README.md ├── chapter-1 ├── README.md ├── firstValidArray.json ├── firstValidObject.json ├── jsonArray.json ├── jsonBoolean.json ├── jsonDateFormat.json ├── jsonGeneratorTemplate.js ├── jsonLatLon.json ├── jsonNull.json ├── jsonNumbers.json ├── jsonObjectNestObject.json ├── jsonObjectNestedArray.json ├── jsonPropertyName.json ├── jsonStrings.json ├── nameValue.json ├── simpleJsonArray.json ├── simpleJsonObject.json └── speakers.json ├── chapter-10 ├── Project-Setup.md ├── README.md ├── data │ ├── acceptedProposal.json │ ├── rejectedProposal.json │ └── speakerProposal.json ├── myconference │ ├── package.json │ ├── proposalReviewer.js │ ├── schemas │ │ └── speakerProposalSchema.json │ ├── speakerNotifier.js │ └── templates │ │ ├── acceptedProposal.hbs │ │ └── rejectedProposal.hbs └── scripts │ ├── create-topic.sh │ ├── delete-topic.sh │ ├── list-topics.sh │ ├── publish-message.sh │ ├── start-consumer.sh │ ├── start-kafka.sh │ ├── start-zookeeper.sh │ ├── stop-kafka.sh │ ├── stop-zookeeper.sh │ └── test-args.sh ├── chapter-2 ├── Project-Setup.md ├── README.md ├── Web-Project-Setup.md ├── data │ └── speakers.json ├── js │ ├── basic-data-types-stringify.js │ ├── eval-parse-2.js │ ├── eval-parse.js │ ├── obj-literal-parse.js │ ├── obj-literal-stringify-params.js │ ├── obj-literal-stringify-toJson.js │ └── obj-literal.js ├── speakers-test │ ├── package.json │ └── test │ │ └── speakers-spec.js ├── speakers-web-1 │ ├── .babelrc │ ├── .bowerrc │ ├── .editorconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .yo-rc.json │ ├── app │ │ ├── apple-touch-icon.png │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── robots.txt │ │ ├── scripts │ │ │ └── main.js │ │ └── styles │ │ │ └── main.css │ ├── bower.json │ ├── gulpfile.js │ ├── package.json │ └── test │ │ ├── index.html │ │ └── spec │ │ └── test.js ├── speakers-web-2 │ ├── .babelrc │ ├── .bowerrc │ ├── .editorconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .yo-rc.json │ ├── app │ │ ├── apple-touch-icon.png │ │ ├── data │ │ │ └── speakers.json │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── robots.txt │ │ ├── scripts │ │ │ └── main.js │ │ └── styles │ │ │ └── main.css │ ├── bower.json │ ├── gulpfile.js │ ├── package.json │ └── test │ │ ├── index.html │ │ └── spec │ │ └── test.js └── speakers-web-3 │ ├── .babelrc │ ├── .bowerrc │ ├── .editorconfig │ ├── .gitattributes │ ├── .gitignore │ ├── .yo-rc.json │ ├── app │ ├── apple-touch-icon.png │ ├── favicon.ico │ ├── index.html │ ├── robots.txt │ ├── scripts │ │ └── main.js │ ├── styles │ │ └── main.css │ └── templates │ │ └── speakers-mustache-template.html │ ├── bower.json │ ├── gulpfile.js │ ├── package.json │ └── test │ ├── index.html │ └── spec │ └── test.js ├── chapter-3 ├── Project-Setup.md ├── README.md ├── data │ └── speakers.json ├── ruby │ ├── basic_data_types_serialize.rb │ ├── obj_deserialize.rb │ ├── obj_deserialize_active_support.rb │ ├── obj_serialize_active_support.rb │ ├── obj_serialize_pretty.rb │ └── ostruct_example.rb ├── speakers-api-1 │ ├── .gitignore │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── Rakefile │ ├── app │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── speakers_controller.rb │ │ ├── jobs │ │ │ └── application_job.rb │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── speaker.rb │ │ └── serializers │ │ │ └── speaker_serializer.rb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ ├── rake │ │ ├── setup │ │ ├── spring │ │ └── update │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── active_model_serializers.rb │ │ │ ├── application_controller_renderer.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── cors.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── new_framework_defaults.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ ├── secrets.yml │ │ └── spring.rb │ ├── db │ │ └── seeds.rb │ ├── lib │ │ └── tasks │ │ │ └── .keep │ ├── log │ │ └── .keep │ ├── public │ │ └── robots.txt │ └── tmp │ │ └── .keep ├── speakers-api-2 │ ├── .gitignore │ ├── Gemfile │ ├── Gemfile.lock │ ├── README.md │ ├── Rakefile │ ├── app │ │ ├── controllers │ │ │ ├── application_controller.rb │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── speakers_controller.rb │ │ ├── jobs │ │ │ └── application_job.rb │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ └── speaker.rb │ │ └── serializers │ │ │ └── speaker_serializer.rb │ ├── bin │ │ ├── bundle │ │ ├── rails │ │ ├── rake │ │ ├── setup │ │ ├── spring │ │ └── update │ ├── config.ru │ ├── config │ │ ├── application.rb │ │ ├── boot.rb │ │ ├── environment.rb │ │ ├── environments │ │ │ ├── development.rb │ │ │ ├── production.rb │ │ │ └── test.rb │ │ ├── initializers │ │ │ ├── active_model_serializers.rb │ │ │ ├── application_controller_renderer.rb │ │ │ ├── backtrace_silencers.rb │ │ │ ├── cors.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── inflections.rb │ │ │ ├── mime_types.rb │ │ │ ├── new_framework_defaults.rb │ │ │ └── wrap_parameters.rb │ │ ├── locales │ │ │ └── en.yml │ │ ├── puma.rb │ │ ├── routes.rb │ │ ├── secrets.yml │ │ └── spring.rb │ ├── db │ │ └── seeds.rb │ ├── lib │ │ └── tasks │ │ │ └── .keep │ ├── log │ │ └── .keep │ ├── public │ │ └── robots.txt │ └── tmp │ │ └── .keep └── speakers-test │ ├── .bundle │ └── config │ ├── Gemfile │ ├── Gemfile.lock │ ├── Rakefile │ ├── models │ └── speaker.rb │ └── test │ ├── minitest_helper.rb │ └── speakers_spec.rb ├── chapter-4 ├── Project-Setup.md ├── README.md ├── speakers-api │ ├── build.gradle │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ │ └── main │ │ └── java │ │ └── org │ │ └── jsonatwork │ │ └── ch4 │ │ ├── Application.java │ │ ├── Speaker.java │ │ └── SpeakerController.java └── speakers-test │ ├── build.gradle │ ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ ├── settings.gradle │ └── src │ ├── main │ └── java │ │ └── org │ │ └── jsonatwork │ │ └── ch4 │ │ └── Speaker.java │ └── test │ ├── java │ └── jsonatwork │ │ └── ch4 │ │ ├── BasicJsonTypesTest.java │ │ ├── SpeakerJsonFlatFileTest.java │ │ └── SpeakersApiJsonTest.java │ └── resources │ ├── speaker.json │ └── speakers.json ├── chapter-5 ├── README.md ├── ex-1-basic-schema.json ├── ex-1-basic.json ├── ex-10-pattern-properties-invalid.json ├── ex-10-pattern-properties-schema.json ├── ex-10-pattern-properties.json ├── ex-11-regex-invalid.json ├── ex-11-regex-schema.json ├── ex-11-regex.json ├── ex-12-dependent-properties-invalid.json ├── ex-12-dependent-properties-schema.json ├── ex-12-dependent-properties.json ├── ex-13-internal-ref-invalid.json ├── ex-13-internal-ref-schema.json ├── ex-13-internal-ref.json ├── ex-14-external-ref-invalid.json ├── ex-14-external-ref-schema.json ├── ex-14-external-ref.json ├── ex-14-my-common-schema.json ├── ex-15-one-of-invalid.json ├── ex-15-one-of-schema.json ├── ex-15-one-of.json ├── ex-16-any-of-invalid.json ├── ex-16-any-of-schema.json ├── ex-16-any-of.json ├── ex-17-all-of-invalid.json ├── ex-17-all-of-schema.json ├── ex-17-all-of.json ├── ex-18-speaker-schema-generated-modified.json ├── ex-18-speaker-schema-generated.json ├── ex-18-speaker-template.js ├── ex-18-speaker.json ├── ex-18-speakers-generated-modified.json ├── ex-18-speakers-generated.json ├── ex-2-basic-types-invalid.json ├── ex-2-basic-types-schema.json ├── ex-2-basic-types.json ├── ex-3-basic-types-no-addl-props-invalid.json ├── ex-3-basic-types-no-addl-props-schema.json ├── ex-3-basic-types-no-addl-props.json ├── ex-4-basic-types-validation-req-invalid.json ├── ex-4-basic-types-validation-req-schema.json ├── ex-4-basic-types-validation-req.json ├── ex-5-number-min-max-invalid.json ├── ex-5-number-min-max-schema.json ├── ex-5-number-min-max.json ├── ex-6-array-simple-invalid.json ├── ex-6-array-simple-schema.json ├── ex-6-array-simple.json ├── ex-7-array-min-max-invalid.json ├── ex-7-array-min-max-schema.json ├── ex-7-array-min-max.json ├── ex-8-array-enum-invalid.json ├── ex-8-array-enum-schema.json ├── ex-8-array-enum.json ├── ex-9-named-object-invalid.json ├── ex-9-named-object-schema.json └── ex-9-named-object.json ├── chapter-6 ├── .gitignore ├── Project-Setup.md ├── README.md ├── cities-weather-test │ ├── package.json │ └── test │ │ ├── jq-spec.js │ │ ├── json-pointer-spec.js │ │ └── jsonpath-spec.js └── data │ ├── cities-weather-orig.json │ └── cities-weather.json ├── chapter-7 ├── Project-Setup.md ├── README.md ├── cities-weather-transform-test │ ├── lib │ │ └── jsont.js │ ├── package.json │ └── test │ │ ├── handlebars-spec.js │ │ ├── json-patch-spec.js │ │ ├── json-xml-spec.js │ │ ├── jsont-spec.js │ │ └── mustache-spec.js ├── data │ ├── cities-weather-short-badgerfish.json │ ├── cities-weather-short-parker.json │ ├── cities-weather-short-transformed.json │ ├── cities-weather-short.json │ ├── cities-weather-short.xml │ ├── cities-weather.json │ ├── cities-weather.xml │ ├── city-weather.json │ ├── speaker.json │ ├── speaker.xml │ ├── weather.css │ └── weather.html └── templates │ ├── transform-html.hbs │ ├── transform-html.mustache │ ├── transform-json.hbs │ ├── transform-json.mustache │ ├── transform-xml.hbs │ └── transform-xml.mustache ├── chapter-8 ├── .gitignore ├── Project-Setup.md ├── README.md ├── data │ ├── presentations-operations.jsonld │ ├── presentations.json │ ├── presentations.jsonld │ ├── speaker-collection-json-links.json │ ├── speaker-hal-embed-presentations.json │ ├── speaker-hal.json │ ├── speaker-jsonapi-embed-presentations.json │ ├── speaker-jsonapi-link-presentations.json │ ├── speaker-siren.json │ ├── speaker-template-collection-json.json │ ├── speaker.json │ ├── speaker.jsonld │ ├── speakers-hal-links.json │ ├── speakers-hal-next-rel.json │ ├── speakers-hal-server-next-rel-invalid.json │ ├── speakers-hal-server-next-rel.json │ ├── speakers-jsonapi-links.json │ └── speakers.json └── speakers-hal-test │ ├── package.json │ └── test │ └── hal-spec.js └── chapter-9 ├── README.md ├── mongo-export.txt ├── mongo-import.txt ├── mongo-speakers-create.js └── speakers.json /.gitignore: -------------------------------------------------------------------------------- 1 | **/node_modules/ 2 | 3 | **/target 4 | **/build 5 | **/.gradle/** 6 | 7 | .DS_Store 8 | **/npm-debug.log 9 | 10 | **/vendor -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Tom Marrs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | JSON at Work 2 | ============ 3 | 4 | [![Join the chat at https://gitter.im/tmarrs/json-at-work-examples](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/tmarrs/json-at-work-examples?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) 5 | Code examples for __JSON at Work__, [O'Reilly Media](http://www.oreilly.com/). 6 | -------------------------------------------------------------------------------- /chapter-1/README.md: -------------------------------------------------------------------------------- 1 | Chapter 1 Code 2 | ============== 3 | Code examples for Chapter 1 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-1/firstValidArray.json: -------------------------------------------------------------------------------- 1 | [ 2 | "also", 3 | "a", 4 | "valid", 5 | "JSON", 6 | "doc" 7 | ] 8 | -------------------------------------------------------------------------------- /chapter-1/firstValidObject.json: -------------------------------------------------------------------------------- 1 | { 2 | "thisIs": "My first JSON document" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-1/jsonArray.json: -------------------------------------------------------------------------------- 1 | { 2 | "presentations": [ 3 | { 4 | "title": "JSON at Work: Overview and Ecosystem", 5 | "length": "90 minutes", 6 | "abstract": [ 7 | "JSON is more than just a simple replacement for XML when", 8 | "you make an AJAX call." 9 | ], 10 | "track": "Web APIs" 11 | }, 12 | { 13 | "title": "RESTful Security at Work", 14 | "length": "90 minutes", 15 | "abstract": [ 16 | "You’ve been working with RESTful Web Services for a few years", 17 | "now, and you’d like to know if your services are secure." 18 | ], 19 | "track": "Web APIs" 20 | } 21 | ] 22 | } -------------------------------------------------------------------------------- /chapter-1/jsonBoolean.json: -------------------------------------------------------------------------------- 1 | { 2 | "isRegistered": true, 3 | "emailValidated": false 4 | } 5 | -------------------------------------------------------------------------------- /chapter-1/jsonDateFormat.json: -------------------------------------------------------------------------------- 1 | { 2 | "dateRegistered": "2014-03-01T23:46:11-05:00" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-1/jsonGeneratorTemplate.js: -------------------------------------------------------------------------------- 1 | [ 2 | '{{repeat(3)}}', 3 | { 4 | id: '{{index()}}', 5 | firstName: '{{firstName()}}', 6 | lastName: '{{surname()}}', 7 | picture: 'http://placehold.it/32x32', 8 | company: '{{company().toUpperCase()}}', 9 | email: '{{email()}}', 10 | about: '{{lorem(1, "paragraphs")}}', 11 | presentation: [ 12 | '{{repeat(3)}}', 13 | '{{lorem(1, "sentences")}}' 14 | ] 15 | } 16 | ] -------------------------------------------------------------------------------- /chapter-1/jsonLatLon.json: -------------------------------------------------------------------------------- 1 | { 2 | "empireStateBuilding": "40.748747-73.985547" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-1/jsonNull.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": { 3 | "line1": "555 Any Street", 4 | "line2": null, 5 | "city": "Denver", 6 | "stateOrProvince": "CO", 7 | "zipOrPostalCode": "80202", 8 | "country": "USA" 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /chapter-1/jsonNumbers.json: -------------------------------------------------------------------------------- 1 | { 2 | "age": 29, 3 | "cost": 299.99, 4 | "temperature": -10.5, 5 | "speedOfLight": 1.23e11, 6 | "speedOfLight2": 1.23e+11, 7 | "avogadro": 6.023E23, 8 | "avogadro2": 6.023E+23, 9 | "oneHundredth": 10e-3, 10 | "oneTenth": 10E-2 11 | } 12 | -------------------------------------------------------------------------------- /chapter-1/jsonObjectNestObject.json: -------------------------------------------------------------------------------- 1 | { 2 | "speaker": { 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "topics": [ 6 | "JSON", 7 | "REST", 8 | "SOA" 9 | ], 10 | "address": { 11 | "line1": "555 Any Street", 12 | "city": "Denver", 13 | "stateOrProvince": "CO", 14 | "zipOrPostalCode": "80202", 15 | "country": "USA" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chapter-1/jsonObjectNestedArray.json: -------------------------------------------------------------------------------- 1 | { 2 | "speaker": { 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "topics": ["JSON", "REST", "SOA"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /chapter-1/jsonPropertyName.json: -------------------------------------------------------------------------------- 1 | { 2 | "firstName": "John Smith" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-1/jsonStrings.json: -------------------------------------------------------------------------------- 1 | [ 2 | "fred", 3 | "fred\t", 4 | "\b", 5 | "", 6 | "\t", 7 | "\u004A" 8 | ] 9 | -------------------------------------------------------------------------------- /chapter-1/nameValue.json: -------------------------------------------------------------------------------- 1 | { 2 | "conference": "OSCON", 3 | "speechTitle": "JSON at Work", 4 | "track": "Web APIs" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-1/simpleJsonArray.json: -------------------------------------------------------------------------------- 1 | [ 2 | "Fred", 29, false 3 | ] 4 | -------------------------------------------------------------------------------- /chapter-1/simpleJsonObject.json: -------------------------------------------------------------------------------- 1 | { 2 | "address": { 3 | "line1": "555 Any Street", 4 | "city": "Denver", 5 | "stateOrProvince": "CO", 6 | "zipOrPostalCode": "80202", 7 | "country": "USA" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chapter-1/speakers.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [ 3 | { 4 | "about": "Incididunt mollit cupidatat magna excepteur do tempor ex non eiusmod magna exercitation proident nisi non. Sunt ad consequat eu non esse excepteur. Veniam quis Lorem ea labore ullamco veniam nisi do sunt. Nisi irure sit qui irure mollit ad aliquip non culpa sint reprehenderit ullamco.\r\n", 5 | "company": "Ecratic", 6 | "email": "larsonrichard@ecratic.com", 7 | "firstName": "Larson", 8 | "id": 0, 9 | "lastName": " Richard", 10 | "picture": "http://placehold.it/32x32", 11 | "tags": [ 12 | "JavaScript", "AngularJS", "Yeoman" 13 | ] 14 | }, 15 | { 16 | "about": "Labore tempor irure adipisicing consectetur velit. Ipsum Lorem non mollit aliquip. Fugiat est irure quis laboris minim anim esse fugiat et culpa exercitation. Dolor cillum excepteur officia Lorem ullamco magna et cupidatat dolor incididunt occaecat adipisicing consectetur in. Ullamco ullamco commodo nulla eiusmod. Lorem Lorem non sunt laboris ut et elit mollit deserunt nostrud est et id adipisicing.\r\n", 17 | "company": "Acusage", 18 | "email": "esterclements@acusage.com", 19 | "firstName": "Ester", 20 | "id": 1, 21 | "lastName": "Clements", 22 | "picture": "http://placehold.it/32x32", 23 | "tags": [ 24 | "REST", "Ruby on Rails", "APIs" 25 | ] 26 | }, 27 | { 28 | "about": "Proident ex Lorem et Lorem ad. Do voluptate officia minim in nisi ut sit nisi ex eu nostrud do ut. Aute ad dolor tempor dolor aute nisi deserunt deserunt ut deserunt cillum quis. Ipsum nulla sit reprehenderit consequat incididunt incididunt dolore et magna aliquip ut ex. Cupidatat exercitation ipsum dolore nisi incididunt anim est. Culpa veniam ut excepteur aliqua exercitation.\r\n", 29 | "company": "Talkola", 30 | "email": "christensenfisher@talkola.com", 31 | "firstName": "Christensen", 32 | "id": 2, 33 | "lastName": "Fisher", 34 | "picture": "http://placehold.it/32x32", 35 | "tags": [ 36 | "Java", "Spring", "Maven", "REST" 37 | ] 38 | } 39 | ] 40 | } -------------------------------------------------------------------------------- /chapter-10/README.md: -------------------------------------------------------------------------------- 1 | Chapter 10 Code 2 | =============== 3 | Code examples for Chapter 10 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-10/data/acceptedProposal.json: -------------------------------------------------------------------------------- 1 | { 2 | "decision": { 3 | "accepted": true, 4 | "timeSlot": { 5 | "date": "2017-11-06", 6 | "time": "10:OO" 7 | } 8 | }, 9 | "proposal": { 10 | "speaker": { 11 | "firstName": "Larson", 12 | "lastName": "Richard", 13 | "email": "larson.richard@ecratic.com", 14 | "bio": "Larson Richard is the CTO of ... and he founded a JavaScript meetup in ..." 15 | }, 16 | "session": { 17 | "title": "Enterprise Node", 18 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 19 | "type": "How-To", 20 | "length": "3 hours" 21 | }, 22 | "conference": { 23 | "name": "Ultimate JavaScript Conference by MyConference", 24 | "beginDate": "2017-11-06", 25 | "endDate": "2017-11-10" 26 | }, 27 | "topic": { 28 | "primary": "Node.js", 29 | "secondary": [ 30 | "REST", 31 | "Architecture", 32 | "JavaScript" 33 | ] 34 | }, 35 | "audience": { 36 | "takeaway": "Audience members will learn how to ...", 37 | "jobTitles": [ 38 | "Architects", 39 | "Developers" 40 | ], 41 | "level": "Intermediate" 42 | }, 43 | "installation": [ 44 | "Git", 45 | "Laptop", 46 | "Node.js" 47 | ] 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /chapter-10/data/rejectedProposal.json: -------------------------------------------------------------------------------- 1 | { 2 | "decision": { 3 | "accepted": false 4 | }, 5 | "proposal": { 6 | "speaker": { 7 | "firstName": "Larson", 8 | "lastName": "Richard", 9 | "email": "larson.richard@ecratic.com", 10 | "bio": "Larson Richard is the CTO of ... and he founded a JavaScript meetup in ..." 11 | }, 12 | "session": { 13 | "title": "Enterprise Node", 14 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 15 | "type": "How-To", 16 | "length": "3 hours" 17 | }, 18 | "conference": { 19 | "name": "Ultimate JavaScript Conference by MyConference", 20 | "beginDate": "2017-11-06", 21 | "endDate": "2017-11-10" 22 | }, 23 | "topic": { 24 | "primary": "Node.js", 25 | "secondary": [ 26 | "REST", 27 | "Architecture", 28 | "JavaScript" 29 | ] 30 | }, 31 | "audience": { 32 | "takeaway": "Audience members will learn how to ...", 33 | "jobTitles": [ 34 | "Architects", 35 | "Developers" 36 | ], 37 | "level": "Intermediate" 38 | }, 39 | "installation": [ 40 | "Git", 41 | "Laptop", 42 | "Node.js" 43 | ] 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /chapter-10/data/speakerProposal.json: -------------------------------------------------------------------------------- 1 | { 2 | "speaker": { 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "email": "larson.richard@ecratic.com", 6 | "bio": "Larson Richard is the CTO of ... and he founded a JavaScript meetup in ..." 7 | }, 8 | "session": { 9 | "title": "Enterprise Node", 10 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 11 | "type": "How-To", 12 | "length": "3 hours" 13 | }, 14 | "conference": { 15 | "name": "Ultimate JavaScript Conference by MyConference", 16 | "beginDate": "2017-11-06", 17 | "endDate": "2017-11-10" 18 | }, 19 | "topic": { 20 | "primary": "Node.js", 21 | "secondary": [ 22 | "REST", 23 | "Architecture", 24 | "JavaScript" 25 | ] 26 | }, 27 | "audience": { 28 | "takeaway": "Audience members will learn how to ...", 29 | "jobTitles": [ 30 | "Architects", 31 | "Developers" 32 | ], 33 | "level": "Intermediate" 34 | }, 35 | "installation": [ 36 | "Git", 37 | "Laptop", 38 | "Node.js" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /chapter-10/myconference/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "myconference", 3 | "version": "1.0.0", 4 | "description": "Kafka Producer/Consumer.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tmarrs/json-at-work-examples.git" 12 | }, 13 | "keywords": [ 14 | "Kafka", 15 | "Node", 16 | "JSON" 17 | ], 18 | "author": "Tom Marrs", 19 | "license": "ISC", 20 | "bugs": { 21 | "url": "https://github.com/tmarrs/json-at-work-examples/issues" 22 | }, 23 | "homepage": "https://github.com/tmarrs/json-at-work-examples#readme", 24 | "dependencies": { 25 | "ajv": "^5.0.0", 26 | "handlebars": "^4.0.6", 27 | "kafka-node": "^1.1.0", 28 | "nodemailer": "^4.0.1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter-10/myconference/templates/acceptedProposal.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | {{proposal.speaker.firstName}}, 6 |

7 |

8 | We are pleased to inform you that your talk on {{proposal.session.title}} 9 | has been accepted for the {{proposal.conference.name}}. 10 |

11 |

12 | Your session scheduled for {{decision.timeSlot.date}} at {{decision.timeSlot.time}}. 13 |

14 |

15 | Sincerely,
16 | The {{proposal.conference.name}} Event Team. 17 |

18 | 19 | -------------------------------------------------------------------------------- /chapter-10/myconference/templates/rejectedProposal.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |

5 | {{proposal.speaker.firstName}}, 6 |

7 |

8 | We appreciate your interest, but regret to inform you that your talk on {{proposal.session.title}} was not accepted for the {{proposal.conference.name}}. 9 |

10 |

11 | We look forward to seeing you at future events, and hope that you submit 12 | more conference talks. 13 |

14 |

15 | Sincerely,
16 | The {{proposal.conference.name}} Event Team. 17 |

18 | 19 | -------------------------------------------------------------------------------- /chapter-10/scripts/create-topic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check Command-Line Arguments. 4 | USAGE="usage: ${0##*/} " 5 | 6 | if [ $# -eq 0 ] ; then 7 | echo "No arguments supplied - ${USAGE}" 8 | exit 1 9 | fi 10 | 11 | if [ $# -ne 1 ] ; then 12 | echo "Incorrect # of arguments - ${USAGE}" 13 | exit 1 14 | fi 15 | 16 | kafka-topics --zookeeper localhost:2181 --create \ 17 | --topic $1 --partitions 1 \ 18 | --replication-factor 1 -------------------------------------------------------------------------------- /chapter-10/scripts/delete-topic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check Command-Line Arguments. 4 | USAGE="usage: ${0##*/} " 5 | 6 | if [ $# -eq 0 ] ; then 7 | echo "No arguments supplied - ${USAGE}" 8 | exit 1 9 | fi 10 | 11 | if [ $# -ne 1 ] ; then 12 | echo "Incorrect # of arguments - ${USAGE}" 13 | exit 1 14 | fi 15 | 16 | kafka-topics --zookeeper localhost:2181 --delete --topic $1 -------------------------------------------------------------------------------- /chapter-10/scripts/list-topics.sh: -------------------------------------------------------------------------------- 1 | kafka-topics --zookeeper localhost:2181 --list -------------------------------------------------------------------------------- /chapter-10/scripts/publish-message.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | USAGE="usage: ${0##*/} [-f | <\"Message\"> ]" 4 | 5 | # Get parameters from the CLI. 6 | getJsonFileNameFromCli() 7 | { 8 | while getopts "f::" opt "$@"; do 9 | case $opt in 10 | f) 11 | JSON_MESSAGE_FILE_NAME_FROM_CLI=$OPTARG 12 | ;; 13 | \?) 14 | echo "Invalid option: -$OPTARG ${USAGE}" >&2 15 | exit 1 16 | ;; 17 | :) 18 | echo "Option -$OPTARG requires an argument. ${USAGE}" >&2 19 | exit 1 20 | ;; 21 | *) 22 | echo "Unimplemented option: -$OPTARG - ${USAGE}" >&2 23 | exit 1 24 | ;; 25 | esac 26 | done 27 | } 28 | 29 | if [ $# -eq 0 ] ; then 30 | echo "No arguments supplied - ${USAGE}" 31 | exit 1 32 | fi 33 | 34 | if [ $# -eq 2 ] ; then # Message supplied as CLI param. 35 | MESSAGE_FROM_CLI=$1 36 | TOPIC_NAME_FROM_CLI=$2 37 | elif [ $# -eq 3 ] ; then # JSON file supplied as CLI param. 38 | getJsonFileNameFromCli $@ 39 | TOPIC_NAME_FROM_CLI=$3 40 | else 41 | echo "Incorrect # of arguments - ${USAGE}" 42 | exit 1 43 | fi 44 | 45 | if [[ -z $TOPIC_NAME_FROM_CLI ]] ; then 46 | echo "No topic - [${USAGE}]" 47 | exit 1 48 | fi 49 | 50 | # echo "Topic Name from CLI = [$TOPIC_NAME_FROM_CLI]" 51 | 52 | if [[ ! -z $MESSAGE_FROM_CLI ]] ; then 53 | # echo "Text Message from CLI = [$MESSAGE_FROM_CLI]" 54 | 55 | echo $MESSAGE_FROM_CLI | kafka-console-producer \ 56 | --broker-list localhost:9092 \ 57 | --topic $TOPIC_NAME_FROM_CLI 58 | elif [[ ! -z $JSON_MESSAGE_FILE_NAME_FROM_CLI ]] ; then 59 | # echo "JSON Message file name from CLI = [$JSON_MESSAGE_FILE_NAME_FROM_CLI]" 60 | 61 | if [ ! -f "$JSON_MESSAGE_FILE_NAME_FROM_CLI" ] ; then 62 | echo "JSON Message file: $JSON_MESSAGE_FILE_NAME_FROM_CLI does not exist." 63 | exit 1 64 | fi 65 | 66 | JSON_MESSAGE=`cat $JSON_MESSAGE_FILE_NAME_FROM_CLI` 67 | echo $JSON_MESSAGE | kafka-console-producer \ 68 | --broker-list localhost:9092 \ 69 | --topic $TOPIC_NAME_FROM_CLI 70 | fi 71 | -------------------------------------------------------------------------------- /chapter-10/scripts/start-consumer.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Check Command-Line Arguments. 4 | USAGE="usage: ${0##*/} " 5 | 6 | if [ $# -eq 0 ] ; then 7 | echo "No arguments supplied - ${USAGE}" 8 | exit 1 9 | fi 10 | 11 | if [ $# -ne 1 ] ; then 12 | echo "Incorrect # of arguments - ${USAGE}" 13 | exit 1 14 | fi 15 | 16 | kafka-console-consumer --bootstrap-server localhost:9092 \ 17 | --topic $1 -------------------------------------------------------------------------------- /chapter-10/scripts/start-kafka.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | kafka-server-start /usr/local/etc/kafka/server.properties -------------------------------------------------------------------------------- /chapter-10/scripts/start-zookeeper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | zkServer start -------------------------------------------------------------------------------- /chapter-10/scripts/stop-kafka.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | kafka-server-stop 4 | -------------------------------------------------------------------------------- /chapter-10/scripts/stop-zookeeper.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | zkServer stop -------------------------------------------------------------------------------- /chapter-2/README.md: -------------------------------------------------------------------------------- 1 | Chapter 2 Code 2 | ============== 3 | Code examples for Chapter 2 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-2/data/speakers.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [{ 3 | "about": "Incididunt mollit cupidatat magna excepteur do tempor ex non eiusmod magna exercitation proident nisi non. Sunt ad consequat eu non esse excepteur. Veniam quis Lorem ea labore ullamco veniam nisi do sunt. Nisi irure sit qui irure mollit ad aliquip non culpa sint reprehenderit ullamco.\r\n", 4 | "company": "Ecratic", 5 | "email": "larsonrichard@ecratic.com", 6 | "firstName": "Larson", 7 | "id": 0, 8 | "lastName": "Richard", 9 | "picture": "http://placehold.it/32x32", 10 | "tags": [ 11 | "JavaScript", 12 | "AngularJS", 13 | "Yeoman" 14 | ] 15 | }, { 16 | "about": "Labore tempor irure adipisicing consectetur velit. Ipsum Lorem non mollit aliquip. Fugiat est irure quis laboris minim anim esse fugiat et culpa exercitation. Dolor cillum excepteur officia Lorem ullamco magna et cupidatat dolor incididunt occaecat adipisicing consectetur in. Ullamco ullamco commodo nulla eiusmod. Lorem Lorem non sunt laboris ut et elit mollit deserunt nostrud est et id adipisicing.\r\n", 17 | "company": "Acusage", 18 | "email": "esterclements@acusage.com", 19 | "firstName": "Ester", 20 | "id": 1, 21 | "lastName": "Clements", 22 | "picture": "http://placehold.it/32x32", 23 | "tags": [ 24 | "REST", 25 | "Ruby on Rails", 26 | "APIs" 27 | ] 28 | }, { 29 | "about": "Proident ex Lorem et Lorem ad. Do voluptate officia minim in nisi ut sit nisi ex eu nostrud do ut. Aute ad dolor tempor dolor aute nisi deserunt deserunt ut deserunt cillum quis. Ipsum nulla sit reprehenderit consequat incididunt incididunt dolore et magna aliquip ut ex. Cupidatat exercitation ipsum dolore nisi incididunt anim est. Culpa veniam ut excepteur aliqua exercitation.\r\n", 30 | "company": "Talkola", 31 | "email": "christensenfisher@talkola.com", 32 | "firstName": "Christensen", 33 | "id": 2, 34 | "lastName": "Fisher", 35 | "picture": "http://placehold.it/32x32", 36 | "tags": [ 37 | "Java", 38 | "Spring", 39 | "Maven", 40 | "REST" 41 | ] 42 | }] 43 | } 44 | -------------------------------------------------------------------------------- /chapter-2/js/basic-data-types-stringify.js: -------------------------------------------------------------------------------- 1 | var age = 39; // Integer 2 | console.log('age = ' + JSON.stringify(age) + '\n'); 3 | 4 | var fullName = 'Larson Richard'; // String 5 | console.log('fullName = ' + JSON.stringify(fullName) + '\n'); 6 | 7 | var tags = ['json', 'rest', 'api', 'oauth']; // Array 8 | console.log('tags = ' + JSON.stringify(tags) + '\n'); 9 | 10 | var reqistered = true; // Boolean 11 | console.log('registered = ' + JSON.stringify(reqistered) + '\n'); 12 | 13 | var speaker = { 14 | firstName: 'Larson', 15 | lastName: 'Richard', 16 | email: 'larsonrichard@ecratic.com', 17 | about: 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 18 | company: 'Ecratic', 19 | tags: ['json', 'rest', 'api', 'oauth'], 20 | registered: true 21 | }; 22 | 23 | console.log('speaker = ' + JSON.stringify(speaker)); -------------------------------------------------------------------------------- /chapter-2/js/eval-parse-2.js: -------------------------------------------------------------------------------- 1 | var x = '{ "sessionDate": new Date() }'; 2 | 3 | console.log('Parse with eval(): ' + eval('(' + x + ')').sessionDate + '\n'); 4 | 5 | console.log('Parse with JSON.parse(): ' + JSON.parse(x).sessionDate); -------------------------------------------------------------------------------- /chapter-2/js/eval-parse.js: -------------------------------------------------------------------------------- 1 | var x = '{ "sessionDate": "2014-10-06T13:30:00.000Z" }'; 2 | 3 | console.log('Parse with eval(): ' + eval('(' + x + ')').sessionDate + '\n'); 4 | 5 | console.log('Parse with JSON.parse(): ' + JSON.parse(x).sessionDate); -------------------------------------------------------------------------------- /chapter-2/js/obj-literal-parse.js: -------------------------------------------------------------------------------- 1 | var json = '{' + // Multi-line JSON string. 2 | '"firstName": "Larson",' + 3 | '"lastName": "Richard",' + 4 | '"email": "larsonrichard@ecratic.com",' + 5 | '"about": "Incididunt mollit cupidatat magna excepteur do tempor ex non ...",' + 6 | '"company": "Ecratic",' + 7 | '"tags": [' + 8 | '"json",' + 9 | '"rest",' + 10 | '"api",' + 11 | '"oauth"' + 12 | '],' + 13 | '"registered": true' + 14 | '}'; 15 | 16 | // De-serialize JSON string into speaker object. 17 | var speaker = JSON.parse(json); 18 | 19 | // Print 2nd speaker object. 20 | console.log('speaker.firstName = ' + speaker.firstName); -------------------------------------------------------------------------------- /chapter-2/js/obj-literal-stringify-params.js: -------------------------------------------------------------------------------- 1 | var speaker = { 2 | firstName: 'Larson', 3 | lastName: 'Richard', 4 | email: 'larsonrichard@ecratic.com', 5 | about: 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 6 | company: 'Ecratic', 7 | tags: ['json', 'rest', 'api', 'oauth'], 8 | registered: true 9 | }; 10 | 11 | function serializeSpeaker(key, value) { 12 | return (typeof value === 'string' || Array.isArray(value)) ? undefined : value; 13 | } 14 | 15 | // Pretty Print. 16 | console.log('Speaker (pretty print):\n' + JSON.stringify(speaker, null, 2) + '\n'); 17 | 18 | 19 | // Pretty print and filter out Strings and Arrays. 20 | console.log('Speaker without Strings and Arrays:\n' + 21 | JSON.stringify(speaker, serializeSpeaker, 2)); -------------------------------------------------------------------------------- /chapter-2/js/obj-literal-stringify-toJson.js: -------------------------------------------------------------------------------- 1 | var speaker = { 2 | firstName: 'Larson', 3 | lastName: 'Richard', 4 | email: 'larsonrichard@ecratic.com', 5 | about: 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 6 | company: 'Ecratic', 7 | tags: ['json', 'rest', 'api', 'oauth'], 8 | registered: true 9 | }; 10 | 11 | speaker.toJSON = function() { 12 | return 'Hi there!'; 13 | }; 14 | 15 | console.log('speaker.toJSON(): ' + JSON.stringify(speaker, null, 2)); -------------------------------------------------------------------------------- /chapter-2/js/obj-literal.js: -------------------------------------------------------------------------------- 1 | var speaker = { 2 | firstName: 'Larson', 3 | lastName: 'Richard', 4 | email: 'larsonrichard@ecratic.com', 5 | about: 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 6 | company: 'Ecratic', 7 | tags: ['json', 'rest', 'api', 'oauth'], 8 | registered: true, 9 | name: function() { 10 | return (this.firstName + ' ' + this.lastName); 11 | } 12 | }; -------------------------------------------------------------------------------- /chapter-2/speakers-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speakers-test", 3 | "version": "1.0.0", 4 | "description": "Unit Tests for speakers API.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tmarrs/json-at-work-examples.git" 12 | }, 13 | "keywords": [ 14 | "JSON", 15 | "Mocha", 16 | "Chai" 17 | ], 18 | "author": "Tom Marrs (https://github.com/tmarrs)", 19 | "license": "MIT", 20 | "bugs": { 21 | "url": "https://github.com/tmarrs/json-at-work-examples/issues" 22 | }, 23 | "homepage": "https://github.com/tmarrs/json-at-work-examples#readme", 24 | "dependencies": { 25 | "chai": "^3.5.0", 26 | "mocha": "^3.2.0", 27 | "unirest": "^0.5.1" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /chapter-2/speakers-test/test/speakers-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var unirest = require('unirest'); 5 | 6 | var SPEAKERS_ALL_URI = 'http://localhost:5000/speakers'; 7 | 8 | 9 | describe('speakers', function() { 10 | var req; 11 | 12 | beforeEach(function() { 13 | req = unirest.get(SPEAKERS_ALL_URI) 14 | .header('Accept', 'application/json'); 15 | }); 16 | 17 | it('should return a 200 response', function(done) { 18 | req.end(function(res) { 19 | expect(res.statusCode).to.eql(200); 20 | expect(res.headers['content-type']).to.eql( 21 | 'application/json; charset=utf-8'); 22 | 23 | done(); 24 | }); 25 | }); 26 | 27 | it('should return all speakers', function(done) { 28 | req.end(function(res) { 29 | var speakers = res.body; 30 | var speaker3 = speakers[2]; 31 | 32 | //console.log(speakers); 33 | //console.log(speaker3); 34 | expect(speakers.length).to.eql(3); 35 | expect(speaker3.company).to.eql('Talkola'); 36 | expect(speaker3.firstName).to.eql('Christensen'); 37 | expect(speaker3.lastName).to.eql('Fisher'); 38 | expect(speaker3.tags).to.eql([ 39 | 'Java', 'Spring', 40 | 'Maven', 'REST' 41 | ]); 42 | 43 | done(); 44 | }); 45 | }); 46 | 47 | }); 48 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 26 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | bower_components 5 | test/bower_components 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-mocha": { 3 | "ui": "bdd", 4 | "rjs": false 5 | } 6 | } -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/app/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-1/app/apple-touch-icon.png -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-1/app/favicon.ico -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/app/scripts/main.js: -------------------------------------------------------------------------------- 1 | console.log('\'Allo \'Allo!'); 2 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .browserupgrade { 2 | margin: 0.2em 0; 3 | background: #ccc; 4 | color: #000; 5 | padding: 0.2em 0; 6 | } 7 | 8 | /* Space out content a bit */ 9 | body { 10 | padding-top: 1.5rem; 11 | padding-bottom: 1.5rem; 12 | } 13 | 14 | /* Everything but the jumbotron gets side spacing for mobile first views */ 15 | .header, 16 | .marketing, 17 | .footer { 18 | padding-right: 1rem; 19 | padding-left: 1rem; 20 | } 21 | 22 | /* Custom page header */ 23 | .header { 24 | padding-bottom: 1rem; 25 | border-bottom: .05rem solid #e5e5e5; 26 | } 27 | 28 | /* Make the masthead heading the same height as the navigation */ 29 | .header h3 { 30 | margin-top: 0; 31 | margin-bottom: 0; 32 | line-height: 3rem; 33 | } 34 | 35 | /* Custom page footer */ 36 | .footer { 37 | padding-top: 1.5rem; 38 | color: #777; 39 | border-top: .05rem solid #e5e5e5; 40 | } 41 | 42 | /* Customize container */ 43 | @media (min-width: 48em) { 44 | .container { 45 | max-width: 46rem; 46 | } 47 | } 48 | .container-narrow > hr { 49 | margin: 2rem 0; 50 | } 51 | 52 | /* Main marketing message and sign up button */ 53 | .jumbotron { 54 | text-align: center; 55 | border-bottom: .05rem solid #e5e5e5; 56 | } 57 | .jumbotron .btn { 58 | padding: .75rem 1.5rem; 59 | font-size: 1.5rem; 60 | } 61 | 62 | /* Supporting marketing content */ 63 | .marketing { 64 | margin: 3rem 0; 65 | } 66 | .marketing p + h4 { 67 | margin-top: 1.5rem; 68 | } 69 | 70 | /* Responsive: Portrait tablets and up */ 71 | @media screen and (min-width: 48em) { 72 | /* Remove the padding we set earlier */ 73 | .header, 74 | .marketing, 75 | .footer { 76 | padding-right: 0; 77 | padding-left: 0; 78 | } 79 | /* Space out the masthead */ 80 | .header { 81 | margin-bottom: 2rem; 82 | } 83 | /* Remove the bottom border on the jumbotron for visual effect */ 84 | .jumbotron { 85 | border-bottom: 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speakers-web-1", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "~4.0.0-alpha.6" 6 | }, 7 | "overrides": { 8 | "bootstrap": { 9 | "main": [ 10 | "scss/bootstrap.scss", 11 | "dist/js/bootstrap.js", 12 | "dist/css/bootstrap.css" 13 | ] 14 | } 15 | }, 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "mocha": "^3.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": ">=4" 5 | }, 6 | "devDependencies": { 7 | "babel-core": "^6.4.0", 8 | "babel-preset-es2015": "^6.3.13", 9 | "babel-register": "^6.5.2", 10 | "browser-sync": "^2.2.1", 11 | "del": "^2.2.0", 12 | "gulp": "^3.9.0", 13 | "gulp-autoprefixer": "^3.0.1", 14 | "gulp-babel": "^6.1.1", 15 | "gulp-cache": "^0.4.2", 16 | "gulp-cssnano": "^2.0.0", 17 | "gulp-eslint": "^3.0.0", 18 | "gulp-htmlmin": "^3.0.0", 19 | "gulp-if": "^2.0.2", 20 | "gulp-imagemin": "^3.0.1", 21 | "gulp-load-plugins": "^1.2.4", 22 | "gulp-plumber": "^1.0.1", 23 | "gulp-size": "^2.1.0", 24 | "gulp-sourcemaps": "^2.2.0", 25 | "gulp-uglify": "^2.0.0", 26 | "gulp-useref": "^3.0.0", 27 | "main-bower-files": "^2.5.0", 28 | "run-sequence": "^1.2.2", 29 | "wiredep": "^4.0.0" 30 | }, 31 | "eslintConfig": { 32 | "env": { 33 | "es6": true, 34 | "node": true, 35 | "browser": true 36 | }, 37 | "rules": { 38 | "quotes": [ 39 | 2, 40 | "single" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-1/test/spec/test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | describe('Give it some context', function () { 5 | describe('maybe a bit more context here', function () { 6 | it('should run here few assertions', function () { 7 | 8 | }); 9 | }); 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 26 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | bower_components 5 | test/bower_components 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-mocha": { 3 | "ui": "bdd", 4 | "rjs": false 5 | } 6 | } -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-2/app/apple-touch-icon.png -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/data/speakers.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [ 3 | { 4 | "about": "Incididunt mollit cupidatat magna excepteur do tempor ex non eiusmod magna exercitation proident nisi non. Sunt ad consequat eu non esse excepteur. Veniam quis Lorem ea labore ullamco veniam nisi do sunt. Nisi irure sit qui irure mollit ad aliquip non culpa sint reprehenderit ullamco.\r\n", 5 | "company": "Ecratic", 6 | "email": "larsonrichard@ecratic.com", 7 | "firstName": "Larson", 8 | "id": 0, 9 | "lastName": " Richard", 10 | "picture": "http://placehold.it/32x32", 11 | "tags": [ 12 | "JavaScript", 13 | "AngularJS", 14 | "Yeoman" 15 | ] 16 | }, 17 | { 18 | "about": "Labore tempor irure adipisicing consectetur velit. Ipsum Lorem non mollit aliquip. Fugiat est irure quis laboris minim anim esse fugiat et culpa exercitation. Dolor cillum excepteur officia Lorem ullamco magna et cupidatat dolor incididunt occaecat adipisicing consectetur in. Ullamco ullamco commodo nulla eiusmod. Lorem Lorem non sunt laboris ut et elit mollit deserunt nostrud est et id adipisicing.\r\n", 19 | "company": "Acusage", 20 | "email": "esterclements@acusage.com", 21 | "firstName": "Ester", 22 | "id": 1, 23 | "lastName": "Clements", 24 | "picture": "http://placehold.it/32x32", 25 | "tags": [ 26 | "REST", 27 | "Ruby on Rails", 28 | "APIs" 29 | ] 30 | }, 31 | { 32 | "about": "Proident ex Lorem et Lorem ad. Do voluptate officia minim in nisi ut sit nisi ex eu nostrud do ut. Aute ad dolor tempor dolor aute nisi deserunt deserunt ut deserunt cillum quis. Ipsum nulla sit reprehenderit consequat incididunt incididunt dolore et magna aliquip ut ex. Cupidatat exercitation ipsum dolore nisi incididunt anim est. Culpa veniam ut excepteur aliqua exercitation.\r\n", 33 | "company": "Talkola", 34 | "email": "christensenfisher@talkola.com", 35 | "firstName": "Christensen", 36 | "id": 2, 37 | "lastName": "Fisher", 38 | "picture": "http://placehold.it/32x32", 39 | "tags": [ 40 | "Java", 41 | "Spring", 42 | "Maven", 43 | "REST" 44 | ] 45 | } 46 | ] 47 | } -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-2/app/favicon.ico -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/scripts/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | console.log('Hello JSON at Work!'); 4 | 5 | $(document).ready(function() { 6 | 7 | function addSpeakersjQuery(speakers) { 8 | $.each(speakers, function(index, speaker) { 9 | var tbody = $('#speakers-tbody'); 10 | var tr = $(''); 11 | var nameCol = $(''); 12 | var aboutCol = $(''); 13 | var topicsCol = $(''); 14 | 15 | nameCol.text(speaker.firstName + ' ' + speaker.lastName); 16 | aboutCol.text(speaker.about); 17 | topicsCol.text(speaker.tags.join(', ')); 18 | 19 | tr.append(nameCol); 20 | tr.append(aboutCol); 21 | tr.append(topicsCol); 22 | tbody.append(tr); 23 | }); 24 | } 25 | 26 | $.getJSON('data/speakers.json', 27 | function(data) { 28 | addSpeakersjQuery(data.speakers); 29 | } 30 | ); 31 | 32 | }); 33 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .browserupgrade { 2 | margin: 0.2em 0; 3 | background: #ccc; 4 | color: #000; 5 | padding: 0.2em 0; 6 | } 7 | 8 | /* Space out content a bit */ 9 | body { 10 | padding-top: 1.5rem; 11 | padding-bottom: 1.5rem; 12 | } 13 | 14 | /* Everything but the jumbotron gets side spacing for mobile first views */ 15 | .header, 16 | .marketing, 17 | .footer { 18 | padding-right: 1rem; 19 | padding-left: 1rem; 20 | } 21 | 22 | /* Custom page header */ 23 | .header { 24 | padding-bottom: 1rem; 25 | border-bottom: .05rem solid #e5e5e5; 26 | } 27 | 28 | /* Make the masthead heading the same height as the navigation */ 29 | .header h3 { 30 | margin-top: 0; 31 | margin-bottom: 0; 32 | line-height: 3rem; 33 | } 34 | 35 | /* Custom page footer */ 36 | .footer { 37 | padding-top: 1.5rem; 38 | color: #777; 39 | border-top: .05rem solid #e5e5e5; 40 | } 41 | 42 | /* Customize container */ 43 | @media (min-width: 48em) { 44 | .container { 45 | max-width: 46rem; 46 | } 47 | } 48 | .container-narrow > hr { 49 | margin: 2rem 0; 50 | } 51 | 52 | /* Main marketing message and sign up button */ 53 | .jumbotron { 54 | text-align: center; 55 | border-bottom: .05rem solid #e5e5e5; 56 | } 57 | .jumbotron .btn { 58 | padding: .75rem 1.5rem; 59 | font-size: 1.5rem; 60 | } 61 | 62 | /* Supporting marketing content */ 63 | .marketing { 64 | margin: 3rem 0; 65 | } 66 | .marketing p + h4 { 67 | margin-top: 1.5rem; 68 | } 69 | 70 | /* Responsive: Portrait tablets and up */ 71 | @media screen and (min-width: 48em) { 72 | /* Remove the padding we set earlier */ 73 | .header, 74 | .marketing, 75 | .footer { 76 | padding-right: 0; 77 | padding-left: 0; 78 | } 79 | /* Space out the masthead */ 80 | .header { 81 | margin-bottom: 2rem; 82 | } 83 | /* Remove the bottom border on the jumbotron for visual effect */ 84 | .jumbotron { 85 | border-bottom: 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speakers-web-2", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "~4.0.0-alpha.6" 6 | }, 7 | "overrides": { 8 | "bootstrap": { 9 | "main": [ 10 | "scss/bootstrap.scss", 11 | "dist/js/bootstrap.js", 12 | "dist/css/bootstrap.css" 13 | ] 14 | } 15 | }, 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "mocha": "^3.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": ">=4" 5 | }, 6 | "devDependencies": { 7 | "babel-core": "^6.4.0", 8 | "babel-preset-es2015": "^6.3.13", 9 | "babel-register": "^6.5.2", 10 | "browser-sync": "^2.2.1", 11 | "del": "^2.2.0", 12 | "gulp": "^3.9.0", 13 | "gulp-autoprefixer": "^3.0.1", 14 | "gulp-babel": "^6.1.1", 15 | "gulp-cache": "^0.4.2", 16 | "gulp-cssnano": "^2.0.0", 17 | "gulp-eslint": "^3.0.0", 18 | "gulp-htmlmin": "^3.0.0", 19 | "gulp-if": "^2.0.2", 20 | "gulp-imagemin": "^3.0.1", 21 | "gulp-load-plugins": "^1.2.4", 22 | "gulp-plumber": "^1.0.1", 23 | "gulp-size": "^2.1.0", 24 | "gulp-sourcemaps": "^2.2.0", 25 | "gulp-uglify": "^2.0.0", 26 | "gulp-useref": "^3.0.0", 27 | "main-bower-files": "^2.5.0", 28 | "run-sequence": "^1.2.2", 29 | "wiredep": "^4.0.0" 30 | }, 31 | "eslintConfig": { 32 | "env": { 33 | "es6": true, 34 | "node": true, 35 | "browser": true 36 | }, 37 | "rules": { 38 | "quotes": [ 39 | 2, 40 | "single" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-2/test/spec/test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | describe('Give it some context', function () { 5 | describe('maybe a bit more context here', function () { 6 | it('should run here few assertions', function () { 7 | 8 | }); 9 | }); 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "es2015" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.bowerrc: -------------------------------------------------------------------------------- 1 | { 2 | "directory": "bower_components" 3 | } 4 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | 8 | [*] 9 | 10 | # change these settings to your own preference 11 | indent_style = space 12 | indent_size = 2 13 | 14 | # we recommend you to keep these unchanged 15 | end_of_line = lf 16 | charset = utf-8 17 | trim_trailing_whitespace = true 18 | insert_final_newline = true 19 | 20 | [*.md] 21 | trim_trailing_whitespace = false 22 | 23 | [{package,bower}.json] 24 | indent_style = space 25 | indent_size = 2 26 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | dist 3 | .tmp 4 | bower_components 5 | test/bower_components 6 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/.yo-rc.json: -------------------------------------------------------------------------------- 1 | { 2 | "generator-mocha": { 3 | "ui": "bdd", 4 | "rjs": false 5 | } 6 | } -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/apple-touch-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-3/app/apple-touch-icon.png -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-2/speakers-web-3/app/favicon.ico -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/robots.txt: -------------------------------------------------------------------------------- 1 | # robotstxt.org/ 2 | 3 | User-agent: * 4 | Disallow: 5 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/scripts/main.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | console.log('Hello JSON at Work!'); 4 | 5 | $(document).ready(function() { 6 | 7 | function addSpeakersMustache(speakers) { 8 | var tbody = $('#speakers-tbody'); 9 | 10 | $.get('templates/speakers-mustache-template.html', function(templatePartial) { 11 | var template = $(templatePartial).filter('#speakerTemplate').html(); 12 | tbody.append(Mustache.render(template, speakers)); 13 | }).fail(function() { 14 | alert("Error loading Speakers mustache template"); 15 | }); 16 | 17 | } 18 | 19 | $.getJSON('http://localhost:5000/speakers', 20 | function(data) { 21 | addSpeakersMustache(data); 22 | } 23 | ); 24 | 25 | }); 26 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/styles/main.css: -------------------------------------------------------------------------------- 1 | .browserupgrade { 2 | margin: 0.2em 0; 3 | background: #ccc; 4 | color: #000; 5 | padding: 0.2em 0; 6 | } 7 | 8 | /* Space out content a bit */ 9 | body { 10 | padding-top: 1.5rem; 11 | padding-bottom: 1.5rem; 12 | } 13 | 14 | /* Everything but the jumbotron gets side spacing for mobile first views */ 15 | .header, 16 | .marketing, 17 | .footer { 18 | padding-right: 1rem; 19 | padding-left: 1rem; 20 | } 21 | 22 | /* Custom page header */ 23 | .header { 24 | padding-bottom: 1rem; 25 | border-bottom: .05rem solid #e5e5e5; 26 | } 27 | 28 | /* Make the masthead heading the same height as the navigation */ 29 | .header h3 { 30 | margin-top: 0; 31 | margin-bottom: 0; 32 | line-height: 3rem; 33 | } 34 | 35 | /* Custom page footer */ 36 | .footer { 37 | padding-top: 1.5rem; 38 | color: #777; 39 | border-top: .05rem solid #e5e5e5; 40 | } 41 | 42 | /* Customize container */ 43 | @media (min-width: 48em) { 44 | .container { 45 | max-width: 46rem; 46 | } 47 | } 48 | .container-narrow > hr { 49 | margin: 2rem 0; 50 | } 51 | 52 | /* Main marketing message and sign up button */ 53 | .jumbotron { 54 | text-align: center; 55 | border-bottom: .05rem solid #e5e5e5; 56 | } 57 | .jumbotron .btn { 58 | padding: .75rem 1.5rem; 59 | font-size: 1.5rem; 60 | } 61 | 62 | /* Supporting marketing content */ 63 | .marketing { 64 | margin: 3rem 0; 65 | } 66 | .marketing p + h4 { 67 | margin-top: 1.5rem; 68 | } 69 | 70 | /* Responsive: Portrait tablets and up */ 71 | @media screen and (min-width: 48em) { 72 | /* Remove the padding we set earlier */ 73 | .header, 74 | .marketing, 75 | .footer { 76 | padding-right: 0; 77 | padding-left: 0; 78 | } 79 | /* Space out the masthead */ 80 | .header { 81 | margin-bottom: 2rem; 82 | } 83 | /* Remove the bottom border on the jumbotron for visual effect */ 84 | .jumbotron { 85 | border-bottom: 0; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/app/templates/speakers-mustache-template.html: -------------------------------------------------------------------------------- 1 | 5 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speakers-web-3", 3 | "private": true, 4 | "dependencies": { 5 | "bootstrap": "~4.0.0-alpha.6" 6 | }, 7 | "overrides": { 8 | "bootstrap": { 9 | "main": [ 10 | "scss/bootstrap.scss", 11 | "dist/js/bootstrap.js", 12 | "dist/css/bootstrap.css" 13 | ] 14 | } 15 | }, 16 | "devDependencies": { 17 | "chai": "^3.5.0", 18 | "mocha": "^3.2.0" 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "engines": { 4 | "node": ">=4" 5 | }, 6 | "devDependencies": { 7 | "babel-core": "^6.4.0", 8 | "babel-preset-es2015": "^6.3.13", 9 | "babel-register": "^6.5.2", 10 | "browser-sync": "^2.2.1", 11 | "del": "^2.2.0", 12 | "gulp": "^3.9.0", 13 | "gulp-autoprefixer": "^3.0.1", 14 | "gulp-babel": "^6.1.1", 15 | "gulp-cache": "^0.4.2", 16 | "gulp-cssnano": "^2.0.0", 17 | "gulp-eslint": "^3.0.0", 18 | "gulp-htmlmin": "^3.0.0", 19 | "gulp-if": "^2.0.2", 20 | "gulp-imagemin": "^3.0.1", 21 | "gulp-load-plugins": "^1.2.4", 22 | "gulp-plumber": "^1.0.1", 23 | "gulp-size": "^2.1.0", 24 | "gulp-sourcemaps": "^2.2.0", 25 | "gulp-uglify": "^2.0.0", 26 | "gulp-useref": "^3.0.0", 27 | "main-bower-files": "^2.5.0", 28 | "run-sequence": "^1.2.2", 29 | "wiredep": "^4.0.0" 30 | }, 31 | "eslintConfig": { 32 | "env": { 33 | "es6": true, 34 | "node": true, 35 | "browser": true 36 | }, 37 | "rules": { 38 | "quotes": [ 39 | 2, 40 | "single" 41 | ] 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/test/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Mocha Spec Runner 6 | 7 | 8 | 9 |
10 | 11 | 12 | 13 | 18 | 19 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /chapter-2/speakers-web-3/test/spec/test.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | 'use strict'; 3 | 4 | describe('Give it some context', function () { 5 | describe('maybe a bit more context here', function () { 6 | it('should run here few assertions', function () { 7 | 8 | }); 9 | }); 10 | }); 11 | })(); 12 | -------------------------------------------------------------------------------- /chapter-3/README.md: -------------------------------------------------------------------------------- 1 | Chapter 3 Code 2 | ============== 3 | Code examples for Chapter 3 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-3/data/speakers.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [{ 3 | "about": "Incididunt mollit cupidatat magna excepteur do tempor ex non eiusmod magna exercitation proident nisi non. Sunt ad consequat eu non esse excepteur. Veniam quis Lorem ea labore ullamco veniam nisi do sunt. Nisi irure sit qui irure mollit ad aliquip non culpa sint reprehenderit ullamco.\r\n", 4 | "company": "Ecratic", 5 | "email": "larsonrichard@ecratic.com", 6 | "firstName": "Larson", 7 | "id": 0, 8 | "lastName": "Richard", 9 | "picture": "http://placehold.it/32x32", 10 | "tags": [ 11 | "JavaScript", 12 | "AngularJS", 13 | "Yeoman" 14 | ] 15 | }, { 16 | "about": "Labore tempor irure adipisicing consectetur velit. Ipsum Lorem non mollit aliquip. Fugiat est irure quis laboris minim anim esse fugiat et culpa exercitation. Dolor cillum excepteur officia Lorem ullamco magna et cupidatat dolor incididunt occaecat adipisicing consectetur in. Ullamco ullamco commodo nulla eiusmod. Lorem Lorem non sunt laboris ut et elit mollit deserunt nostrud est et id adipisicing.\r\n", 17 | "company": "Acusage", 18 | "email": "esterclements@acusage.com", 19 | "firstName": "Ester", 20 | "id": 1, 21 | "lastName": "Clements", 22 | "picture": "http://placehold.it/32x32", 23 | "tags": [ 24 | "REST", 25 | "Ruby on Rails", 26 | "APIs" 27 | ] 28 | }, { 29 | "about": "Proident ex Lorem et Lorem ad. Do voluptate officia minim in nisi ut sit nisi ex eu nostrud do ut. Aute ad dolor tempor dolor aute nisi deserunt deserunt ut deserunt cillum quis. Ipsum nulla sit reprehenderit consequat incididunt incididunt dolore et magna aliquip ut ex. Cupidatat exercitation ipsum dolore nisi incididunt anim est. Culpa veniam ut excepteur aliqua exercitation.\r\n", 30 | "company": "Talkola", 31 | "email": "christensenfisher@talkola.com", 32 | "firstName": "Christensen", 33 | "id": 2, 34 | "lastName": "Fisher", 35 | "picture": "http://placehold.it/32x32", 36 | "tags": [ 37 | "Java", 38 | "Spring", 39 | "Maven", 40 | "REST" 41 | ] 42 | }] 43 | } 44 | -------------------------------------------------------------------------------- /chapter-3/ruby/basic_data_types_serialize.rb: -------------------------------------------------------------------------------- 1 | require 'multi_json' 2 | 3 | puts "Current JSON Engine = #{MultiJson.current_adapter()}" 4 | puts 5 | 6 | age = 39 # Integer 7 | puts "age = #{MultiJson.dump(age)}" 8 | puts 9 | 10 | full_name = 'Larson Richard' # String 11 | puts "full_name = #{MultiJson.dump(full_name)}" 12 | puts 13 | 14 | reqistered = true # Boolean 15 | puts "reqistered = #{MultiJson.dump(reqistered)}" 16 | puts 17 | 18 | tags = %w(JavaScript, AngularJS, Yeoman) # Array of Strings 19 | puts "tags = #{MultiJson.dump(tags)}" 20 | puts 21 | 22 | email = { email: 'larsonrichard@ecratic.com' } # Hash 23 | puts "email = #{MultiJson.dump(email)}" 24 | puts 25 | 26 | class Speaker 27 | def initialize(first_name, last_name, email, about, 28 | company, tags, registered) 29 | @first_name = first_name 30 | @last_name = last_name 31 | @email = email 32 | @about = about 33 | @company = company 34 | @tags = tags 35 | @registered = registered 36 | end 37 | end 38 | 39 | speaker = Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 40 | 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 41 | 'Ecratic', %w(JavaScript, AngularJS, Yeoman), true) 42 | 43 | puts "speaker (using oj gem) = #{MultiJson.dump(speaker)}" 44 | puts 45 | -------------------------------------------------------------------------------- /chapter-3/ruby/obj_deserialize.rb: -------------------------------------------------------------------------------- 1 | require 'multi_json' 2 | require 'ostruct' 3 | require 'awesome_print' 4 | 5 | puts "Current JSON Engine = #{MultiJson.current_adapter()}" 6 | puts 7 | 8 | class Speaker 9 | def initialize(first_name, last_name, email, about, 10 | company, tags, registered) 11 | @first_name = first_name 12 | @last_name = last_name 13 | @email = email 14 | @about = about 15 | @company = company 16 | @tags = tags 17 | @registered = registered 18 | end 19 | end 20 | 21 | speaker = Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 22 | 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 23 | 'Ecratic', %w(JavaScript, AngularJS, Yeoman), true) 24 | 25 | json_speaker = MultiJson.dump(speaker, pretty: true) 26 | puts "speaker (using oj gem) = #{MultiJson.dump(speaker)}" 27 | puts 28 | 29 | ostruct_spkr = OpenStruct.new(MultiJson.load(json_speaker)) 30 | 31 | speaker2 = Speaker.new(ostruct_spkr.first_name, ostruct_spkr.last_name, 32 | ostruct_spkr.email, ostruct_spkr.about, ostruct_spkr.company, 33 | ostruct_spkr.tags, ostruct_spkr.registered) 34 | 35 | puts "speaker 2 after MultiJson.load()" 36 | ap speaker2 37 | puts 38 | -------------------------------------------------------------------------------- /chapter-3/ruby/obj_deserialize_active_support.rb: -------------------------------------------------------------------------------- 1 | require 'multi_json' 2 | require 'active_support/json' 3 | require 'active_support/core_ext/string' 4 | require 'ostruct' 5 | require 'awesome_print' 6 | 7 | class Speaker 8 | def initialize(first_name, last_name, email, about, 9 | company, tags, registered) 10 | @first_name = first_name 11 | @last_name = last_name 12 | @email = email 13 | @about = about 14 | @company = company 15 | @tags = tags 16 | @registered = registered 17 | end 18 | end 19 | 20 | speaker = Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 21 | 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 22 | 'Ecratic', %w(JavaScript, AngularJS, Yeoman), true) 23 | 24 | json_speaker = ActiveSupport::JSON.encode(speaker) 25 | puts "speaker (using oj gem) = #{ActiveSupport::JSON.encode(speaker)}" 26 | puts 27 | 28 | ostruct_spkr = OpenStruct.new(ActiveSupport::JSON.decode(json_speaker)) 29 | 30 | speaker2 = Speaker.new(ostruct_spkr.first_name, ostruct_spkr.last_name, 31 | ostruct_spkr.email, ostruct_spkr.about, ostruct_spkr.company, 32 | ostruct_spkr.tags, ostruct_spkr.registered) 33 | 34 | puts "speaker 2 after ActiveSupport::JSON.decode()" 35 | ap speaker2 36 | puts 37 | -------------------------------------------------------------------------------- /chapter-3/ruby/obj_serialize_active_support.rb: -------------------------------------------------------------------------------- 1 | require 'active_support/json' 2 | require 'active_support/core_ext/string' 3 | 4 | class Speaker 5 | def initialize(first_name, last_name, email, about, 6 | company, tags, registered) 7 | @first_name = first_name 8 | @last_name = last_name 9 | @email = email 10 | @about = about 11 | @company = company 12 | @tags = tags 13 | @registered = registered 14 | end 15 | end 16 | 17 | speaker = Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 18 | 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 19 | 'Ecratic', %w(JavaScript, AngularJS, Yeoman), true) 20 | 21 | json = ActiveSupport::JSON.encode(speaker).camelize(first_letter = :lower) 22 | puts "Speaker as camel-cased JSON \n#{json}" 23 | puts 24 | 25 | json = ActiveSupport::JSON.encode(speaker, 26 | only: ['first_name', 'last_name']) 27 | .camelize(first_letter = :lower) 28 | 29 | puts "Speaker as camel-cased JSON with only firstName and lastName \n#{json}" 30 | puts 31 | 32 | -------------------------------------------------------------------------------- /chapter-3/ruby/obj_serialize_pretty.rb: -------------------------------------------------------------------------------- 1 | require 'multi_json' 2 | 3 | puts "Current JSON Engine = #{MultiJson.current_adapter()}" 4 | puts 5 | 6 | class Speaker 7 | def initialize(first_name, last_name, email, about, 8 | company, tags, registered) 9 | @first_name = first_name 10 | @last_name = last_name 11 | @email = email 12 | @about = about 13 | @company = company 14 | @tags = tags 15 | @registered = registered 16 | end 17 | end 18 | 19 | speaker = Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 20 | 'Incididunt mollit cupidatat magna excepteur do tempor ex non ...', 21 | 'Ecratic', %w(JavaScript, AngularJS, Yeoman), true) 22 | 23 | puts "speaker (using oj gem) = #{MultiJson.dump(speaker, pretty: true)}" 24 | puts 25 | -------------------------------------------------------------------------------- /chapter-3/ruby/ostruct_example.rb: -------------------------------------------------------------------------------- 1 | require 'ostruct' 2 | 3 | h = { first_name: 'Fred' } 4 | m = OpenStruct.new(h) 5 | puts m # prints: # 6 | puts m.first_name # prints: Fred -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore Byebug command history file. 17 | .byebug_history 18 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | git_source(:github) do |repo_name| 4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 5 | "https://github.com/#{repo_name}.git" 6 | end 7 | 8 | 9 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 10 | gem 'rails', '~> 5.0.2' 11 | # Use Puma as the app server 12 | gem 'puma', '~> 3.0' 13 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 14 | # gem 'jbuilder', '~> 2.5' 15 | # Use ActiveModel has_secure_password 16 | # gem 'bcrypt', '~> 3.1.7' 17 | gem 'active_model_serializers' 18 | gem 'oj' 19 | 20 | # Use Capistrano for deployment 21 | # gem 'capistrano-rails', group: :development 22 | 23 | # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible 24 | # gem 'rack-cors' 25 | 26 | group :development, :test do 27 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 28 | gem 'byebug', platform: :mri 29 | end 30 | 31 | group :development do 32 | gem 'listen', '~> 3.0.5' 33 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 34 | gem 'spring' 35 | gem 'spring-watcher-listen', '~> 2.0.0' 36 | end 37 | 38 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 39 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 40 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | end 3 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-1/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/controllers/speakers_controller.rb: -------------------------------------------------------------------------------- 1 | class SpeakersController < ApplicationController 2 | before_action :set_speakers, only: [:index, :show] 3 | 4 | # GET /speakers 5 | def index 6 | render json: @speakers 7 | end 8 | 9 | # GET /speakers/:id 10 | def show 11 | id = params[:id].to_i - 1 12 | 13 | if id >= 0 && id < @speakers.length 14 | render json: @speakers[id] 15 | else 16 | render plain: '404 Not found', status: 404 17 | end 18 | end 19 | private 20 | 21 | def set_speakers 22 | @speakers = [] 23 | 24 | @speakers << Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 25 | 'Incididunt mollit cupidatat magna ...', 'Ecratic', 26 | ['JavaScript', 'AngularJS', 'Yeoman'], true) 27 | 28 | @speakers << Speaker.new('Ester', 'Clements', 'esterclements@acusage.com', 29 | 'Labore tempor irure adipisicing consectetur ...', 'Acusage', 30 | ['REST', 'Ruby on Rails', 'APIs'], true) 31 | 32 | @speakers << Speaker.new('Christensen', 'Fisher', 'christensenfisher@talkola.com', 33 | 'Proident ex Lorem et Lorem ad ...', 'Talkola', 34 | ['Java', 'Spring', 'Maven', 'REST'], true) 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-1/app/models/concerns/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/models/speaker.rb: -------------------------------------------------------------------------------- 1 | class Speaker < ActiveModelSerializers::Model 2 | attr_accessor :first_name, :last_name, :email, 3 | :about, :company, :tags, :registered 4 | 5 | def initialize(first_name, last_name, email, about, 6 | company, tags, registered) 7 | @first_name = first_name 8 | @last_name = last_name 9 | @email = email 10 | @about = about 11 | @company = company 12 | @tags = tags 13 | @registered = registered 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/app/serializers/speaker_serializer.rb: -------------------------------------------------------------------------------- 1 | class SpeakerSerializer < ActiveModel::Serializer 2 | attributes :first_name, :last_name, :email, 3 | :about, :company, :tags, :registered 4 | end 5 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | # require "active_record/railtie" 8 | require "action_controller/railtie" 9 | # require "action_mailer/railtie" 10 | require "action_view/railtie" 11 | # require "action_cable/engine" 12 | # require "sprockets/railtie" 13 | # require "rails/test_unit/railtie" 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | module SpeakersApi1 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | 25 | # Only loads a smaller set of middleware suitable for API only apps. 26 | # Middleware like session, flash, cookies can be added back manually. 27 | # Skip views, helpers and assets when generating a new resource. 28 | config.api_only = true 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Print deprecation notices to the Rails logger. 30 | config.active_support.deprecation = :log 31 | 32 | 33 | # Raises error for missing translations 34 | # config.action_view.raise_on_missing_translations = true 35 | 36 | # Use an evented file watcher to asynchronously detect changes in source code, 37 | # routes, locales, etc. This feature depends on the listen gem. 38 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 39 | end 40 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Print deprecation notices to the stderr. 32 | config.active_support.deprecation = :stderr 33 | 34 | # Raises error for missing translations 35 | # config.action_view.raise_on_missing_translations = true 36 | end 37 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/active_model_serializers.rb: -------------------------------------------------------------------------------- 1 | ActiveModelSerializers.config.key_transform = :camel_lower -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins 'example.com' 11 | # 12 | # resource '*', 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 6 | 7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 8 | # Previous versions had false. 9 | ActiveSupport.to_time_preserves_timezone = true 10 | 11 | # Do not halt callback chains when a callback returns false. Previous versions had true. 12 | ActiveSupport.halt_callback_chains_on_return_false = false 13 | 14 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 15 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 16 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | resources :speakers, :only => [:show, :index] 4 | 5 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 6 | end 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 031d84cf11d28d4e550928a30b1394d6e7f576ea3c9ed09e3a829d4e0bd8646a581f45288bdd01768de6d7a235d35fc5203cd5317232842dc92097d69a299374 15 | 16 | test: 17 | secret_key_base: e21cc5ed52c7d2d4031deaecd5e3e3486d6de790446bcc1301ce7822272633da05880aa14ad6219d148c572c90f6ae96f7d38cd25daac74a351dac3ec5bebe6d 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-1/lib/tasks/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-1/log/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-1/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-1/tmp/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files for more about ignoring files. 2 | # 3 | # If you find yourself ignoring temporary files generated by your text editor 4 | # or operating system, you probably want to add a global ignore instead: 5 | # git config --global core.excludesfile '~/.gitignore_global' 6 | 7 | # Ignore bundler config. 8 | /.bundle 9 | 10 | # Ignore all logfiles and tempfiles. 11 | /log/* 12 | /tmp/* 13 | !/log/.keep 14 | !/tmp/.keep 15 | 16 | # Ignore Byebug command history file. 17 | .byebug_history 18 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | git_source(:github) do |repo_name| 4 | repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/") 5 | "https://github.com/#{repo_name}.git" 6 | end 7 | 8 | 9 | # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' 10 | gem 'rails', '~> 5.0.2' 11 | # Use Puma as the app server 12 | gem 'puma', '~> 3.0' 13 | # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder 14 | # gem 'jbuilder', '~> 2.5' 15 | # Use ActiveModel has_secure_password 16 | # gem 'bcrypt', '~> 3.1.7' 17 | gem 'active_model_serializers' 18 | gem 'oj' 19 | 20 | # Use Capistrano for deployment 21 | # gem 'capistrano-rails', group: :development 22 | 23 | # Use Rack CORS for handling Cross-Origin Resource Sharing (CORS), making cross-origin AJAX possible 24 | # gem 'rack-cors' 25 | 26 | group :development, :test do 27 | # Call 'byebug' anywhere in the code to stop execution and get a debugger console 28 | gem 'byebug', platform: :mri 29 | end 30 | 31 | group :development do 32 | gem 'listen', '~> 3.0.5' 33 | # Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring 34 | gem 'spring' 35 | gem 'spring-watcher-listen', '~> 2.0.0' 36 | end 37 | 38 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 39 | gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby] 40 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative 'config/application' 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | end 3 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-2/app/controllers/concerns/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/controllers/speakers_controller.rb: -------------------------------------------------------------------------------- 1 | class SpeakersController < ApplicationController 2 | before_action :set_speakers, only: [:index, :show] 3 | 4 | # GET /speakers 5 | def index 6 | render json: @speakers 7 | end 8 | 9 | # GET /speakers/:id 10 | def show 11 | id = params[:id].to_i - 1 12 | 13 | if id >= 0 && id < @speakers.length 14 | render json: @speakers[id] 15 | else 16 | render plain: '404 Not found', status: 404 17 | end 18 | end 19 | private 20 | 21 | def set_speakers 22 | @speakers = [] 23 | 24 | @speakers << Speaker.new('Larson', 'Richard', 'larsonrichard@ecratic.com', 25 | 'Incididunt mollit cupidatat magna ...', 'Ecratic', 26 | ['JavaScript', 'AngularJS', 'Yeoman'], true) 27 | 28 | @speakers << Speaker.new('Ester', 'Clements', 'esterclements@acusage.com', 29 | 'Labore tempor irure adipisicing consectetur ...', 'Acusage', 30 | ['REST', 'Ruby on Rails', 'APIs'], true) 31 | 32 | @speakers << Speaker.new('Christensen', 'Fisher', 'christensenfisher@talkola.com', 33 | 'Proident ex Lorem et Lorem ad ...', 'Talkola', 34 | ['Java', 'Spring', 'Maven', 'REST'], true) 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | end 3 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/models/concerns/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-2/app/models/concerns/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/models/speaker.rb: -------------------------------------------------------------------------------- 1 | class Speaker < ActiveModelSerializers::Model 2 | attr_accessor :first_name, :last_name, :email, 3 | :about, :company, :tags, :registered 4 | 5 | def initialize(first_name, last_name, email, about, 6 | company, tags, registered) 7 | @first_name = first_name 8 | @last_name = last_name 9 | @email = email 10 | @about = about 11 | @company = company 12 | @tags = tags 13 | @registered = registered 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/app/serializers/speaker_serializer.rb: -------------------------------------------------------------------------------- 1 | class SpeakerSerializer < ActiveModel::Serializer 2 | attributes :name, :email, :about, 3 | :company, :tags, :registered 4 | 5 | def name 6 | "#{object.first_name} #{object.last_name}" 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/bundle: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) 3 | load Gem.bin_path('bundler', 'bundle') 4 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | APP_PATH = File.expand_path('../config/application', __dir__) 8 | require_relative '../config/boot' 9 | require 'rails/commands' 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | begin 3 | load File.expand_path('../spring', __FILE__) 4 | rescue LoadError => e 5 | raise unless e.message.include?('spring') 6 | end 7 | require_relative '../config/boot' 8 | require 'rake' 9 | Rake.application.run 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a starting point to setup your application. 15 | # Add necessary setup steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | # puts "\n== Copying sample files ==" 22 | # unless File.exist?('config/database.yml') 23 | # cp 'config/database.yml.sample', 'config/database.yml' 24 | # end 25 | 26 | puts "\n== Preparing database ==" 27 | system! 'bin/rails db:setup' 28 | 29 | puts "\n== Removing old logs and tempfiles ==" 30 | system! 'bin/rails log:clear tmp:clear' 31 | 32 | puts "\n== Restarting application server ==" 33 | system! 'bin/rails restart' 34 | end 35 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/spring: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | # This file loads spring without using Bundler, in order to be fast. 4 | # It gets overwritten when you run the `spring binstub` command. 5 | 6 | unless defined?(Spring) 7 | require 'rubygems' 8 | require 'bundler' 9 | 10 | lockfile = Bundler::LockfileParser.new(Bundler.default_lockfile.read) 11 | spring = lockfile.specs.detect { |spec| spec.name == "spring" } 12 | if spring 13 | Gem.use_paths Gem.dir, Bundler.bundle_path.to_s, *Gem.path 14 | gem 'spring', spring.version 15 | require 'spring/binstub' 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/bin/update: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require 'pathname' 3 | require 'fileutils' 4 | include FileUtils 5 | 6 | # path to your application root. 7 | APP_ROOT = Pathname.new File.expand_path('../../', __FILE__) 8 | 9 | def system!(*args) 10 | system(*args) || abort("\n== Command #{args} failed ==") 11 | end 12 | 13 | chdir APP_ROOT do 14 | # This script is a way to update your development environment automatically. 15 | # Add necessary update steps to this file. 16 | 17 | puts '== Installing dependencies ==' 18 | system! 'gem install bundler --conservative' 19 | system('bundle check') || system!('bundle install') 20 | 21 | puts "\n== Updating database ==" 22 | system! 'bin/rails db:migrate' 23 | 24 | puts "\n== Removing old logs and tempfiles ==" 25 | system! 'bin/rails log:clear tmp:clear' 26 | 27 | puts "\n== Restarting application server ==" 28 | system! 'bin/rails restart' 29 | end 30 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative 'config/environment' 4 | 5 | run Rails.application 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require "rails" 4 | # Pick the frameworks you want: 5 | require "active_model/railtie" 6 | require "active_job/railtie" 7 | # require "active_record/railtie" 8 | require "action_controller/railtie" 9 | # require "action_mailer/railtie" 10 | require "action_view/railtie" 11 | # require "action_cable/engine" 12 | # require "sprockets/railtie" 13 | # require "rails/test_unit/railtie" 14 | 15 | # Require the gems listed in Gemfile, including any gems 16 | # you've limited to :test, :development, or :production. 17 | Bundler.require(*Rails.groups) 18 | 19 | module SpeakersApi2 20 | class Application < Rails::Application 21 | # Settings in config/environments/* take precedence over those specified here. 22 | # Application configuration should go into files in config/initializers 23 | # -- all .rb files in that directory are automatically loaded. 24 | 25 | # Only loads a smaller set of middleware suitable for API only apps. 26 | # Middleware like session, flash, cookies can be added back manually. 27 | # Skip views, helpers and assets when generating a new resource. 28 | config.api_only = true 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/boot.rb: -------------------------------------------------------------------------------- 1 | ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../Gemfile', __dir__) 2 | 3 | require 'bundler/setup' # Set up gems listed in the Gemfile. 4 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative 'application' 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # In the development environment your application's code is reloaded on 5 | # every request. This slows down response time but is perfect for development 6 | # since you don't have to restart the web server when you make code changes. 7 | config.cache_classes = false 8 | 9 | # Do not eager load code on boot. 10 | config.eager_load = false 11 | 12 | # Show full error reports. 13 | config.consider_all_requests_local = true 14 | 15 | # Enable/disable caching. By default caching is disabled. 16 | if Rails.root.join('tmp/caching-dev.txt').exist? 17 | config.action_controller.perform_caching = true 18 | 19 | config.cache_store = :memory_store 20 | config.public_file_server.headers = { 21 | 'Cache-Control' => 'public, max-age=172800' 22 | } 23 | else 24 | config.action_controller.perform_caching = false 25 | 26 | config.cache_store = :null_store 27 | end 28 | 29 | # Print deprecation notices to the Rails logger. 30 | config.active_support.deprecation = :log 31 | 32 | 33 | # Raises error for missing translations 34 | # config.action_view.raise_on_missing_translations = true 35 | 36 | # Use an evented file watcher to asynchronously detect changes in source code, 37 | # routes, locales, etc. This feature depends on the listen gem. 38 | config.file_watcher = ActiveSupport::EventedFileUpdateChecker 39 | end 40 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | Rails.application.configure do 2 | # Settings specified here will take precedence over those in config/application.rb. 3 | 4 | # The test environment is used exclusively to run your application's 5 | # test suite. You never need to work with it otherwise. Remember that 6 | # your test database is "scratch space" for the test suite and is wiped 7 | # and recreated between test runs. Don't rely on the data there! 8 | config.cache_classes = true 9 | 10 | # Do not eager load code on boot. This avoids loading your whole application 11 | # just for the purpose of running a single test. If you are using a tool that 12 | # preloads Rails for running tests, you may have to set it to true. 13 | config.eager_load = false 14 | 15 | # Configure public file server for tests with Cache-Control for performance. 16 | config.public_file_server.enabled = true 17 | config.public_file_server.headers = { 18 | 'Cache-Control' => 'public, max-age=3600' 19 | } 20 | 21 | # Show full error reports and disable caching. 22 | config.consider_all_requests_local = true 23 | config.action_controller.perform_caching = false 24 | 25 | # Raise exceptions instead of rendering exception templates. 26 | config.action_dispatch.show_exceptions = false 27 | 28 | # Disable request forgery protection in test environment. 29 | config.action_controller.allow_forgery_protection = false 30 | 31 | # Print deprecation notices to the stderr. 32 | config.active_support.deprecation = :stderr 33 | 34 | # Raises error for missing translations 35 | # config.action_view.raise_on_missing_translations = true 36 | end 37 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/active_model_serializers.rb: -------------------------------------------------------------------------------- 1 | ActiveModelSerializers.config.key_transform = :camel_lower -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/application_controller_renderer.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # ApplicationController.renderer.defaults.merge!( 4 | # http_host: 'example.org', 5 | # https: false 6 | # ) 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/backtrace_silencers.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # You can add backtrace silencers for libraries that you're using but don't wish to see in your backtraces. 4 | # Rails.backtrace_cleaner.add_silencer { |line| line =~ /my_noisy_library/ } 5 | 6 | # You can also remove all the silencers if you're trying to debug a problem that might stem from framework code. 7 | # Rails.backtrace_cleaner.remove_silencers! 8 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/cors.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Avoid CORS issues when API is called from the frontend app. 4 | # Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin AJAX requests. 5 | 6 | # Read more: https://github.com/cyu/rack-cors 7 | 8 | # Rails.application.config.middleware.insert_before 0, Rack::Cors do 9 | # allow do 10 | # origins 'example.com' 11 | # 12 | # resource '*', 13 | # headers: :any, 14 | # methods: [:get, :post, :put, :patch, :delete, :options, :head] 15 | # end 16 | # end 17 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure sensitive parameters which will be filtered from the log file. 4 | Rails.application.config.filter_parameters += [:password] 5 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, '\1en' 8 | # inflect.singular /^(ox)en/i, '\1' 9 | # inflect.irregular 'person', 'people' 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym 'RESTful' 16 | # end 17 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/mime_types.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new mime types for use in respond_to blocks: 4 | # Mime::Type.register "text/richtext", :rtf 5 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/new_framework_defaults.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | # 3 | # This file contains migration options to ease your Rails 5.0 upgrade. 4 | # 5 | # Read the Guide for Upgrading Ruby on Rails for more info on each option. 6 | 7 | # Make Ruby 2.4 preserve the timezone of the receiver when calling `to_time`. 8 | # Previous versions had false. 9 | ActiveSupport.to_time_preserves_timezone = true 10 | 11 | # Do not halt callback chains when a callback returns false. Previous versions had true. 12 | ActiveSupport.halt_callback_chains_on_return_false = false 13 | 14 | # Configure SSL options to enable HSTS with subdomains. Previous versions had false. 15 | Rails.application.config.ssl_options = { hsts: { subdomains: true } } 16 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/initializers/wrap_parameters.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # This file contains settings for ActionController::ParamsWrapper which 4 | # is enabled by default. 5 | 6 | # Enable parameter wrapping for JSON. You can disable this by setting :format to an empty array. 7 | ActiveSupport.on_load(:action_controller) do 8 | wrap_parameters format: [:json] 9 | end 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization 2 | # and are automatically loaded by Rails. If you want to use locales other 3 | # than English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t 'hello' 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t('hello') %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more, please read the Rails Internationalization guide 20 | # available at http://guides.rubyonrails.org/i18n.html. 21 | 22 | en: 23 | hello: "Hello world" 24 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/puma.rb: -------------------------------------------------------------------------------- 1 | # Puma can serve each request in a thread from an internal thread pool. 2 | # The `threads` method setting takes two numbers a minimum and maximum. 3 | # Any libraries that use thread pools should be configured to match 4 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 5 | # and maximum, this matches the default thread size of Active Record. 6 | # 7 | threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 }.to_i 8 | threads threads_count, threads_count 9 | 10 | # Specifies the `port` that Puma will listen on to receive requests, default is 3000. 11 | # 12 | port ENV.fetch("PORT") { 3000 } 13 | 14 | # Specifies the `environment` that Puma will run in. 15 | # 16 | environment ENV.fetch("RAILS_ENV") { "development" } 17 | 18 | # Specifies the number of `workers` to boot in clustered mode. 19 | # Workers are forked webserver processes. If using threads and workers together 20 | # the concurrency of the application would be max `threads` * `workers`. 21 | # Workers do not work on JRuby or Windows (both of which do not support 22 | # processes). 23 | # 24 | # workers ENV.fetch("WEB_CONCURRENCY") { 2 } 25 | 26 | # Use the `preload_app!` method when specifying a `workers` number. 27 | # This directive tells Puma to first boot the application and load code 28 | # before forking the application. This takes advantage of Copy On Write 29 | # process behavior so workers use less memory. If you use this option 30 | # you need to make sure to reconnect any threads in the `on_worker_boot` 31 | # block. 32 | # 33 | # preload_app! 34 | 35 | # The code in the `on_worker_boot` will be called if you are using 36 | # clustered mode by specifying a number of `workers`. After each worker 37 | # process is booted this block will be run, if you are using `preload_app!` 38 | # option you will want to use this block to reconnect to any threads 39 | # or connections that may have been created at application boot, Ruby 40 | # cannot share connections between processes. 41 | # 42 | # on_worker_boot do 43 | # ActiveRecord::Base.establish_connection if defined?(ActiveRecord) 44 | # end 45 | 46 | # Allow puma to be restarted by `rails restart` command. 47 | plugin :tmp_restart 48 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | 3 | resources :speakers, :only => [:show, :index] 4 | 5 | # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html 6 | end 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/secrets.yml: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Your secret key is used for verifying the integrity of signed cookies. 4 | # If you change this key, all old signed cookies will become invalid! 5 | 6 | # Make sure the secret is at least 30 characters and all random, 7 | # no regular words or you'll be exposed to dictionary attacks. 8 | # You can use `rails secret` to generate a secure secret key. 9 | 10 | # Make sure the secrets in this file are kept private 11 | # if you're sharing your code publicly. 12 | 13 | development: 14 | secret_key_base: 94e8433b9e15ba92289e640af223e4c0b554b3d10d6751876fda8d28f01a95b21c3e8a017783e5d234666fee8d4425844418299135082526fb2e31e0b98f42c5 15 | 16 | test: 17 | secret_key_base: 405fe7ae260c4c7330b821edc0e78c9121e43b6ded9ec6c8788e4e69b19f02522edb2466fb261c252607430f442e3a6423e93f656109b13a1833d9044046757a 18 | 19 | # Do not keep production secrets in the repository, 20 | # instead read values from the environment. 21 | production: 22 | secret_key_base: <%= ENV["SECRET_KEY_BASE"] %> 23 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/config/spring.rb: -------------------------------------------------------------------------------- 1 | %w( 2 | .ruby-version 3 | .rbenv-vars 4 | tmp/restart.txt 5 | tmp/caching-dev.txt 6 | ).each { |path| Spring.watch(path) } 7 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/db/seeds.rb: -------------------------------------------------------------------------------- 1 | # This file should contain all the record creation needed to seed the database with its default values. 2 | # The data can then be loaded with the rails db:seed command (or created alongside the database with db:setup). 3 | # 4 | # Examples: 5 | # 6 | # movies = Movie.create([{ name: 'Star Wars' }, { name: 'Lord of the Rings' }]) 7 | # Character.create(name: 'Luke', movie: movies.first) 8 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/lib/tasks/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-2/lib/tasks/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/log/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-2/log/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/public/robots.txt: -------------------------------------------------------------------------------- 1 | # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file 2 | # 3 | # To ban all spiders from the entire site uncomment the next two lines: 4 | # User-agent: * 5 | # Disallow: / 6 | -------------------------------------------------------------------------------- /chapter-3/speakers-api-2/tmp/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-3/speakers-api-2/tmp/.keep -------------------------------------------------------------------------------- /chapter-3/speakers-test/.bundle/config: -------------------------------------------------------------------------------- 1 | --- 2 | BUNDLE_PATH: "vendor/bundle" 3 | BUNDLE_DISABLE_SHARED_GEMS: "true" 4 | -------------------------------------------------------------------------------- /chapter-3/speakers-test/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'rake' 4 | gem 'minitest' 5 | gem 'minitest-reporters' 6 | gem 'unirest' 7 | gem 'awesome_print' 8 | gem 'plissken' 9 | gem 'ruby-jq' 10 | -------------------------------------------------------------------------------- /chapter-3/speakers-test/Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | activemodel (4.2.8) 5 | activesupport (= 4.2.8) 6 | builder (~> 3.1) 7 | activesupport (4.2.8) 8 | i18n (~> 0.7) 9 | minitest (~> 5.1) 10 | thread_safe (~> 0.3, >= 0.3.4) 11 | tzinfo (~> 1.1) 12 | addressable (2.3.8) 13 | ansi (1.5.0) 14 | awesome_print (1.7.0) 15 | builder (3.2.3) 16 | i18n (0.8.1) 17 | json (1.8.6) 18 | mime-types (1.25.1) 19 | minitest (5.10.1) 20 | minitest-reporters (1.1.14) 21 | ansi 22 | builder 23 | minitest (>= 5.0) 24 | ruby-progressbar 25 | multi_json (1.12.1) 26 | plissken (0.3.0) 27 | symbolize (~> 4.2) 28 | rake (12.0.0) 29 | rest-client (1.6.9) 30 | mime-types (~> 1.16) 31 | ruby-jq (0.1.6) 32 | multi_json 33 | ruby-progressbar (1.8.1) 34 | symbolize (4.5.2) 35 | activemodel (>= 3.2, < 5) 36 | activesupport (>= 3.2, < 5) 37 | i18n 38 | thread_safe (0.3.6) 39 | tzinfo (1.2.3) 40 | thread_safe (~> 0.1) 41 | unirest (1.1.2) 42 | addressable (~> 2.3.5) 43 | json (~> 1.8.1) 44 | rest-client (~> 1.6.7) 45 | 46 | PLATFORMS 47 | ruby 48 | 49 | DEPENDENCIES 50 | awesome_print 51 | minitest 52 | minitest-reporters 53 | plissken 54 | rake 55 | ruby-jq 56 | unirest 57 | 58 | BUNDLED WITH 59 | 1.14.6 60 | -------------------------------------------------------------------------------- /chapter-3/speakers-test/Rakefile: -------------------------------------------------------------------------------- 1 | require 'rake/testtask' 2 | 3 | Rake::TestTask.new(:test) do |t| 4 | t.libs = %w(lib test) 5 | t.pattern = 'test/**/*_spec.rb' 6 | t.warning = false 7 | end 8 | 9 | task :default => :test -------------------------------------------------------------------------------- /chapter-3/speakers-test/models/speaker.rb: -------------------------------------------------------------------------------- 1 | class Speaker 2 | attr_accessor :first_name, :last_name, :email, 3 | :about, :company, :tags, :registered 4 | 5 | def initialize(first_name, last_name, email, about, 6 | company, tags, registered) 7 | @first_name = first_name 8 | @last_name = last_name 9 | @email = email 10 | @about = about 11 | @company = company 12 | @tags = tags 13 | @registered = registered 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /chapter-3/speakers-test/test/minitest_helper.rb: -------------------------------------------------------------------------------- 1 | require 'minitest/spec' 2 | require 'minitest/autorun' 3 | 4 | require "minitest/reporters" 5 | Minitest::Reporters.use! Minitest::Reporters::SpecReporter.new -------------------------------------------------------------------------------- /chapter-4/README.md: -------------------------------------------------------------------------------- 1 | Chapter 4 Code 2 | ============== 3 | Code examples for Chapter 4 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | 5 | -------------------------------------------------------------------------------- /chapter-4/speakers-api/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE") 7 | } 8 | } 9 | 10 | apply plugin: 'java' 11 | apply plugin: 'org.springframework.boot' 12 | 13 | ext { 14 | jdkVersion = "1.8" 15 | } 16 | 17 | sourceCompatibility = jdkVersion 18 | targetCompatibility = jdkVersion 19 | 20 | tasks.withType(JavaCompile) { 21 | options.encoding = 'UTF-8' 22 | } 23 | 24 | jar { 25 | baseName = 'speakers-api' 26 | version = '0.0.1' 27 | } 28 | 29 | repositories { 30 | mavenCentral() 31 | } 32 | 33 | test { 34 | testLogging { 35 | showStandardStreams = true // Show standard output & standard error. 36 | } 37 | ignoreFailures = false 38 | } 39 | 40 | dependencies { 41 | compile ( 42 | [group: 'org.springframework.boot', name: 'spring-boot-starter-web'] 43 | ) 44 | } -------------------------------------------------------------------------------- /chapter-4/speakers-api/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-4/speakers-api/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/speakers-api/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 30 21:48:53 MDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip 7 | -------------------------------------------------------------------------------- /chapter-4/speakers-api/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * In a single project build this file can be empty or even removed. 6 | * 7 | * Detailed information about configuring a multi-project build in Gradle can be found 8 | * in the user guide at https://docs.gradle.org/3.4.1/userguide/multi_project_builds.html 9 | */ 10 | 11 | /* 12 | // To declare projects as part of a multi-project build use the 'include' method 13 | include 'shared' 14 | include 'api' 15 | include 'services:webservice' 16 | */ 17 | 18 | rootProject.name = 'speakers-api' 19 | -------------------------------------------------------------------------------- /chapter-4/speakers-api/src/main/java/org/jsonatwork/ch4/Application.java: -------------------------------------------------------------------------------- 1 | package org.jsonatwork.ch4; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Application { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(Application.class, args); 11 | } 12 | } -------------------------------------------------------------------------------- /chapter-4/speakers-api/src/main/java/org/jsonatwork/ch4/Speaker.java: -------------------------------------------------------------------------------- 1 | package org.jsonatwork.ch4; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class Speaker { 8 | private int id; 9 | private int age; 10 | private String fullName; 11 | private List tags = new ArrayList(); 12 | private boolean registered; 13 | 14 | public Speaker() { 15 | super(); 16 | } 17 | 18 | public Speaker(int id, int age, String fullName, List tags, boolean registered) { 19 | super(); 20 | this.id = id; 21 | this.age = age; 22 | this.fullName = fullName; 23 | this.tags = tags; 24 | this.registered = registered; 25 | } 26 | 27 | public Speaker(int id, int age, String fullName, String[] tags, boolean registered) { 28 | this(id, age, fullName, Arrays.asList(tags), registered); 29 | } 30 | 31 | public int getId() { 32 | return id; 33 | } 34 | 35 | public void setId(int id) { 36 | this.id = id; 37 | } 38 | 39 | public int getAge() { 40 | return age; 41 | } 42 | 43 | public void setAge(int age) { 44 | this.age = age; 45 | } 46 | 47 | public String getFullName() { 48 | return fullName; 49 | } 50 | 51 | public void setFullName(String fullName) { 52 | this.fullName = fullName; 53 | } 54 | 55 | public List getTags() { 56 | return tags; 57 | } 58 | 59 | public void setTags(List tags) { 60 | this.tags = tags; 61 | } 62 | 63 | public boolean isRegistered() { 64 | return registered; 65 | } 66 | 67 | public void setRegistered(boolean registered) { 68 | this.registered = registered; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return String.format("Speaker [id=%s, age=%s, fullName=%s, tags=%s, registered=%s]", id, age, fullName, tags, 74 | registered); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /chapter-4/speakers-api/src/main/java/org/jsonatwork/ch4/SpeakerController.java: -------------------------------------------------------------------------------- 1 | package org.jsonatwork.ch4; 2 | 3 | import java.util.*; 4 | import org.springframework.web.bind.annotation.*; 5 | import org.springframework.http.*; 6 | 7 | @RestController 8 | public class SpeakerController { 9 | 10 | private static Speaker speakers[] = { 11 | new Speaker(1, 39, "Larson Richard", new String[] {"JavaScript", "AngularJS", "Yeoman"}, true), 12 | new Speaker(2, 29, "Ester Clements", new String[] {"REST", "Ruby on Rails", "APIs"}, true), 13 | new Speaker(3, 45, "Christensen Fisher", new String[] {"Java", "Spring", "Maven", "REST"}, false) 14 | }; 15 | 16 | @RequestMapping(value = "/speakers", method = RequestMethod.GET) 17 | public List getAllSpeakers() { 18 | return Arrays.asList(speakers); 19 | } 20 | 21 | @RequestMapping(value = "/speakers/{id}", method = RequestMethod.GET) 22 | public ResponseEntity getSpeakerById(@PathVariable long id) { 23 | int tempId = ((new Long(id)).intValue() - 1); 24 | 25 | if (tempId >= 0 && tempId < speakers.length) { 26 | return new ResponseEntity(speakers[tempId], HttpStatus.OK); 27 | } else { 28 | return new ResponseEntity(HttpStatus.NOT_FOUND); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /chapter-4/speakers-test/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | ext { 4 | jdkVersion = "1.8" 5 | jacksonVersion = "2.8.7" 6 | jsonUnitVersion = "1.20.0" 7 | commonsIoVersion = "1.3.2" 8 | unirestVersion = "1.4.9" 9 | junitVersion = "4.12" 10 | } 11 | 12 | sourceCompatibility = jdkVersion 13 | targetCompatibility = jdkVersion 14 | 15 | tasks.withType(JavaCompile) { 16 | options.encoding = 'UTF-8' 17 | } 18 | 19 | repositories { 20 | mavenCentral() 21 | } 22 | 23 | test { 24 | testLogging { 25 | showStandardStreams = true // Show standard output & standard error. 26 | } 27 | ignoreFailures = false 28 | } 29 | 30 | dependencies { 31 | compile ( 32 | [group: 'com.fasterxml.jackson.core', name: 'jackson-core', version: jacksonVersion], 33 | [group: 'com.fasterxml.jackson.core', name: 'jackson-annotations', version: jacksonVersion], 34 | [group: 'com.fasterxml.jackson.core', name: 'jackson-databind', version: jacksonVersion], 35 | [group: 'org.apache.commons', name: 'commons-io', version: commonsIoVersion], 36 | [group: 'com.mashape.unirest', name: 'unirest-java', version: unirestVersion] 37 | ) 38 | testCompile ( 39 | [group: 'junit', name: 'junit', version: junitVersion], 40 | [group: 'net.javacrumbs.json-unit', name: 'json-unit', version: jsonUnitVersion], 41 | [group: 'net.javacrumbs.json-unit', name: 'json-unit-fluent', version: jsonUnitVersion] 42 | ) 43 | } 44 | -------------------------------------------------------------------------------- /chapter-4/speakers-test/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmarrs/json-at-work-examples/c6bfd049f2bd111f58918673ccc84dcb024016c4/chapter-4/speakers-test/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /chapter-4/speakers-test/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Mar 30 20:06:46 MDT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-3.4.1-bin.zip 7 | -------------------------------------------------------------------------------- /chapter-4/speakers-test/settings.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * This settings file was generated by the Gradle 'init' task. 3 | * 4 | * The settings file is used to specify which projects to include in your build. 5 | * In a single project build this file can be empty or even removed. 6 | * 7 | * Detailed information about configuring a multi-project build in Gradle can be found 8 | * in the user guide at https://docs.gradle.org/3.4.1/userguide/multi_project_builds.html 9 | */ 10 | 11 | /* 12 | // To declare projects as part of a multi-project build use the 'include' method 13 | include 'shared' 14 | include 'api' 15 | include 'services:webservice' 16 | */ 17 | 18 | rootProject.name = 'speakers-test' 19 | -------------------------------------------------------------------------------- /chapter-4/speakers-test/src/main/java/org/jsonatwork/ch4/Speaker.java: -------------------------------------------------------------------------------- 1 | package org.jsonatwork.ch4; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | 7 | public class Speaker { 8 | private int id; 9 | private int age; 10 | private String fullName; 11 | private List tags = new ArrayList(); 12 | private boolean registered; 13 | 14 | public Speaker() { 15 | super(); 16 | } 17 | 18 | public Speaker(int id, int age, String fullName, List tags, boolean registered) { 19 | super(); 20 | this.id = id; 21 | this.age = age; 22 | this.fullName = fullName; 23 | this.tags = tags; 24 | this.registered = registered; 25 | } 26 | 27 | public Speaker(int id, int age, String fullName, String[] tags, boolean registered) { 28 | this(id, age, fullName, Arrays.asList(tags), registered); 29 | } 30 | 31 | public int getId() { 32 | return id; 33 | } 34 | 35 | public void setId(int id) { 36 | this.id = id; 37 | } 38 | 39 | public int getAge() { 40 | return age; 41 | } 42 | 43 | public void setAge(int age) { 44 | this.age = age; 45 | } 46 | 47 | public String getFullName() { 48 | return fullName; 49 | } 50 | 51 | public void setFullName(String fullName) { 52 | this.fullName = fullName; 53 | } 54 | 55 | public List getTags() { 56 | return tags; 57 | } 58 | 59 | public void setTags(List tags) { 60 | this.tags = tags; 61 | } 62 | 63 | public boolean isRegistered() { 64 | return registered; 65 | } 66 | 67 | public void setRegistered(boolean registered) { 68 | this.registered = registered; 69 | } 70 | 71 | @Override 72 | public String toString() { 73 | return String.format("Speaker [id=%s, age=%s, fullName=%s, tags=%s, registered=%s]", id, age, fullName, tags, 74 | registered); 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /chapter-4/speakers-test/src/test/resources/speaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1, 3 | "fullName": "Larson Richard", 4 | "tags": [ 5 | "JavaScript", "AngularJS", "Yeoman" 6 | ], 7 | "age": 39, 8 | "registered": true 9 | } 10 | -------------------------------------------------------------------------------- /chapter-4/speakers-test/src/test/resources/speakers.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [ 3 | { 4 | "id": 1, 5 | "fullName": "Larson Richard", 6 | "tags": [ 7 | "JavaScript", 8 | "AngularJS", 9 | "Yeoman" 10 | ], 11 | "age": 39, 12 | "registered": true 13 | }, 14 | { 15 | "id": 2, 16 | "fullName": "Ester Clements", 17 | "tags": [ 18 | "REST", 19 | "Ruby on Rails", 20 | "APIs" 21 | ], 22 | "age": 29, 23 | "registered": true 24 | }, 25 | { 26 | "id": 3, 27 | "fullName": "Christensen Fisher", 28 | "tags": [ 29 | "Java", 30 | "Spring", 31 | "Maven", 32 | "REST" 33 | ], 34 | "age": 45, 35 | "registered": false 36 | } 37 | ] 38 | } -------------------------------------------------------------------------------- /chapter-5/README.md: -------------------------------------------------------------------------------- 1 | Chapter 5 Code 2 | ============== 3 | Code examples for Chapter 5 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-5/ex-1-basic-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /chapter-5/ex-1-basic.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-10-pattern-properties-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "line1": "555 Main Street", 3 | "line4": "#2", 4 | "city": "Denver", 5 | "state": "CO", 6 | "zip": "80231", 7 | "country": "USA" 8 | } 9 | -------------------------------------------------------------------------------- /chapter-5/ex-10-pattern-properties-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "city": { 6 | "type": "string" 7 | }, 8 | "state": { 9 | "type": "string" 10 | }, 11 | "zip": { 12 | "type": "string" 13 | }, 14 | "country": { 15 | "type": "string" 16 | } 17 | }, 18 | "patternProperties": { 19 | "^line[1-3]$": { 20 | "type": "string" 21 | } 22 | }, 23 | "additionalProperties": false, 24 | "required": ["city", "state", "zip", "country", "line1"] 25 | } 26 | -------------------------------------------------------------------------------- /chapter-5/ex-10-pattern-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "line1": "555 Main Street", 3 | "line2": "#2", 4 | "city": "Denver", 5 | "state": "CO", 6 | "zip": "80231", 7 | "country": "USA" 8 | } 9 | -------------------------------------------------------------------------------- /chapter-5/ex-11-regex-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-11-regex-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string", 7 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 8 | }, 9 | "firstName": { 10 | "type": "string" 11 | }, 12 | "lastName": { 13 | "type": "string" 14 | } 15 | }, 16 | "additionalProperties": false, 17 | "required": ["email", "firstName", "lastName"] 18 | } 19 | -------------------------------------------------------------------------------- /chapter-5/ex-11-regex.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-12-dependent-properties-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "favoriteTopic": "JavaScript" 6 | } 7 | -------------------------------------------------------------------------------- /chapter-5/ex-12-dependent-properties-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string", 7 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 8 | }, 9 | "firstName": { 10 | "type": "string" 11 | }, 12 | "lastName": { 13 | "type": "string" 14 | }, 15 | "tags": { 16 | "type": "array", 17 | "items": { 18 | "type": "string" 19 | } 20 | }, 21 | "favoriteTopic": { 22 | "type": "string" 23 | } 24 | }, 25 | "additionalProperties": false, 26 | "required": ["email", "firstName", "lastName"], 27 | "dependencies": { 28 | "favoriteTopic": ["tags"] 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /chapter-5/ex-12-dependent-properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "tags": [ 6 | "JavaScript", "AngularJS", "Yeoman" 7 | ], 8 | "favoriteTopic": "JavaScript" 9 | } 10 | -------------------------------------------------------------------------------- /chapter-5/ex-13-internal-ref-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-13-internal-ref-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "$ref": "#/definitions/emailPattern" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | } 14 | }, 15 | "additionalProperties": false, 16 | "required": ["email", "firstName", "lastName"], 17 | "definitions": { 18 | "emailPattern": { 19 | "type": "string", 20 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /chapter-5/ex-13-internal-ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-14-external-ref-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-14-external-ref-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "$ref": "http://localhost:8081/ex-14-my-common-schema.json#/definitions/emailPattern" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | } 14 | }, 15 | "additionalProperties": false, 16 | "required": ["email", "firstName", "lastName"] 17 | } 18 | -------------------------------------------------------------------------------- /chapter-5/ex-14-external-ref.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard" 5 | } 6 | -------------------------------------------------------------------------------- /chapter-5/ex-14-my-common-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "http://localhost:8081/ex-14-my-common-schema.json", 4 | 5 | "definitions": { 6 | "emailPattern": { 7 | "type": "string", 8 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-5/ex-15-one-of-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": true, 6 | "rating": 1.9 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-15-one-of-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string", 7 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 8 | }, 9 | "firstName": { 10 | "type": "string" 11 | }, 12 | "lastName": { 13 | "type": "string" 14 | }, 15 | "postedSlides": { 16 | "type": "boolean" 17 | }, 18 | "rating": { 19 | "type": "number", 20 | "oneOf": [{ 21 | "maximum": 2 22 | }, { 23 | "maximum": 5 24 | }] 25 | } 26 | }, 27 | "additionalProperties": false, 28 | "required": [ 29 | "email", 30 | "firstName", 31 | "lastName", 32 | "postedSlides", 33 | "rating" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /chapter-5/ex-15-one-of.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": true, 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-16-any-of-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": "maybe", 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-16-any-of-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string", 7 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 8 | }, 9 | "firstName": { 10 | "type": "string" 11 | }, 12 | "lastName": { 13 | "type": "string" 14 | }, 15 | "postedSlides": { 16 | "anyOf": [{ 17 | "type": "boolean" 18 | }, { 19 | "type": "string", 20 | "enum": [ 21 | "yes", 22 | "Yes", 23 | "no", 24 | "No" 25 | ] 26 | }] 27 | }, 28 | "rating": { 29 | "type": "number" 30 | } 31 | }, 32 | "additionalProperties": false, 33 | "required": [ 34 | "email", 35 | "firstName", 36 | "lastName", 37 | "postedSlides", 38 | "rating" 39 | ] 40 | } 41 | -------------------------------------------------------------------------------- /chapter-5/ex-16-any-of.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": "yes", 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-17-all-of-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "ThisLastNameIsWayTooLong", 5 | "postedSlides": true, 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-17-all-of-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string", 7 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 8 | }, 9 | "firstName": { 10 | "type": "string" 11 | }, 12 | "lastName": { 13 | "allOf": [{ 14 | "type": "string" 15 | }, { 16 | "maxLength": 20 17 | }] 18 | }, 19 | "postedSlides": { 20 | "type": "boolean" 21 | }, 22 | "rating": { 23 | "type": "number", 24 | "maximum": 5 25 | } 26 | }, 27 | "additionalProperties": false, 28 | "required": [ 29 | "email", 30 | "firstName", 31 | "lastName", 32 | "postedSlides", 33 | "rating" 34 | ] 35 | } 36 | -------------------------------------------------------------------------------- /chapter-5/ex-17-all-of.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": true, 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-18-speaker-schema-generated-modified.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "about": { 6 | "type": "string" 7 | }, 8 | "email": { 9 | "type": "string", 10 | "pattern": "^[\\w|-|.]+@[\\w]+\\.[A-Za-z]{2,4}$" 11 | }, 12 | "firstName": { 13 | "type": "string" 14 | }, 15 | "lastName": { 16 | "type": "string" 17 | }, 18 | "picture": { 19 | "type": "string" 20 | }, 21 | "tags": { 22 | "type": "array", 23 | "items": [ 24 | { 25 | "type": "string" 26 | } 27 | ] 28 | }, 29 | "company": { 30 | "type": "string" 31 | } 32 | }, 33 | "additionalProperties": false, 34 | "required": [ "about", "email", "firstName", 35 | "lastName", "picture", "tags", "company" 36 | ] 37 | } -------------------------------------------------------------------------------- /chapter-5/ex-18-speaker-schema-generated.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "id": "/", 4 | "type": "object", 5 | "properties": { 6 | "about": { 7 | "id": "about", 8 | "type": "string" 9 | }, 10 | "email": { 11 | "id": "email", 12 | "type": "string" 13 | }, 14 | "firstName": { 15 | "id": "firstName", 16 | "type": "string" 17 | }, 18 | "lastName": { 19 | "id": "lastName", 20 | "type": "string" 21 | }, 22 | "picture": { 23 | "id": "picture", 24 | "type": "string" 25 | }, 26 | "tags": { 27 | "id": "tags", 28 | "type": "array", 29 | "items": [{ 30 | "id": "0", 31 | "type": "string" 32 | }, { 33 | "id": "1", 34 | "type": "string" 35 | }, { 36 | "id": "2", 37 | "type": "string" 38 | }] 39 | }, 40 | "company": { 41 | "id": "company", 42 | "type": "string" 43 | } 44 | }, 45 | "additionalProperties": false, 46 | "required": [ 47 | "about", 48 | "email", 49 | "firstName", 50 | "lastName", 51 | "picture", 52 | "tags", 53 | "company" 54 | ] 55 | } 56 | -------------------------------------------------------------------------------- /chapter-5/ex-18-speaker-template.js: -------------------------------------------------------------------------------- 1 | // Template for http://www.json-generator.com/ 2 | 3 | [ 4 | '{{repeat(3)}}', { 5 | id: '{{integer()}}', 6 | picture: 'http://placehold.it/32x32', 7 | name: '{{firstName()}}', 8 | lastName: '{{surname()}}', 9 | company: '{{company()}}', 10 | email: '{{email()}}', 11 | about: '{{lorem(1, "paragraphs")}}' 12 | } 13 | ] -------------------------------------------------------------------------------- /chapter-5/ex-18-speaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Fred Smith is the CTO of Full Ventures, where he ...", 3 | "email": "fred.smith@fullventures.com", 4 | "firstName": "Fred", 5 | "lastName": "Smith", 6 | "picture": "http://placehold.it/fsmith-full-ventures-small.png", 7 | "tags": [ 8 | "JavaScript", 9 | "REST", 10 | "JSON" 11 | ], 12 | "company": "Full Ventures, Inc." 13 | } 14 | -------------------------------------------------------------------------------- /chapter-5/ex-18-speakers-generated-modified.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": [ 3 | { 4 | "id": 0, 5 | "picture": "http://placehold.it/32x32", 6 | "name": "Allen", 7 | "lastName": "Strickland", 8 | "company": "Coriander", 9 | "email": "allenstrickland@coriander.com", 10 | "about": "Quis enim labore exercitation elit duis irure sit incididunt dolore esse est. Culpa laboris ex labore labore nulla ad cillum fugiat reprehenderit nostrud irure. Officia et cupidatat et pariatur nulla nisi aliquip. In magna et ad eiusmod exercitation veniam. Culpa esse enim amet do aliqua reprehenderit sunt ipsum velit nulla reprehenderit. Ad minim consectetur magna adipisicing ut.\r\n" 11 | }, 12 | { 13 | "id": 1, 14 | "picture": "http://placehold.it/32x32", 15 | "name": "Merle", 16 | "lastName": "Prince", 17 | "company": "Xylar", 18 | "email": "merleprince@xylar.com", 19 | "about": "Id voluptate duis est laborum laborum esse. Ipsum fugiat ut pariatur adipisicing et cillum. Duis aute cillum adipisicing labore qui velit velit nostrud ad. Velit est mollit officia excepteur minim minim occaecat enim qui magna ad ut adipisicing deserunt. Qui officia ex aute laboris. Pariatur et anim cillum veniam. Labore eiusmod non velit do eiusmod tempor nostrud do cupidatat.\r\n" 20 | }, 21 | { 22 | "id": 2, 23 | "picture": "http://placehold.it/32x32", 24 | "name": "Salazar", 25 | "lastName": "Ewing", 26 | "company": "Zentime", 27 | "email": "salazarewing@zentime.com", 28 | "about": "Officia qui id nostrud non laboris in eiusmod ex et. Aute sunt consequat do labore dolor in et ea excepteur cillum incididunt enim sunt. Et voluptate qui occaecat eu. Nulla aute esse reprehenderit aliquip officia incididunt excepteur nisi. Culpa ad occaecat ipsum deserunt ex dolor ullamco quis.\r\n" 29 | } 30 | ] 31 | } -------------------------------------------------------------------------------- /chapter-5/ex-18-speakers-generated.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": 5, 3 | "picture": "http://placehold.it/32x32", 4 | "name": "Allen", 5 | "lastName": "Strickland", 6 | "company": "Coriander", 7 | "email": "allenstrickland@coriander.com", 8 | "about": "Quis enim labore exercitation elit duis irure sit incididunt dolore esse est. Culpa laboris ex labore labore nulla ad cillum fugiat reprehenderit nostrud irure. Officia et cupidatat et pariatur nulla nisi aliquip. In magna et ad eiusmod exercitation veniam. Culpa esse enim amet do aliqua reprehenderit sunt ipsum velit nulla reprehenderit. Ad minim consectetur magna adipisicing ut.\r\n" 9 | }, { 10 | "id": 9, 11 | "picture": "http://placehold.it/32x32", 12 | "name": "Merle", 13 | "lastName": "Prince", 14 | "company": "Xylar", 15 | "email": "merleprince@xylar.com", 16 | "about": "Id voluptate duis est laborum laborum esse. Ipsum fugiat ut pariatur adipisicing et cillum. Duis aute cillum adipisicing labore qui velit velit nostrud ad. Velit est mollit officia excepteur minim minim occaecat enim qui magna ad ut adipisicing deserunt. Qui officia ex aute laboris. Pariatur et anim cillum veniam. Labore eiusmod non velit do eiusmod tempor nostrud do cupidatat.\r\n" 17 | }, { 18 | "id": 8, 19 | "picture": "http://placehold.it/32x32", 20 | "name": "Salazar", 21 | "lastName": "Ewing", 22 | "company": "Zentime", 23 | "email": "salazarewing@zentime.com", 24 | "about": "Officia qui id nostrud non laboris in eiusmod ex et. Aute sunt consequat do labore dolor in et ea excepteur cillum incididunt enim sunt. Et voluptate qui occaecat eu. Nulla aute esse reprehenderit aliquip officia incididunt excepteur nisi. Culpa ad occaecat ipsum deserunt ex dolor ullamco quis.\r\n" 25 | }] 26 | -------------------------------------------------------------------------------- /chapter-5/ex-2-basic-types-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "age": 39, 6 | "rating": 4.1, 7 | "company": "None" 8 | } 9 | -------------------------------------------------------------------------------- /chapter-5/ex-2-basic-types-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | }, 14 | "age": { 15 | "type": "integer" 16 | }, 17 | "postedSlides": { 18 | "type": "boolean" 19 | }, 20 | "rating": { 21 | "type": "number" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /chapter-5/ex-2-basic-types.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "age": 39, 6 | "postedSlides": true, 7 | "rating": 4.1 8 | } 9 | -------------------------------------------------------------------------------- /chapter-5/ex-3-basic-types-no-addl-props-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "age": 39, 6 | "rating": 4.1, 7 | "company": "None" 8 | } 9 | -------------------------------------------------------------------------------- /chapter-5/ex-3-basic-types-no-addl-props-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | }, 14 | "postedSlides": { 15 | "type": "boolean" 16 | }, 17 | "rating": { 18 | "type": "number" 19 | } 20 | }, 21 | "additionalProperties": false 22 | } 23 | -------------------------------------------------------------------------------- /chapter-5/ex-3-basic-types-no-addl-props.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": true, 6 | "rating": 4.1 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-4-basic-types-validation-req-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "postedSlides": true, 6 | "age": 39 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-4-basic-types-validation-req-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "email": { 6 | "type": "string" 7 | }, 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | }, 14 | "postedSlides": { 15 | "type": "boolean" 16 | }, 17 | "rating": { 18 | "type": "number" 19 | } 20 | }, 21 | "additionalProperties": false, 22 | "required": ["email", "firstName", "lastName", "postedSlides", "rating"] 23 | } 24 | -------------------------------------------------------------------------------- /chapter-5/ex-4-basic-types-validation-req.json: -------------------------------------------------------------------------------- 1 | { 2 | "email": "larsonrichard@ecratic.com", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "rating": 4.1, 6 | "postedSlides": true 7 | } 8 | -------------------------------------------------------------------------------- /chapter-5/ex-5-number-min-max-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "rating": 6.2 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-5-number-min-max-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "rating": { 6 | "type": "number", 7 | "minimum": 1.0, 8 | "maximum": 5.0 9 | } 10 | }, 11 | "additionalProperties": false, 12 | "required": ["rating"] 13 | } 14 | -------------------------------------------------------------------------------- /chapter-5/ex-5-number-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "rating": 4.99 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-6-array-simple-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["fred", 1] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-6-array-simple-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "tags": { 6 | "type": "array", 7 | "items": { 8 | "type": "string" 9 | }, 10 | "additionalItems": false 11 | } 12 | }, 13 | "additionalProperties": false, 14 | "required": ["tags"] 15 | } 16 | -------------------------------------------------------------------------------- /chapter-5/ex-6-array-simple.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["fred"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-7-array-min-max-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["fred", "a", "x", "betty", "alpha"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-7-array-min-max-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "tags": { 6 | "type": "array", 7 | "minItems": 2, 8 | "maxItems": 4, 9 | "items": { 10 | "type": "string" 11 | } 12 | } 13 | }, 14 | "additionalProperties": false, 15 | "required": ["tags"] 16 | } 17 | -------------------------------------------------------------------------------- /chapter-5/ex-7-array-min-max.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["fred", "a"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-8-array-enum-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["Java", "REST", "JS"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-8-array-enum-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "tags": { 6 | "type": "array", 7 | "minItems": 2, 8 | "maxItems": 4, 9 | "items": { 10 | "enum": [ 11 | "Open Source", "Java", "JavaScript", "JSON", "REST" 12 | ] 13 | } 14 | } 15 | }, 16 | "additionalProperties": false, 17 | "required": ["tags"] 18 | } 19 | -------------------------------------------------------------------------------- /chapter-5/ex-8-array-enum.json: -------------------------------------------------------------------------------- 1 | { 2 | "tags": ["Java", "REST"] 3 | } 4 | -------------------------------------------------------------------------------- /chapter-5/ex-9-named-object-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "speaker": { 3 | "firstName": "Larson", 4 | "lastName": " Richard", 5 | "email": "larsonrichard@ecratic.com", 6 | "postedSlides": true, 7 | "tags": [ 8 | "JavaScript", "AngularJS", "Yeoman" 9 | ] 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /chapter-5/ex-9-named-object-schema.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json-schema.org/draft-04/schema#", 3 | "type": "object", 4 | "properties": { 5 | "speaker": { 6 | "type": "object", 7 | "properties": { 8 | "firstName": { 9 | "type": "string" 10 | }, 11 | "lastName": { 12 | "type": "string" 13 | }, 14 | "email": { 15 | "type": "string" 16 | }, 17 | "postedSlides": { 18 | "type": "boolean" 19 | }, 20 | "rating": { 21 | "type": "number" 22 | }, 23 | "tags": { 24 | "type": "array", 25 | "items": { 26 | "type": "string" 27 | } 28 | } 29 | }, 30 | "additionalProperties": false, 31 | "required": ["firstName", "lastName", "email", 32 | "postedSlides", "rating", "tags" 33 | ] 34 | } 35 | }, 36 | "additionalProperties": false, 37 | "required": ["speaker"] 38 | } 39 | -------------------------------------------------------------------------------- /chapter-5/ex-9-named-object.json: -------------------------------------------------------------------------------- 1 | { 2 | "speaker": { 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "email": "larsonrichard@ecratic.com", 6 | "postedSlides": true, 7 | "rating": 4.1, 8 | "tags": [ 9 | "JavaScript", "AngularJS", "Yeoman" 10 | ] 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /chapter-6/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /chapter-6/README.md: -------------------------------------------------------------------------------- 1 | Chapter 6 Code 2 | ============== 3 | Code examples for Chapter 6 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-6/cities-weather-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cities-weather-test", 3 | "version": "1.0.0", 4 | "description": "Unit Tests for cities (weather) API using JSON Search.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/tmarrs/json-at-work-examples" 12 | }, 13 | "keywords": [ 14 | "JSON", 15 | "Search", 16 | "Mocha", 17 | "Chai" 18 | ], 19 | "author": "Tom Marrs", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/tmarrs/json-at-work-examples/issues" 23 | }, 24 | "homepage": "https://github.com/tmarrs/json-at-work-examples", 25 | "dependencies": { 26 | "chai": "^3.2.0", 27 | "json-pointer": "^0.3.0", 28 | "jsonpath": "^0.2.0", 29 | "mocha": "^2.3.2", 30 | "node-jq": "^0.5.1", 31 | "underscore": "^1.8.3", 32 | "unirest": "^0.4.2" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /chapter-6/cities-weather-test/test/json-pointer-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Attribution: Cities Weather data provided by OpenWeatherMap API 4 | ([http://openweathermap.org]) under Creative Commons Share A Like 5 | License (https://creativecommons.org/licenses/by-sa/4.0). 6 | Changes were made to the data to work with json-server. 7 | This does not imply an endorsement by the licensor. 8 | 9 | This code is distributed under Creative Commons Share A Like License. 10 | */ 11 | 12 | var expect = require('chai').expect; 13 | var pointer = require('json-pointer'); 14 | var unirest = require('unirest'); 15 | 16 | describe('cities-json-pointer', function() { 17 | var req; 18 | 19 | beforeEach(function() { 20 | req = unirest.get('http://localhost:5000/cities') 21 | .header('Accept', 'application/json'); 22 | }); 23 | 24 | it('should return a 200 response', function(done) { 25 | req.end(function(res) { 26 | expect(res.statusCode).to.eql(200); 27 | expect(res.headers['content-type']).to.eql( 28 | 'application/json; charset=utf-8'); 29 | done(); 30 | }); 31 | }); 32 | 33 | it('should return the 1st city', function(done) { 34 | req.end(function(res) { 35 | var cities = res.body; 36 | var firstCity = null; 37 | 38 | //console.log('\n\n1st Object: '); 39 | firstCity = pointer.get(cities, '/0'); 40 | //console.log(firstCity); 41 | expect(firstCity.name).to.eql('Rancho Palos Verdes'); 42 | expect(firstCity.weather[0].main).to.eql('Clear'); 43 | done(); 44 | }); 45 | }); 46 | 47 | it('should return the name of the 2nd city', function(done) { 48 | req.end(function(res) { 49 | var cities = res.body; 50 | var secondCityName = null; 51 | 52 | //console.log('\nName of the 2nd City: '); 53 | secondCityName = pointer.get(cities, '/1/name'); 54 | //console.log(secondCityName); 55 | expect(secondCityName).to.eql("San Pedro"); 56 | done(); 57 | }); 58 | }); 59 | }); 60 | -------------------------------------------------------------------------------- /chapter-7/README.md: -------------------------------------------------------------------------------- 1 | Chapter 7 Code 2 | ============== 3 | Code examples for Chapter 7 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-7/cities-weather-transform-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cities-weather-transform-test", 3 | "version": "1.0.0", 4 | "description": "JSON Transform tests.", 5 | "main": "index.js", 6 | "directories": { 7 | "test": "test" 8 | }, 9 | "scripts": { 10 | "test": "mocha test" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/tmarrs/json-at-work-examples" 15 | }, 16 | "keywords": [ 17 | "JSON", 18 | "Transform", 19 | "Mocha", 20 | "Chai" 21 | ], 22 | "author": "Tom Marrs", 23 | "license": "MIT", 24 | "bugs": { 25 | "url": "https://github.com/tmarrs/json-at-work-examples/issues" 26 | }, 27 | "homepage": "https://github.com/tmarrs/json-at-work-examples", 28 | "dependencies": { 29 | "handlebars": "^4.0.5", 30 | "json-patch": "^0.5.0", 31 | "jsonfile": "^2.2.3", 32 | "mustache": "^2.2.1", 33 | "xml2js": "^0.4.15" 34 | }, 35 | "devDependencies": { 36 | "chai": "^3.4.0", 37 | "mocha": "^2.3.3" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /chapter-7/cities-weather-transform-test/test/handlebars-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Attribution: Cities Weather data provided by OpenWeatherMap API 4 | ([http://openweathermap.org]) under Creative Commons Share A Like 5 | License (https://creativecommons.org/licenses/by-sa/4.0). 6 | Changes were made to the data to work with json-server. 7 | This does not imply an endorsement by the licensor. 8 | 9 | This code is distributed under Creative Commons Share A Like License. 10 | */ 11 | 12 | var expect = require('chai').expect; 13 | var jsonfile = require('jsonfile'); 14 | var fs = require('fs'); 15 | var handlebars = require('handlebars'); 16 | 17 | describe('cities-handlebars', function() { 18 | var jsonCitiesFileName = null; 19 | var htmlTemplateFileName = null; 20 | 21 | 22 | beforeEach(function() { 23 | var baseDir = __dirname + '/../..'; 24 | 25 | jsonCitiesFileName = baseDir + '/data/cities-weather-short.json'; 26 | htmlTemplateFileName = baseDir + 27 | '/templates/transform-html.hbs'; 28 | }); 29 | 30 | 31 | it('should transform cities JSON data to HTML', function(done) { 32 | jsonfile.readFile(jsonCitiesFileName, function(readJsonFileError, 33 | jsonObj) { 34 | if (!readJsonFileError) { 35 | fs.readFile(htmlTemplateFileName, 'utf8', function( 36 | readTemplateFileError, templateFileData) { 37 | if (!readTemplateFileError) { 38 | var template = handlebars.compile(templateFileData); 39 | var html = template(jsonObj); 40 | 41 | console.log('\n\n\nHTML Output:\n' + html); 42 | done(); 43 | } else { 44 | done(readTemplateFileError); 45 | } 46 | }); 47 | } else { 48 | done(readJsonFileError); 49 | } 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /chapter-7/cities-weather-transform-test/test/json-xml-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Attribution: Cities Weather data provided by OpenWeatherMap API 4 | ([http://openweathermap.org]) under Creative Commons Share A Like 5 | License (https://creativecommons.org/licenses/by-sa/4.0). 6 | Changes were made to the data to work with json-server. 7 | This does not imply an endorsement by the licensor. 8 | 9 | This code is distributed under Creative Commons Share A Like License. 10 | */ 11 | 12 | var expect = require('chai').expect; 13 | var jsonfile = require('jsonfile'); 14 | var fs = require('fs'); 15 | var xml2js = require('xml2js'); 16 | 17 | 18 | describe('json-xml', function() { 19 | var jsonCitiesFileName = null; 20 | var xmlCitiesFileName = null; 21 | 22 | beforeEach(function() { 23 | var baseDir = __dirname + '/../..'; 24 | 25 | jsonCitiesFileName = baseDir + '/data/cities-weather-short.json'; 26 | xmlCitiesFileName = baseDir + 27 | '/data/cities-weather-short.xml'; 28 | }); 29 | 30 | it('should transform cities JSON data to XML', function(done) { 31 | jsonfile.readFile(jsonCitiesFileName, function(readJsonFileError, 32 | jsonObj) { 33 | if (!readJsonFileError) { 34 | var builder = new xml2js.Builder(); 35 | var xml = builder.buildObject(jsonObj); 36 | 37 | console.log('\n\n\nXML Output:\n' + xml); 38 | done(); 39 | } else { 40 | done(readJsonFileError); 41 | } 42 | }); 43 | }); 44 | 45 | it('should transform cities XML data to JSON', function(done) { 46 | fs.readFile(xmlCitiesFileName, 'utf8', function( 47 | readXmlFileError, xmlData) { 48 | if (!readXmlFileError) { 49 | var parser = new xml2js.Parser(); 50 | 51 | parser.parseString(xmlData, function(error, xmlObj) { 52 | if (!error) { 53 | console.log('\n\n\nJSON Output:\n' + 54 | JSON.stringify(xmlObj, null, 2)); 55 | 56 | done(); 57 | } else { 58 | done(error); 59 | } 60 | }); 61 | } else { 62 | done(readXmlFileError); 63 | } 64 | }); 65 | }); 66 | }); 67 | -------------------------------------------------------------------------------- /chapter-7/cities-weather-transform-test/test/jsont-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Attribution: Cities Weather data provided by OpenWeatherMap API 4 | ([http://openweathermap.org]) under Creative Commons Share A Like 5 | License (https://creativecommons.org/licenses/by-sa/4.0). 6 | Changes were made to the data to work with json-server. 7 | This does not imply an endorsement by the licensor. 8 | 9 | This code is distributed under Creative Commons Share A Like License. 10 | */ 11 | 12 | var expect = require('chai').expect; 13 | var jsonfile = require('jsonfile'); 14 | var jsonT = require('../lib/jsont').jsonT; 15 | 16 | describe('cities-jsont', function() { 17 | var jsonCitiesFileName = null; 18 | 19 | var transformRules = { 20 | 'self': '{ "cities": [{cities}] }', 21 | 'cities[*]': '{ "id": "{$.id}", "name": "{$.name}", ' + 22 | '"weather": { "currentTemp": {$.main.temp}, "lowTemp": {$.main.temp_min}, ' + 23 | '"hiTemp": {$.main.temp_max}, "humidity": {$.main.humidity}, ' + 24 | '"windSpeed": {$.wind.speed}, "summary": "{$.weather[0].main}", ' + 25 | '"description": "{$.weather[0].description}" } },' 26 | }; 27 | 28 | function repairJson(jsonStr) { 29 | var repairedJsonStr = jsonStr; 30 | 31 | var repairs = [ 32 | [/,\s*}/gi, ' }'], 33 | [/,\s*\]/gi, ' ]'] 34 | ]; 35 | 36 | for (var i = 0, len = repairs.length; i < len; ++i) { 37 | repairedJsonStr = repairedJsonStr.replace(repairs[i][0], repairs[i][1]); 38 | } 39 | 40 | return repairedJsonStr; 41 | } 42 | 43 | beforeEach(function() { 44 | var baseDir = __dirname + '/../../data'; 45 | 46 | jsonCitiesFileName = baseDir + '/cities-weather-short.json'; 47 | }); 48 | 49 | it('should transform cities JSON data', function(done) { 50 | jsonfile.readFile(jsonCitiesFileName, function(readFileError, 51 | jsonObj) { 52 | if (!readFileError) { 53 | var jsonStr = jsonT(jsonObj, transformRules); 54 | 55 | jsonStr = repairJson(jsonStr); 56 | console.log(JSON.stringify(JSON.parse(jsonStr), null, 2)); 57 | //console.log(jsonStr); 58 | done(); 59 | } else { 60 | done(readFileError); 61 | } 62 | }); 63 | }); 64 | }); 65 | -------------------------------------------------------------------------------- /chapter-7/cities-weather-transform-test/test/mustache-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | /* Attribution: Cities Weather data provided by OpenWeatherMap API 4 | ([http://openweathermap.org]) under Creative Commons Share A Like 5 | License (https://creativecommons.org/licenses/by-sa/4.0). 6 | Changes were made to the data to work with json-server. 7 | This does not imply an endorsement by the licensor. 8 | 9 | This code is distributed under Creative Commons Share A Like License. 10 | */ 11 | 12 | var expect = require('chai').expect; 13 | var jsonfile = require('jsonfile'); 14 | var fs = require('fs'); 15 | var mustache = require('mustache'); 16 | 17 | describe('cities-mustache', function() { 18 | var jsonCitiesFileName = null; 19 | var htmlTemplateFileName = null; 20 | 21 | 22 | beforeEach(function() { 23 | var baseDir = __dirname + '/../..'; 24 | 25 | jsonCitiesFileName = baseDir + '/data/cities-weather-short.json'; 26 | htmlTemplateFileName = baseDir + 27 | '/templates/transform-html.mustache'; 28 | }); 29 | 30 | 31 | it('should transform cities JSON data to HTML', function(done) { 32 | jsonfile.readFile(jsonCitiesFileName, function(readJsonFileError, 33 | jsonObj) { 34 | if (!readJsonFileError) { 35 | fs.readFile(htmlTemplateFileName, 'utf8', function( 36 | readTemplateFileError, templateFileData) { 37 | if (!readTemplateFileError) { 38 | var template = templateFileData.toString(); 39 | var html = mustache.render(template, jsonObj); 40 | 41 | console.log('\n\n\nHTML Output:\n' + html); 42 | done(); 43 | } else { 44 | done(readTemplateFileError); 45 | } 46 | }); 47 | } else { 48 | done(readJsonFileError); 49 | } 50 | }); 51 | }); 52 | }); 53 | -------------------------------------------------------------------------------- /chapter-7/data/cities-weather-short-parker.json: -------------------------------------------------------------------------------- 1 | { 2 | "cities": [{ 3 | "id": 5386035, 4 | "name": "Rancho Palos Verdes", 5 | "coord": { 6 | "lon": -118.387016, 7 | "lat": 33.744461 8 | }, 9 | "main": null, 10 | "dt": 1442171078, 11 | "wind": { 12 | "speed": 4.1, 13 | "deg": 300 14 | }, 15 | "clouds": { 16 | "all": 5 17 | }, 18 | "weather": [{ 19 | "id": 800, 20 | "main": "Clear", 21 | "description": "Sky is Clear", 22 | "icon": "02d" 23 | }] 24 | }, { 25 | "id": 5392528, 26 | "name": "San Pedro", 27 | "coord": { 28 | "lon": -118.29229, 29 | "lat": 33.735851 30 | }, 31 | "main": null, 32 | "dt": 1442171080, 33 | "wind": { 34 | "speed": 4.1, 35 | "deg": 300 36 | }, 37 | "clouds": { 38 | "all": 5 39 | }, 40 | "weather": [{ 41 | "id": 800, 42 | "main": "Clear", 43 | "description": "Sky is Clear", 44 | "icon": "02d" 45 | }] 46 | }, { 47 | "id": 3988392, 48 | "name": "Rosarito", 49 | "coord": { 50 | "lon": -117.033333, 51 | "lat": 32.333328 52 | }, 53 | "main": null, 54 | "dt": 1442170905, 55 | "wind": { 56 | "speed": 4.6, 57 | "deg": 240 58 | }, 59 | "clouds": { 60 | "all": 32 61 | }, 62 | "weather": [{ 63 | "id": 802, 64 | "main": "Clouds", 65 | "description": "scattered clouds", 66 | "icon": "03d" 67 | }] 68 | }] 69 | } 70 | -------------------------------------------------------------------------------- /chapter-7/data/cities-weather-short-transformed.json: -------------------------------------------------------------------------------- 1 | { 2 | "cities": [{ 3 | "id": "5386035", 4 | "name": "Rancho Palos Verdes", 5 | "weather": { 6 | "currentTemp": 84.34, 7 | "lowTemp": 78.8, 8 | "hiTemp": 93, 9 | "humidity": 58, 10 | "windSpeed": 4.1, 11 | "summary": "Clear", 12 | "description": "Sky is Clear" 13 | } 14 | }, { 15 | "id": "5392528", 16 | "name": "San Pedro", 17 | "weather": { 18 | "currentTemp": 84.02, 19 | "lowTemp": 78.8, 20 | "hiTemp": 91, 21 | "humidity": 58, 22 | "windSpeed": 4.1, 23 | "summary": "Clear", 24 | "description": "Sky is Clear" 25 | } 26 | }, { 27 | "id": "3988392", 28 | "name": "Rosarito", 29 | "weather": { 30 | "currentTemp": 82.47, 31 | "lowTemp": 78.8, 32 | "hiTemp": 86, 33 | "humidity": 61, 34 | "windSpeed": 4.6, 35 | "summary": "Clouds", 36 | "description": "scattered clouds" 37 | } 38 | }] 39 | } 40 | -------------------------------------------------------------------------------- /chapter-7/data/cities-weather-short.json: -------------------------------------------------------------------------------- 1 | { 2 | "cities": [{ 3 | "id": 5386035, 4 | "name": "Rancho Palos Verdes", 5 | "coord": { 6 | "lon": -118.387016, 7 | "lat": 33.744461 8 | }, 9 | "main": { 10 | "temp": 84.34, 11 | "pressure": 1012, 12 | "humidity": 58, 13 | "temp_min": 78.8, 14 | "temp_max": 93 15 | }, 16 | "dt": 1442171078, 17 | "wind": { 18 | "speed": 4.1, 19 | "deg": 300 20 | }, 21 | "clouds": { 22 | "all": 5 23 | }, 24 | "weather": [{ 25 | "id": 800, 26 | "main": "Clear", 27 | "description": "Sky is Clear", 28 | "icon": "02d" 29 | }] 30 | }, { 31 | "id": 5392528, 32 | "name": "San Pedro", 33 | "coord": { 34 | "lon": -118.29229, 35 | "lat": 33.735851 36 | }, 37 | "main": { 38 | "temp": 84.02, 39 | "pressure": 1012, 40 | "humidity": 58, 41 | "temp_min": 78.8, 42 | "temp_max": 91 43 | }, 44 | "dt": 1442171080, 45 | "wind": { 46 | "speed": 4.1, 47 | "deg": 300 48 | }, 49 | "clouds": { 50 | "all": 5 51 | }, 52 | "weather": [{ 53 | "id": 800, 54 | "main": "Clear", 55 | "description": "Sky is Clear", 56 | "icon": "02d" 57 | }] 58 | }, { 59 | "id": 3988392, 60 | "name": "Rosarito", 61 | "coord": { 62 | "lon": -117.033333, 63 | "lat": 32.333328 64 | }, 65 | "main": { 66 | "temp": 82.47, 67 | "pressure": 1012, 68 | "humidity": 61, 69 | "temp_min": 78.8, 70 | "temp_max": 86 71 | }, 72 | "dt": 1442170905, 73 | "wind": { 74 | "speed": 4.6, 75 | "deg": 240 76 | }, 77 | "clouds": { 78 | "all": 32 79 | }, 80 | "weather": [{ 81 | "id": 802, 82 | "main": "Clouds", 83 | "description": "scattered clouds", 84 | "icon": "03d" 85 | }] 86 | }] 87 | } 88 | -------------------------------------------------------------------------------- /chapter-7/data/cities-weather-short.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5386035 5 | Rancho Palos Verdes 6 | 7 | -118.387016 8 | 33.744461 9 | 10 |
11 |
1442171078
12 | 13 | 4.1 14 | 300 15 | 16 | 17 | 5 18 | 19 | 20 | 800 21 |
Clear
22 | Sky is Clear 23 | 02d 24 |
25 | 26 | 27 | 5392528 28 | San Pedro 29 | 30 | -118.29229 31 | 33.735851 32 | 33 |
34 |
1442171080
35 | 36 | 4.1 37 | 300 38 | 39 | 40 | 5 41 | 42 | 43 | 800 44 |
Clear
45 | Sky is Clear 46 | 02d 47 |
48 | 49 | 50 | 3988392 51 | Rosarito 52 | 53 | -117.033333 54 | 32.333328 55 | 56 |
57 |
1442170905
58 | 59 | 4.6 60 | 240 61 | 62 | 63 | 32 64 | 65 | 66 | 802 67 |
Clouds
68 | scattered clouds 69 | 03d 70 |
71 | 72 | 73 | -------------------------------------------------------------------------------- /chapter-7/data/city-weather.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 5386035, 3 | "name": "Rancho Palos Verdes", 4 | "coord": { 5 | "lon": -118.387016, 6 | "lat": 33.744461 7 | }, 8 | "main": { 9 | "temp": 84.34, 10 | "pressure": 1012, 11 | "humidity": 58, 12 | "temp_min": 78.8, 13 | "temp_max": 93 14 | }, 15 | "dt": 1442171078, 16 | "wind": { 17 | "speed": 4.1, 18 | "deg": 300 19 | }, 20 | "clouds": { 21 | "all": 5 22 | }, 23 | "weather": [{ 24 | "id": 800, 25 | "main": "Clear", 26 | "description": "Sky is Clear", 27 | "icon": "02d" 28 | }] 29 | } 30 | -------------------------------------------------------------------------------- /chapter-7/data/speaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "about": "Fred Smith is the CTO of Full Ventures, where he ...", 3 | "email": "fred.smith@fullventures.com", 4 | "firstName": "Fred", 5 | "lastName": "Smith", 6 | "tags": [ 7 | "JavaScript", 8 | "REST", 9 | "JSON" 10 | ], 11 | "company": "Full Ventures, Inc." 12 | } 13 | -------------------------------------------------------------------------------- /chapter-7/data/speaker.xml: -------------------------------------------------------------------------------- 1 | 2 | Fred Smith is the CTO of Full Ventures, where he ... 3 | fred.smith@fullventures.com 4 | Fred 5 | Smith 6 | JavaScript 7 | REST 8 | JSON 9 | Full Ventures, Inc. -------------------------------------------------------------------------------- /chapter-7/data/weather.css: -------------------------------------------------------------------------------- 1 | table.weatherTable, table.weatherTable th, table.weatherTable td { 2 | border: 1px solid black; 3 | border-collapse: collapse; 4 | border-width: 1px; 5 | border-style: solid; 6 | padding: 8px; 7 | } 8 | 9 | table.weatherTable th { 10 | background-color: #dedede; 11 | } 12 | -------------------------------------------------------------------------------- /chapter-7/data/weather.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenWeather - California Cities 7 | 8 | 9 | 10 | 11 |

OpenWeather - California Cities

12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 |
CityIDCurrent Temp
Santa Rosa520175
26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /chapter-7/templates/transform-html.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenWeather - California Cities 7 | 8 | 9 | 10 |

OpenWeather - California Cities

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{#each cities}} 26 | 27 | 28 | 29 | {{#main}} 30 | 31 | 32 | 33 | 34 | {{/main}} 35 | 36 | {{#each weather}} 37 | 38 | 39 | {{/each}} 40 | 41 | {{/each}} 42 |
IDCityCurrent TempLow TempHigh TempHumidityWind SpeedSummaryDescription
{{id}}{{name}}{{temp}}{{temp_min}}{{temp_max}}{{humidity}}{{wind.speed}}{{main}}{{description}}
43 | 44 | 45 | -------------------------------------------------------------------------------- /chapter-7/templates/transform-html.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | OpenWeather - California Cities 7 | 8 | 9 | 10 |

OpenWeather - California Cities

11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {{#cities}} 26 | 27 | 28 | 29 | {{#main}} 30 | 31 | 32 | 33 | 34 | {{/main}} 35 | 36 | {{#weather.0}} 37 | 38 | 39 | {{/weather.0}} 40 | 41 | {{/cities}} 42 |
IDCityCurrent TempLow TempHigh TempHumidityWind SpeedSummaryDescription
{{id}}{{name}}{{temp}}{{temp_min}}{{temp_max}}{{humidity}}{{wind.speed}}{{main}}{{description}}
43 | 44 | 45 | -------------------------------------------------------------------------------- /chapter-7/templates/transform-json.hbs: -------------------------------------------------------------------------------- 1 | { 2 | "cities": [ 3 | {{#each cities}} 4 | { 5 | "id": "{{id}}", 6 | "name": "{{name}}", 7 | "weather": { 8 | {{#main}} 9 | "currentTemp": {{temp}}, 10 | "lowTemp": {{temp_min}}, 11 | "hiTemp": {{temp_max}}, 12 | "humidity": {{humidity}}, 13 | {{/main}} 14 | "windSpeed": {{wind.speed}}, 15 | {{#each weather}} 16 | "summary": "{{main}}", 17 | "description": "{{description}}" 18 | {{/each}} 19 | } 20 | }{{#unless @last}},{{/unless}} 21 | {{/each}} 22 | ] 23 | } -------------------------------------------------------------------------------- /chapter-7/templates/transform-json.mustache: -------------------------------------------------------------------------------- 1 | { 2 | "cities": [ 3 | {{#cities}} 4 | { 5 | "id": "{{id}}", 6 | "name": "{{name}}", 7 | "weather": { 8 | {{#main}} 9 | "currentTemp": {{temp}}, 10 | "lowTemp": {{temp_min}}, 11 | "hiTemp": {{temp_max}}, 12 | "humidity": {{humidity}}, 13 | {{/main}} 14 | "windSpeed": {{wind.speed}}, 15 | {{#weather.0}} 16 | "summary": "{{main}}" 17 | "description": "{{description}}" 18 | {{/weather.0}} 19 | } 20 | }, 21 | {{/cities}} 22 | ] 23 | } -------------------------------------------------------------------------------- /chapter-7/templates/transform-xml.hbs: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{#each cities}} 4 | 5 | {{id}} 6 | {{name}} 7 | 8 | {{#main}} 9 | {{temp}} 10 | {{temp_min}} 11 | {{temp_max}} 12 | {{humidity}} 13 | {{/main}} 14 | {{wind.speed}} 15 | {{#each weather}} 16 | {{main}} 17 | {{description}} 18 | {{/each}} 19 | 20 | 21 | {{/each}} 22 | -------------------------------------------------------------------------------- /chapter-7/templates/transform-xml.mustache: -------------------------------------------------------------------------------- 1 | 2 | 3 | {{#cities}} 4 | 5 | {{id}} 6 | {{name}} 7 | 8 | {{#main}} 9 | {{temp}} 10 | {{temp_min}} 11 | {{temp_max}} 12 | {{humidity}} 13 | {{/main}} 14 | {{wind.speed}} 15 | {{#weather.0}} 16 | {{main}} 17 | {{description}} 18 | {{/weather.0}} 19 | 20 | 21 | {{/cities}} 22 | 23 | -------------------------------------------------------------------------------- /chapter-8/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | -------------------------------------------------------------------------------- /chapter-8/README.md: -------------------------------------------------------------------------------- 1 | Chapter 8 Code 2 | ============== 3 | Code examples for Chapter 8 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-8/data/presentations-operations.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": [ 3 | "http://www.w3.org/ns/hydra/core", { 4 | "@vocab": "http://myconference.schema.com/", 5 | "presentations": { 6 | "@type": "@id", 7 | "id": "id", 8 | "speakerId": "speakerId", 9 | "title": "title", 10 | "abstract": "abstract", 11 | "audience": "audience" 12 | } 13 | } 14 | ], 15 | "presentations": [{ 16 | "@id": "http://myconference.api.com/speakers/123456/presentations/1123", 17 | "id": "1123", 18 | "speakerId": "123456", 19 | "title": "Enterprise Node", 20 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 21 | "audience": [ 22 | "Architects", 23 | "Developers" 24 | ] 25 | }, { 26 | "@id": "http://myconference.api.com/speakers/123456/presentations/2123", 27 | "id": "2123", 28 | "speakerId": "123456", 29 | "title": "How to Design and Build Great APIs", 30 | "abstract": "Companies now leverage APIs as part of their online strategy ...", 31 | "audience": [ 32 | "Managers", 33 | "Architects", 34 | "Developers" 35 | ] 36 | }], 37 | "operation": { 38 | "@type": "AddPresentation", 39 | "method": "POST", 40 | "expects": { 41 | "@id": "http://schema.org/id", 42 | "supportedProperty": [{ 43 | "property": "title", 44 | "range": "Text" 45 | }, { 46 | "property": "abstract", 47 | "range": "Text" 48 | }] 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chapter-8/data/presentations.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "1123", 3 | "speakerId": "123456", 4 | "title": "Enterprise Node", 5 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 6 | "audience": [ 7 | "Architects", 8 | "Developers" 9 | ] 10 | }, { 11 | "id": "2123", 12 | "speakerId": "123456", 13 | "title": "How to Design and Build Great APIs", 14 | "abstract": "Companies now leverage APIs as part of their online strategy ...", 15 | "audience": [ 16 | "Managers", 17 | "Architects", 18 | "Developers" 19 | ] 20 | }] 21 | -------------------------------------------------------------------------------- /chapter-8/data/presentations.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@vocab": "http://myconference.schema.com/", 4 | "presentations": { 5 | "@type": "@id", 6 | "id": "id", 7 | "speakerId": "speakerId", 8 | "title": "title", 9 | "abstract": "abstract", 10 | "audience": "audience" 11 | } 12 | }, 13 | "presentations": [{ 14 | "@id": "http://myconference.api.com/speakers/123456/presentations/1123", 15 | "id": "1123", 16 | "speakerId": "123456", 17 | "title": "Enterprise Node", 18 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 19 | "audience": [ 20 | "Architects", 21 | "Developers" 22 | ] 23 | }, { 24 | "@id": "http://myconference.api.com/speakers/123456/presentations/2123", 25 | "id": "2123", 26 | "speakerId": "123456", 27 | "title": "How to Design and Build Great APIs", 28 | "abstract": "Companies now leverage APIs as part of their online strategy ...", 29 | "audience": [ 30 | "Managers", 31 | "Architects", 32 | "Developers" 33 | ] 34 | }] 35 | } 36 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-collection-json-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": { 3 | "version": "1.0", 4 | "href": "http://myconference.api.com/speakers", 5 | "items": [{ 6 | "href": "http://myconference.api.com/speakers/123456", 7 | "data": [{ 8 | "name": "id", 9 | "value": "123456" 10 | }, { 11 | "name": "firstName", 12 | "value": "Larson" 13 | }, { 14 | "name": "lastName", 15 | "value": "Richard" 16 | }, { 17 | "name": "email", 18 | "value": "larson.richard@myconference.com" 19 | }, { 20 | "name": "age", 21 | "value": "39" 22 | }, { 23 | "name": "registered", 24 | "value": "true" 25 | }], 26 | "links": [{ 27 | "rel": "presentations", 28 | "href": "http://myconference.api.com/speakers/123456/presentations", 29 | "prompt": "Presentations" 30 | }] 31 | }] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-hal-embed-presentations.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "http://myconference.api.com/speakers/123456" 5 | }, 6 | "presentations": { 7 | "href": "http://myconference.api.com/speakers/123456/presentations" 8 | } 9 | }, 10 | "_embedded": { 11 | "presentations": [{ 12 | "_links": { 13 | "self": { 14 | "href": "http://myconference.api.com/speakers/123456/presentations/1123" 15 | } 16 | }, 17 | "id": "1123", 18 | "title": "Enterprise Node", 19 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 20 | "audience": [ 21 | "Architects", 22 | "Developers" 23 | ] 24 | }, { 25 | "_links": { 26 | "self": { 27 | "href": "http://myconference.api.com/speakers/123456/presentations/2123" 28 | } 29 | }, 30 | "id": "2123", 31 | "title": "How to Design and Build Great APIs", 32 | "abstract": "Companies now leverage APIs as part of their online strategy ...", 33 | "audience": [ 34 | "Managers", 35 | "Architects", 36 | "Developers" 37 | ] 38 | }] 39 | }, 40 | "id": "123456", 41 | "firstName": "Larson", 42 | "lastName": "Richard", 43 | "email": "larson.richard@myconference.com", 44 | "tags": [ 45 | "JavaScript", 46 | "AngularJS", 47 | "Yeoman" 48 | ], 49 | "age": 39, 50 | "registered": true 51 | } 52 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-hal.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "http://myconference.api.com/speakers/123456" 5 | }, 6 | "presentations": { 7 | "href": "http://myconference.api.com/speakers/123456/presentations" 8 | } 9 | }, 10 | "id": "123456", 11 | "firstName": "Larson", 12 | "lastName": "Richard", 13 | "email": "larson.richard@myconference.com", 14 | "tags": [ 15 | "JavaScript", 16 | "AngularJS", 17 | "Yeoman" 18 | ], 19 | "age": 39, 20 | "registered": true 21 | } 22 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-jsonapi-embed-presentations.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "self": "http://myconference.api.com/speakers/123456" 4 | }, 5 | "data": [{ 6 | "type": "speaker", 7 | "id": "123456", 8 | "attributes": { 9 | "firstName": "Larson", 10 | "lastName": "Richard", 11 | "email": "larson.richard@myconference.com", 12 | "tags": [ 13 | "JavaScript", 14 | "AngularJS", 15 | "Yeoman" 16 | ], 17 | "age": 39, 18 | "registered": true 19 | } 20 | }], 21 | "included": [{ 22 | "type": "presentations", 23 | "id": "1123", 24 | "speakerId": "123456", 25 | "title": "Enterprise Node", 26 | "abstract": "Many developers just see Node as a way to build web APIs or applications ...", 27 | "audience": [ 28 | "Architects", 29 | "Developers" 30 | ] 31 | }, { 32 | "type": "presentations", 33 | "id": "2123", 34 | "speakerId": "123456", 35 | "title": "How to Design and Build Great APIs", 36 | "abstract": "Companies now leverage APIs as part of their online strategy ...", 37 | "audience": [ 38 | "Managers", 39 | "Architects", 40 | "Developers" 41 | ] 42 | }] 43 | } 44 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-jsonapi-link-presentations.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "self": "http://myconference.api.com/speakers/123456", 4 | "presentations": "http://myconference.api.com/speakers/123456/presentations" 5 | }, 6 | "data": [{ 7 | "type": "speaker", 8 | "id": "123456", 9 | "attributes": { 10 | "firstName": "Larson", 11 | "lastName": "Richard", 12 | "email": "larson.richard@myconference.com", 13 | "tags": [ 14 | "JavaScript", 15 | "AngularJS", 16 | "Yeoman" 17 | ], 18 | "age": 39, 19 | "registered": true 20 | } 21 | }] 22 | } 23 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-siren.json: -------------------------------------------------------------------------------- 1 | { 2 | "class": ["speaker"], 3 | "properties": { 4 | "id": "123456", 5 | "firstName": "Larson", 6 | "lastName": "Richard", 7 | "email": "larson.richard@myconference.com", 8 | "tags": [ 9 | "JavaScript", 10 | "AngularJS", 11 | "Yeoman" 12 | ], 13 | "age": 39, 14 | "registered": true 15 | }, 16 | "actions": [{ 17 | "name": "add-presentation", 18 | "title": "Add Presentation", 19 | "method": "POST", 20 | "href": "http://myconference.api.com/speakers/123456/presentations", 21 | "type": "application/x-www-form-urlencoded", 22 | "fields": [{ 23 | "name": "title", 24 | "type": "text" 25 | }, { 26 | "name": "abstract", 27 | "type": "text" 28 | }, { 29 | "name": "audience", 30 | "type": "text" 31 | }] 32 | }], 33 | "links": [{ 34 | "rel": ["self"], 35 | "href": "http://myconference.api.com/speakers/123456" 36 | }, { 37 | "rel": ["presentations"], 38 | "href": "http://myconference.api.com/speakers/123456/presentations" 39 | }] 40 | } 41 | -------------------------------------------------------------------------------- /chapter-8/data/speaker-template-collection-json.json: -------------------------------------------------------------------------------- 1 | { 2 | "collection": { 3 | "version": "1.0", 4 | "href": "http://myconference.api.com/speakers", 5 | "items": [{ 6 | "href": "http://myconference.api.com/speakers/123456", 7 | "data": [{ 8 | "name": "id", 9 | "value": "123456" 10 | }, { 11 | "name": "firstName", 12 | "value": "Larson" 13 | }, { 14 | "name": "lastName", 15 | "value": "Richard" 16 | }, { 17 | "name": "email", 18 | "value": "larson.richard@myconference.com" 19 | }, { 20 | "name": "age", 21 | "value": "39" 22 | }, { 23 | "name": "registered", 24 | "value": "true" 25 | }], 26 | "links": [{ 27 | "rel": "presentations", 28 | "href": "http://myconference.api.com/speakers/123456/presentations", 29 | "prompt": "Presentations" 30 | }] 31 | }] 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /chapter-8/data/speaker.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "123456", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "email": "larson.richard@myconference.com", 6 | "tags": [ 7 | "JavaScript", 8 | "AngularJS", 9 | "Yeoman" 10 | ], 11 | "age": 39, 12 | "registered": true 13 | } 14 | -------------------------------------------------------------------------------- /chapter-8/data/speaker.jsonld: -------------------------------------------------------------------------------- 1 | { 2 | "@context": { 3 | "@vocab": "http://schema.org/Person", 4 | "firstName": "givenName", 5 | "lastName": "familyName", 6 | "email": "email", 7 | "tags": "http://myconference.schema.com/Speaker/tags", 8 | "age": "age", 9 | "registered": "http://myconference.schema.com/Speaker/registered" 10 | }, 11 | "@id": "http://myconference.api.com/speakers/123456", 12 | "id": "123456", 13 | "firstName": "Larson", 14 | "lastName": "Richard", 15 | "email": "larson.richard@myconference.com", 16 | "tags": [ 17 | "JavaScript", 18 | "AngularJS", 19 | "Yeoman" 20 | ], 21 | "age": 39, 22 | "registered": true, 23 | "presentations": "http://myconference.api.com/speakers/123456/presentations" 24 | } 25 | -------------------------------------------------------------------------------- /chapter-8/data/speakers-hal-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "http://myconference.api.com/speakers" 5 | }, 6 | "next": { 7 | "href": "http://myconference.api.com/speakers?limit=25&offset=25" 8 | }, 9 | "find": { 10 | "href": "http://myconference.api.com/speakers{?id}", 11 | "templated": true 12 | } 13 | }, 14 | "speakers": [{ 15 | "id": "123456", 16 | "firstName": "Larson", 17 | "lastName": "Richard", 18 | "email": "larson.richard@myconference.com", 19 | "tags": [ 20 | "JavaScript", 21 | "AngularJS", 22 | "Yeoman" 23 | ], 24 | "age": 39, 25 | "registered": true 26 | }, { 27 | "id": "223456", 28 | "firstName": "Ester", 29 | "lastName": "Clements", 30 | "email": "ester.clements@myconference.com", 31 | "tags": [ 32 | "REST", 33 | "Ruby on Rails", 34 | "APIs" 35 | ], 36 | "age": 29, 37 | "registered": true 38 | }] 39 | } 40 | -------------------------------------------------------------------------------- /chapter-8/data/speakers-hal-next-rel.json: -------------------------------------------------------------------------------- 1 | { 2 | "_links": { 3 | "self": { 4 | "href": "http://myconference.api.com/speakers" 5 | }, 6 | "next": { 7 | "href": "http://myconference.api.com?limit=25&offset=25" 8 | }, 9 | "find": { 10 | "href": "http://myconference.api.com/speakers{?id}", 11 | "templated": true 12 | } 13 | }, 14 | "speakers": [{ 15 | "id": "123456", 16 | "firstName": "Larson", 17 | "lastName": "Richard", 18 | "email": "larson.richard@myconference.com", 19 | "tags": [ 20 | "JavaScript", 21 | "AngularJS", 22 | "Yeoman" 23 | ], 24 | "age": 39, 25 | "registered": true 26 | }, { 27 | "id": "223456", 28 | "firstName": "Ester", 29 | "lastName": "Clements", 30 | "email": "ester.clements@myconference.com", 31 | "tags": [ 32 | "REST", 33 | "Ruby on Rails", 34 | "APIs" 35 | ], 36 | "age": 29, 37 | "registered": true 38 | }] 39 | } 40 | -------------------------------------------------------------------------------- /chapter-8/data/speakers-hal-server-next-rel-invalid.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": { 3 | "_links": { 4 | "next": { 5 | "href": "http://myconference.api.com?limit=25&offset=25" 6 | }, 7 | "find": { 8 | "href": "http://myconference.api.com/speakers{?id}", 9 | "templated": true 10 | } 11 | }, 12 | "speakers": [{ 13 | "id": "123456", 14 | "firstName": "Larson", 15 | "lastName": "Richard", 16 | "email": "larson.richard@myconference.com", 17 | "tags": [ 18 | "JavaScript", 19 | "AngularJS", 20 | "Yeoman" 21 | ], 22 | "age": 39, 23 | "registered": true 24 | }, { 25 | "id": "223456", 26 | "firstName": "Ester", 27 | "lastName": "Clements", 28 | "email": "ester.clements@myconference.com", 29 | "tags": [ 30 | "REST", 31 | "Ruby on Rails", 32 | "APIs" 33 | ], 34 | "age": 29, 35 | "registered": true 36 | }] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /chapter-8/data/speakers-hal-server-next-rel.json: -------------------------------------------------------------------------------- 1 | { 2 | "speakers": { 3 | "_links": { 4 | "self": { 5 | "href": "http://myconference.api.com/speakers" 6 | }, 7 | "next": { 8 | "href": "http://myconference.api.com?limit=25&offset=25" 9 | }, 10 | "find": { 11 | "href": "http://myconference.api.com/speakers{?id}", 12 | "templated": true 13 | } 14 | }, 15 | "speakers": [{ 16 | "id": "123456", 17 | "firstName": "Larson", 18 | "lastName": "Richard", 19 | "email": "larson.richard@myconference.com", 20 | "tags": [ 21 | "JavaScript", 22 | "AngularJS", 23 | "Yeoman" 24 | ], 25 | "age": 39, 26 | "registered": true 27 | }, { 28 | "id": "223456", 29 | "firstName": "Ester", 30 | "lastName": "Clements", 31 | "email": "ester.clements@myconference.com", 32 | "tags": [ 33 | "REST", 34 | "Ruby on Rails", 35 | "APIs" 36 | ], 37 | "age": 29, 38 | "registered": true 39 | }] 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /chapter-8/data/speakers-jsonapi-links.json: -------------------------------------------------------------------------------- 1 | { 2 | "links": { 3 | "self": "http://myconference.api.com/speakers", 4 | "next": "http://myconference.api.com/speakers?limit=25&offset=25" 5 | }, 6 | "data": [{ 7 | "type": "speakers", 8 | "id": "123456", 9 | "attributes": { 10 | "firstName": "Larson", 11 | "lastName": "Richard", 12 | "email": "larson.richard@myconference.com", 13 | "tags": [ 14 | "JavaScript", 15 | "AngularJS", 16 | "Yeoman" 17 | ], 18 | "age": 39, 19 | "registered": true 20 | } 21 | }, { 22 | "type": "speakers", 23 | "id": "223456", 24 | "attributes": { 25 | "firstName": "Ester", 26 | "lastName": "Clements", 27 | "email": "ester.clements@myconference.com", 28 | "tags": [ 29 | "REST", 30 | "Ruby on Rails", 31 | "APIs" 32 | ], 33 | "age": 29, 34 | "registered": true 35 | } 36 | }] 37 | } 38 | -------------------------------------------------------------------------------- /chapter-8/data/speakers.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "id": "123456", 3 | "firstName": "Larson", 4 | "lastName": "Richard", 5 | "email": "larson.richard@myconference.com", 6 | "tags": [ 7 | "JavaScript", 8 | "AngularJS", 9 | "Yeoman" 10 | ], 11 | "age": 39, 12 | "registered": true 13 | }, { 14 | "id": "223456", 15 | "firstName": "Ester", 16 | "lastName": "Clements", 17 | "email": "ester.clements@myconference.com", 18 | "tags": [ 19 | "REST", 20 | "Ruby on Rails", 21 | "APIs" 22 | ], 23 | "age": 29, 24 | "registered": true 25 | }, { 26 | "id": "223456", 27 | "firstName": "Christensen", 28 | "lastName": "Fisher", 29 | "email": "christensen.fisher@myconference.com", 30 | "tags": [ 31 | "Java", 32 | "Spring", 33 | "Maven", 34 | "REST" 35 | ], 36 | "age": 45, 37 | "registered": false 38 | }] 39 | -------------------------------------------------------------------------------- /chapter-8/speakers-hal-test/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "speakers-hal-test", 3 | "version": "1.0.0", 4 | "description": "Unit Tests for speakers API with HAL.", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "mocha test" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/tmarrs/json-at-work-examples.git" 12 | }, 13 | "keywords": [ 14 | "JSON", 15 | "HAL", 16 | "Mocha", 17 | "Chai" 18 | ], 19 | "author": "Me", 20 | "license": "ISC", 21 | "bugs": { 22 | "url": "https://github.com/tmarrs/json-at-work-examples/issues" 23 | }, 24 | "homepage": "https://github.com/tmarrs/json-at-work-examples#readme", 25 | "devDependencies": {}, 26 | "dependencies": { 27 | "chai": "^3.5.0", 28 | "halfred": "^1.0.0", 29 | "mocha": "^3.2.0", 30 | "unirest": "^0.5.1" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /chapter-8/speakers-hal-test/test/hal-spec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var expect = require('chai').expect; 4 | var unirest = require('unirest'); 5 | var halfred = require('halfred'); 6 | 7 | describe('speakers-hal', function() { 8 | var req; 9 | 10 | beforeEach(function() { 11 | halfred.enableValidation(); 12 | req = unirest.get('http://localhost:5000/speakers') 13 | .header('Accept', 'application/json'); 14 | }); 15 | 16 | it('should return a 200 response', function(done) { 17 | req.end(function(res) { 18 | expect(res.statusCode).to.eql(200); 19 | expect(res.headers['content-type']).to.eql( 20 | 'application/json; charset=utf-8'); 21 | done(); 22 | }); 23 | }); 24 | 25 | it('should return a valid HAL response validated by halfred', function( 26 | done) { 27 | req.end(function(res) { 28 | var speakersHALResponse = res.body; 29 | 30 | var resource = halfred.parse(speakersHALResponse); 31 | var speakers = resource.speakers; 32 | var speaker1 = null; 33 | 34 | console.log('\nValidation Issues: '); 35 | console.log(resource.validationIssues()); 36 | expect(resource.validationIssues()).to.be.empty; 37 | console.log(resource); 38 | expect(speakers).to.not.be.null; 39 | expect(speakers).to.not.be.empty; 40 | speaker1 = speakers[0]; 41 | expect(speaker1.firstName).to.not.be.null; 42 | expect(speaker1.firstName).to.eql('Larson'); 43 | done(); 44 | }); 45 | }); 46 | }); 47 | -------------------------------------------------------------------------------- /chapter-9/README.md: -------------------------------------------------------------------------------- 1 | Chapter 9 Code 2 | ============== 3 | Code examples for Chapter 9 of [__JSON at Work__](https://github.com/tmarrs/json-at-work-examples/blob/master/README.md). 4 | -------------------------------------------------------------------------------- /chapter-9/mongo-export.txt: -------------------------------------------------------------------------------- 1 | mongoexport --db=jsaw --collection=speakers --pretty --jsonArray --out=my-speakers.json 2 | 3 | cat my-speakers.json | jq '.[] | {fullName, age}' 4 | 5 | mongoexport --db=jsaw --collection=speakers --pretty --jsonArray | jq '[.[] | del(._id)]' -------------------------------------------------------------------------------- /chapter-9/mongo-import.txt: -------------------------------------------------------------------------------- 1 | mongoimport --db=jsaw --collection=speakers --upsert --jsonArray --file=speakers.json -------------------------------------------------------------------------------- /chapter-9/mongo-speakers-create.js: -------------------------------------------------------------------------------- 1 | db.speakers.insert({ 2 | fullName: 'Carl ClojureDev', 3 | tags: ['Clojure', 'Functional Programming'], 4 | age: 45, 5 | registered: false 6 | }) 7 | 8 | db.speakers.find() 9 | db.speakers.count() 10 | -------------------------------------------------------------------------------- /chapter-9/speakers.json: -------------------------------------------------------------------------------- 1 | [{ 2 | "fullName": "Larson Richard", 3 | "tags": [ 4 | "JavaScript", 5 | "AngularJS", 6 | "Yeoman" 7 | ], 8 | "age": 39, 9 | "registered": true 10 | }, { 11 | "fullName": "Ester Clements", 12 | "tags": [ 13 | "REST", 14 | "Ruby on Rails", 15 | "APIs" 16 | ], 17 | "age": 29, 18 | "registered": true 19 | }, { 20 | "fullName": "Christensen Fisher", 21 | "tags": [ 22 | "Java", 23 | "Spring", 24 | "Maven", 25 | "REST" 26 | ], 27 | "age": 45, 28 | "registered": false 29 | }] 30 | --------------------------------------------------------------------------------