appt 0.0.1.beta.2 → 0.0.1.beta.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/MIT-LICENSE +20 -20
- data/Rakefile +34 -37
- data/app/assets/javascripts/appt/application.js +2 -2
- data/app/assets/stylesheets/appt/application.scss +1 -0
- data/app/assets/stylesheets/appt/calendar.scss +20 -0
- data/app/controllers/appt/appointment_types_controller.rb +7 -0
- data/app/controllers/appt/base_controller.rb +1 -0
- data/app/controllers/appt/calendars_controller.rb +56 -6
- data/app/controllers/appt/external_calendars_controller.rb +72 -0
- data/app/controllers/appt/home_controller.rb +2 -1
- data/app/helpers/appt/application_helper.rb +39 -20
- data/app/helpers/appt/calendars_helper.rb +5 -4
- data/app/mailers/base_mailer.rb +2 -1
- data/app/models/appt/appointment.rb +5 -4
- data/app/models/appt/appointment_type.rb +6 -0
- data/app/models/appt/block.rb +8 -4
- data/app/models/appt/calendar.rb +38 -4
- data/app/models/appt/calendar_event.rb +40 -5
- data/app/models/appt/external_calendar.rb +40 -0
- data/app/views/appt/blocks/_table.html.haml +26 -0
- data/app/views/appt/calendars/_form.html.haml +12 -0
- data/app/views/appt/calendars/_table.html.haml +17 -0
- data/app/views/appt/calendars/edit.html.haml +6 -0
- data/app/views/appt/calendars/index.html.haml +6 -0
- data/app/views/appt/calendars/new.html.haml +6 -0
- data/app/views/appt/calendars/show.html.haml +14 -0
- data/app/views/appt/external_calendars/_form.html.haml +7 -0
- data/app/views/appt/external_calendars/_table.html.haml +10 -0
- data/app/views/appt/external_calendars/edit.html.haml +6 -0
- data/app/views/appt/external_calendars/index.html.haml +7 -0
- data/app/views/appt/external_calendars/new.html.haml +6 -0
- data/app/views/appt/external_calendars/show.html.haml +26 -0
- data/app/views/appt/shared/_navbar.html.haml +2 -1
- data/config/initializers/user_class_extensions.rb +1 -0
- data/config/routes.rb +9 -7
- data/config/secrets.yml +1 -0
- data/db/migrate/20150804002016_create_appt_calendars.rb +16 -9
- data/db/migrate/20150804003348_create_appt_blocks.rb +16 -8
- data/db/migrate/20150804003846_create_appt_appointments.rb +17 -8
- data/db/migrate/20150804234748_create_appt_appointment_types.rb +13 -0
- data/db/migrate/20150805114046_create_appt_external_calendars.rb +10 -0
- data/lib/appt.rb +27 -23
- data/lib/appt/configuration.rb +29 -28
- data/lib/appt/engine.rb +6 -5
- data/lib/appt/icalendar_expander.rb +61 -0
- data/lib/appt/version.rb +4 -3
- data/lib/bootstrap_month_calendar.rb +5 -4
- data/lib/tasks/appt_tasks.rake +5 -4
- data/lib/workhours_serializer.rb +2 -5
- data/test/appt_test.rb +8 -7
- data/test/controllers/appt/appointment_types_controller_test.rb +14 -0
- data/test/controllers/appt/calendars_controller_test.rb +14 -13
- data/test/controllers/appt/external_calendars_controller_test.rb +14 -0
- data/test/dummy/README.rdoc +28 -28
- data/test/dummy/Rakefile +7 -6
- data/test/dummy/app/assets/javascripts/application.js +14 -14
- data/test/dummy/app/assets/stylesheets/application.css +16 -16
- data/test/dummy/app/controllers/application_controller.rb +6 -5
- data/test/dummy/app/helpers/application_helper.rb +3 -2
- data/test/dummy/app/views/layouts/application.html.erb +14 -14
- data/test/dummy/bin/bundle +3 -3
- data/test/dummy/bin/rails +4 -4
- data/test/dummy/bin/rake +4 -4
- data/test/dummy/bin/setup +29 -29
- data/test/dummy/config.ru +5 -4
- data/test/dummy/config/application.rb +29 -29
- data/test/dummy/config/boot.rb +6 -5
- data/test/dummy/config/database.yml +28 -28
- data/test/dummy/config/environment.rb +6 -5
- data/test/dummy/config/environments/development.rb +52 -41
- data/test/dummy/config/environments/production.rb +80 -79
- data/test/dummy/config/environments/test.rb +43 -42
- data/test/dummy/config/initializers/assets.rb +12 -11
- data/test/dummy/config/initializers/backtrace_silencers.rb +8 -7
- data/test/dummy/config/initializers/cookies_serializer.rb +4 -3
- data/test/dummy/config/initializers/filter_parameter_logging.rb +5 -4
- data/test/dummy/config/initializers/inflections.rb +17 -16
- data/test/dummy/config/initializers/mime_types.rb +5 -4
- data/test/dummy/config/initializers/session_store.rb +4 -3
- data/test/dummy/config/initializers/wrap_parameters.rb +15 -14
- data/test/dummy/config/locales/en.yml +33 -33
- data/test/dummy/config/routes.rb +6 -6
- data/test/dummy/config/secrets.yml +22 -22
- data/test/dummy/db/schema.rb +72 -35
- data/test/dummy/db/seeds.rb +18 -0
- data/test/dummy/log/bullet.log +7 -0
- data/test/dummy/log/development.log +14702 -0
- data/test/dummy/log/test.log +1135 -0
- data/test/dummy/public/404.html +67 -67
- data/test/dummy/public/422.html +67 -67
- data/test/dummy/public/500.html +66 -66
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/16dl9CdObHclmKoFXc2zQh9rW-KLDulBg9znI-eRIRA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/2__llMaILi6GIGs0Ud--MoJ_q52Ma04wDCAakl-ciVQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/3W93pks8dviNiXPZszlyU0a3-UE6-QNPnwd9of1laVI.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/3d_B_YYvHmtlJ8mBxPSIBB0RqOS74VO_QtWUQbk-ngk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/56CdkjUr49A5C7KcR5h5IUt4XeVO74F-GwfY4sHbEOA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/6FuEXiynto7KSfrY1ziT5WzWMq6hqDNBQ9hesvk27Jc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/6PmV7DYX7QXLP7g1r1YFHkKv4UbAtm5zuucs8dxjv1o.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/72B7YeiVJA3FGFbsBjK8CYoJnnMXeFdsSF7O6hq2K6g.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/78b7p_RRYPNiUO4wEIFyW53LAs9D_l0gSZDbEJLEZ78.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/7xkYVoPwhhEGUvpD0XjXgu2MkVVn8JtOjsWHqSrYZVg.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/8K4zfT11J6ORkijFMakOfQQ1Gnf_fGTeHQWrWXlW_OQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/91oKKpcrF9-IEZyOEK8YhZDdHg_FhrVa0z2jeUFRBPE.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/98-R4wiwBf7qvdPOM9Au_WrsfFKGiA7vdNxCrpLyvDE.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/9LPwJ1u8ZBJDRotUp4C7ICJ2fzAgnQaHh6iTxAElgh4.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/9yZNL-Dnn_jptlcXDI-O7p8Y4NwxlDYxgJuHKBgM1dU.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/AtCeTliTtRQr_XhEIFKbNI8STS4puMdjFmx5o5stjnQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/BylU07F4ZHT9Ar6X_KvUvW1CNMNdU_KJtYjXT0czATA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/C1Zy92VD2Xpzq7HBuHvD3xeputd7_lQSxPo2UtX1W5Q.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/CQpbYts2MA51RR2I9ERmDY0dY30z00jELh2xuJovD3Q.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/D5WYJAXL5-u3RVbFGxwq7J2gD4sYZVwQPWhJFofAipo.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Dw-DuyBJusMgXWmVGSVfGsRU-RosOLzzxTIiyATsTbc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/DzY9vPV5Lh7Ii_ihokzMXw1Dy9c6tL3YS2-Cdw5j-wg.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/EpURBuSSQ_bzmVbeiAI6QNKn23FF3uC_sHlP8Y6K1hc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/GVsdL_8CB04VraAQejZ5yHyWYoODwJ-PiDyw_VnWw90.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/GruhA1tMkU4vAvUkmmWXlXslzc8SIG2Ng8iLkGbkfa4.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/HRAwJVW86bSUGC4rLf3pfE-T00Qcf7IfP0o352fIXg0.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/HWqpPokMZX7-F8pZ_R0Pz2DIwcLaS3lzlbPmJaQn8yw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/J26Jo3IqNb_QKailyi1A62-8sg-FnDwUKNVaVr0jKjk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/JRQfyuYiAcF7KmqYR-FhZqhm9TBHg7yW5wXHi8vZztA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/KIlEFNTqcscnUTymCqB5vGCVIMvbAk7aS26txbx88VY.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/LAtwVyUTpAWlgERdMnu98Evv9p_sjFGeyTLEM7DXCgQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/LKGqJkoPsV6hUu21PAZb_xrc14OELdJKzVlQjt0Uzm0.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/MFWF_Cz7iSxrlrFqm0HESTvCJk_XcgbgHPT3YOoClJA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/MGxUMp74x2lSe7bOz9rIqbFB6pcCBojXlH8-vKPVbuU.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/MaXf6MpY4KWsBmX5Sovz40v_wIgRigE8PUz9KsV_xGc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/NEzFaE5Y7t-wiP_ly0OaKcuXFf8SzzdVf2ZhvlEnckA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/NFZcSv_g5zKRMCYnX8jIyPP5OXXL8818m75c2QJoK_c.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/NdGQ0O17pVnmrEIkOOewA2SPFXQ7so_qk9ZEZdMTaYg.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/NePX2QhdKEwt6UcpUKh1Pt4kUEiQ3ADOXTuJXkBAY7M.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/OfqWg5SoCpHOR8XVJbnf1o17iJTyVdOkcu0aWJ4necw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/PY5erDfbWJ7v0MpSQl6Lx9C1DZnjATDPKdzjs3v02GU.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/PjzOCx9qa-CNgtm8PFv0lCcRexooD6MlBF4XZ-PHXdo.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/QC4SCuhZeFrJrutxIeNamdltRIB8x8cTZPdLVXlOkX4.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Q_AVKunF7KBB-iiZ8P_9wjqUOo6o-HDzPosGrGrxpXs.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/QpYeSirw33TrEHJwSyW8u_CY183Suu4Wmpvgip4FhAY.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/RbfwLchr_l-LtAM7gk9onOTVY3qQyChUrzo6w9q6an8.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/V5nnfAivxLHBkF21PAP624OS99Df1EJxTAWTttoUXlQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/VMnt2oNeViJzWKWH0s9aqRNfvO7GqoUCfH15SBKUkxM.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/VapeKF0n0myzk8VN-BNMW4qCe93e1879mkEkocG7ymI.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/X3gNcJy85CGLFf_W10hnLOZHvBy2qxA3IHDoCTMBsy4.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Y4rbu-IcL54gklNHGlgaxcqs_iQgE2smBZ6ZPmEDK78.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Z-lByEJf_u2hqGATHTwdLzdLpDqqMFenWI01JXE_PGE.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/ZhqD-EKl_U1S1mkDWYcW423a3-49GhN9ekGaoghKGJc.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/Ztm_GA835w0FeRS-Mjfk45ssKFydDebau0-mMccLv4Q.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/aext7yQ1tC6DW30jToxnq87sklGqlhlZWflZ3Dgdczw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/bPhvH2L4lJk0dVvrsLBlvdcJhdKt8x9RhIzM6TdZK1I.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/bWdBbSJ-8yTmfQhuCNtVgHTr_5t1BjhrJF-6dvot9K8.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/bfJcyXtvEs7I6u_NHWVqANdUOsjdtyCsl3kQf8slSNg.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/cGOON7qWTmVb0CU79sYn4JdFiT71tIfJW7nkEExmOiE.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/dV3jTWklLN0vWvAwOalDF3-iO8_1ZaSXyPBftH7Zajk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/dc2uLeO68ChQlyAv515rJm6cJuH3yluSicQfKTnXBTE.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/eDvdPBf0aDgNvfIkB1-dvQYfVuHbG2Pbo3Uk8Sg0nag.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/enqaXWMrpgXgsGodQZKC1uRgauDSUxbos0J9o_4Yt3I.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/fFt3rC4wH8cMsnvQUcAT1rx2FX9b-j4fhn4Z1fkT42s.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/gvlm0vmUeN9tRyyRNaWCztvTq2pafE3NVjBLMHBBCUY.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/j51wtYtrCUs4R5RtoLM6BFDLJCGtwjPyVxvrghVwCKc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/kNc92xE_8hJB758tIoM9xUiF1HcJPTCZRORiBxs7hKI.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/kmN-qohaDNpU8X8rq03ju50g_o09-49trJ9W95bEsBk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/ktLG9BYQe4im4JsFUtf3V14tWGTv6nt9hTAyreqDHQw.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/l5JxjRujcL8oPdN99DJBnVM7PObhk9VgvPZ0pK61X8E.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/lGMutj_qyEaM3x1QdjTK93OeQ6DKvCcTj5jLLlwUUEA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/lX3IvOvo4Uc0-CbzNwocsDA5YA0pCFzyidNNAILARLQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/mQhyR98m5F6JlyhPFEQMkBs19ycMnZxAfTppltk7yZs.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/mjELJvJ-Mfd-MkPOrFfwf-qBOAkrHQSicYOdbeMDTAU.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/nqWnjOVamRDs6M_szDI7UFPQL7qwt1kjpphbw3OlaVM.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/oxSZRmCfjRs_xcXVLwHDP97lPMXLnUy1XzE2iSDJmf4.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/pvDSvxSg9ZUtIpHcDvJNLuGL1PY-c4i3QgtpBz_MDKU.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/q2LCL-IdFXv0QYy7zINFVS9AZiy-zevl9KzahK1HhPQ.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/qQIozQLMZWLon-Z_PsiNtWElU9NXdgorUh-69NwsjLk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/qtCrDrCCgh5_kcZlXTWOotnUExoY1A08cXu69IsWxjQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/rk9iZYWQrjxawGjUdnPbzE0wP8PgCK4ZrUsqRnYQo_s.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/s0DvAZOohy3BLrrBdYLBFaTI1zuPYmLSp8_0p-kpaSg.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/sXytxfuO9aNBLY8A1RLWrFxw-ltOmIjBtVBRlxJp94Y.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/sh0H7OdNcHH0toZiNOxTWcleR3PMgENZ5jEsxV2IOfM.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/stB1cMvEiJ0p6wwgTu85fkuQJlVGVsNJ9BIY6CcV-Xc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/tdTD0IXwaELTmpKcXyGiDGPX1MOOqLshrcTAejT0-rg.cache +2 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/trNThGe9QEdWorvOlYHuY-q7ZowRU0pBL9h_C7tmow8.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/tvuuof25WSYmy6ovpL7o74pHKBKk3lX0Mvgi_mjP8Pc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/uGHVhvpno292scQRlBP4c4DWSWtTy_f3s-vB6KMISXM.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/uK5XBFLkyQv9ciZDP-xaEFCFHM7TAfaO8W0PaXaUHoU.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/ue89xOUCuBT_QFBZrwyGU4542ZeJjwzBgvqa-3IDM1Y.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/vk1Ef3fC_iwVqn_zrfaSbgSI0fC3c54G80iYq2iMOuY.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/vqklEDA4aB8n_x061GyuUp5MQNfIWoox_-dh-7C_Qgw.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/vxQDA-heflbDCBbymrvO6u3R1Qro8P4s-UWDDloBfNc.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/w0iqNBPl2PXb1i9rQK9jbyX22ZSipTNJcMsqUpHYihA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/w_fIw1xAo2ncIjDFQSCpbj-24IIjj6ghtW6_lSR02bk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zDnd-vkdSFnhkbJL00kKfvnxK2PQ7MM3tszAsQWtIZI.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zJpXEKLmLBLsx2vRnaVOJJ6ourdoqR0t0AF7rvG1gVk.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zZi6jm7vzlkFBhziu-76n61qUzU4_TvtHZ4G9WlK_UA.cache +0 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/z_n6U0juStmQsvliwcmAyoAL_c79T35cAp1aAKG7OFQ.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zjeDxrvxlto6suepUXrhQ_zbjzxXIhih-z3jT4QgiQA.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zn9cgZG1VFaoAT1Fpb7IwJ0pPPAJ23OycKdf1JOcFT8.cache +1 -0
- data/test/dummy/tmp/cache/assets/development/sprockets/v3.0/zrnmM9rBa9CBCEP1mA8bx-cFFCz_zaX3jPoj0MYDfyk.cache +1 -0
- data/test/integration/navigation_test.rb +10 -10
- data/test/models/appt/appointment_test.rb +10 -9
- data/test/models/appt/appointment_type_test.rb +10 -0
- data/test/models/appt/block_test.rb +10 -9
- data/test/models/appt/calendar_event_test.rb +10 -9
- data/test/models/appt/calendar_test.rb +10 -9
- data/test/models/appt/external_calendar_test.rb +10 -0
- data/test/support/ics/all-day.ics +39 -0
- data/test/support/ics/free-busy.ics +34 -0
- data/test/support/ics/multi-day.ics +40 -0
- data/test/support/ics/recurrence.ics +40 -0
- data/test/support/ics/singular.ics +38 -0
- data/test/test_helper.rb +23 -22
- data/test/unit/icalendar_expander_test.rb +95 -0
- metadata +394 -125
- data/README.rdoc +0 -3
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/fixtures/appt/appointments.yml +0 -11
- data/test/fixtures/appt/blocks.yml +0 -11
- data/test/fixtures/appt/calendars.yml +0 -7
@@ -1,5 +1,40 @@
|
|
1
|
-
module Appt
|
2
|
-
class CalendarEvent < ActiveRecord::Base
|
3
|
-
|
4
|
-
|
5
|
-
|
1
|
+
module Appt
|
2
|
+
class CalendarEvent < ActiveRecord::Base
|
3
|
+
extend SimpleCalendar
|
4
|
+
|
5
|
+
self.abstract_class = true
|
6
|
+
|
7
|
+
has_calendar attribute: :day
|
8
|
+
|
9
|
+
serialize :start, Tod::TimeOfDay
|
10
|
+
serialize :end, Tod::TimeOfDay
|
11
|
+
|
12
|
+
belongs_to :calendar
|
13
|
+
validates :calendar, :day, :start, :end, presence: true
|
14
|
+
|
15
|
+
def start_local
|
16
|
+
calendar.local_time_of_day(day, start)
|
17
|
+
end
|
18
|
+
|
19
|
+
def end_local
|
20
|
+
calendar.local_time_of_day(day, self.end)
|
21
|
+
end
|
22
|
+
|
23
|
+
def duration
|
24
|
+
shift.duration / 60
|
25
|
+
end
|
26
|
+
|
27
|
+
def duration=(value)
|
28
|
+
fail 'Cannot assign duration if start is nil' if start.nil?
|
29
|
+
|
30
|
+
self.end = start + value.minutes
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def shift
|
36
|
+
Tod::Shift.new(start, self.end, true)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Appt
|
2
|
+
class ExternalCalendar < ActiveRecord::Base
|
3
|
+
SYNC_BUFFER = 1.day
|
4
|
+
|
5
|
+
has_many :blocks, dependent: :destroy
|
6
|
+
has_many :calendars, ->{ distinct }, through: :blocks
|
7
|
+
|
8
|
+
validates :url, presence: true
|
9
|
+
|
10
|
+
def sync_metadata
|
11
|
+
cal = fetch_calendars.first
|
12
|
+
self.name = cal.custom_properties['x_wr_calname']
|
13
|
+
.join('; ') if cal && cal.custom_properties['x_wr_calname']
|
14
|
+
end
|
15
|
+
|
16
|
+
def sync(target)
|
17
|
+
# TODO: more incremental sync? would need to detect removed events, as
|
18
|
+
# well as handle groups under same id with group_by{ |e| e[:external_id] }
|
19
|
+
|
20
|
+
transaction do
|
21
|
+
blocks.where(calendar_id: target.id).delete_all
|
22
|
+
today = target.today
|
23
|
+
min_date = today - SYNC_BUFFER
|
24
|
+
max_date = today + target.max_days.days + SYNC_BUFFER
|
25
|
+
|
26
|
+
IcalendarExpander.new(target.timezone, fetch_calendars).events(min_date, max_date) do |event|
|
27
|
+
blocks.create!(event.merge(calendar_id: target.id))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def fetch_calendars
|
35
|
+
response = Faraday.get(url)
|
36
|
+
Icalendar.parse(response.body)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
- calendar ||= nil
|
2
|
+
- external_calendar ||= nil
|
3
|
+
|
4
|
+
%table.table
|
5
|
+
%thead
|
6
|
+
%tr
|
7
|
+
%th= Appt::Block.human_attribute_name(:name)
|
8
|
+
%th= Appt::Block.human_attribute_name(:day)
|
9
|
+
%th= Appt::Block.human_attribute_name(:start)
|
10
|
+
%th= Appt::Block.human_attribute_name(:end)
|
11
|
+
- if calendar.nil?
|
12
|
+
%th= Appt::Block.human_attribute_name(:calendar)
|
13
|
+
- if external_calendar.nil?
|
14
|
+
%th= Appt::Block.human_attribute_name(:external_calendar)
|
15
|
+
%tbody
|
16
|
+
- blocks.each do |block|
|
17
|
+
%tr
|
18
|
+
%td= link_to block.name, calendar_block_path(block.calendar, block)
|
19
|
+
%td= block.day
|
20
|
+
%td= block.start
|
21
|
+
%td= block.end
|
22
|
+
- if calendar.nil?
|
23
|
+
%td= link_to block.calendar.name, calendar_path(block.calendar)
|
24
|
+
- if external_calendar.nil?
|
25
|
+
%td
|
26
|
+
= link_to block.external_calendar.name, external_calendar_path(block.external_calendar) if block.external_calendar
|
@@ -0,0 +1,12 @@
|
|
1
|
+
- calendar ||= @calendar
|
2
|
+
|
3
|
+
= bootstrap_form_for calendar, layout: :horizontal do |f|
|
4
|
+
= f.text_field :name
|
5
|
+
= f.time_zone_select :timezone_name, ActiveSupport::TimeZone.us_zones, default: 'Eastern Time (US & Canada)'
|
6
|
+
= f.text_area :availability_text
|
7
|
+
= f.number_field :resolution_minutes
|
8
|
+
= f.number_field :min_hours
|
9
|
+
= f.number_field :max_days
|
10
|
+
= f.number_field :lock_hours
|
11
|
+
= f.form_group do
|
12
|
+
= f.primary
|
@@ -0,0 +1,17 @@
|
|
1
|
+
- if calendars.any?
|
2
|
+
%table.table
|
3
|
+
%thead
|
4
|
+
%th= Appt::Calendar.human_attribute_name(:name)
|
5
|
+
%th= Appt::Calendar.human_attribute_name(:timezone_name)
|
6
|
+
%th
|
7
|
+
%tbody
|
8
|
+
- calendars.each do |calendar|
|
9
|
+
%tr
|
10
|
+
%td= link_to calendar.name || 'Calendar', calendar_path(calendar)
|
11
|
+
%td= calendar.timezone_name
|
12
|
+
%td
|
13
|
+
= link_to 'Edit', edit_calendar_path(calendar), class: 'btn btn-sm btn-default'
|
14
|
+
- else
|
15
|
+
%p
|
16
|
+
You do not have any calendars yet.
|
17
|
+
= link_to 'Create one.', new_calendar_path
|
@@ -0,0 +1,14 @@
|
|
1
|
+
- title @calendar.name || 'Calendar'
|
2
|
+
- breadcrumbs do
|
3
|
+
= breadcrumb 'Calendars', calendars_path
|
4
|
+
|
5
|
+
.container
|
6
|
+
- calendar = BootstrapMonthCalendar.new(self, timezone: @calendar.timezone, events: @calendar.appointments)
|
7
|
+
|
8
|
+
- calendar_render = Proc.new do |date, appointments|
|
9
|
+
%h3= date.day
|
10
|
+
%ul.list-unstyled
|
11
|
+
- appointments.each do |appointment|
|
12
|
+
%li= link_to appointment.name, location_appointment_path(appointment.location, appointment)
|
13
|
+
|
14
|
+
= calendar.render(calendar_render)
|
@@ -0,0 +1,10 @@
|
|
1
|
+
%table.table
|
2
|
+
%thead
|
3
|
+
%tr
|
4
|
+
%th= Appt::ExternalCalendar.human_attribute_name(:name)
|
5
|
+
%th= Appt::ExternalCalendar.human_attribute_name(:url)
|
6
|
+
%tbody
|
7
|
+
- external_calendars.each do |external_calendar|
|
8
|
+
%tr
|
9
|
+
%td= link_to external_calendar.name.blank? ? '<no name>' : external_calendar.name, external_calendar_path(external_calendar)
|
10
|
+
%td= external_calendar.url.truncate(50, omission: "...#{external_calendar.url.last(20)}")
|
@@ -0,0 +1,26 @@
|
|
1
|
+
- title @external_calendar.name
|
2
|
+
- breadcrumbs do
|
3
|
+
= breadcrumb 'External Calendars', external_calendars_path
|
4
|
+
|
5
|
+
.container
|
6
|
+
= bootstrap_form_for :external_calendar_sync, method: :put, layout: :horizontal do |f|
|
7
|
+
= f.form_group do
|
8
|
+
= link_to 'Edit External Calendar', edit_external_calendar_path(@external_calendar), class: 'btn btn-default'
|
9
|
+
= f.button 'Sync Metadata', name: 'method', value: 'sync_metadata', class: 'btn btn-default', type: 'submit'
|
10
|
+
%hr
|
11
|
+
= f.collection_select :calendar_id, Appt::Calendar.order(name: :asc), :id, :name, prompt: 'Select a target calendar...'
|
12
|
+
= f.form_group do
|
13
|
+
= f.button 'Sync Blocks', name: 'method', value: 'sync', class: 'btn btn-default', type: 'submit'
|
14
|
+
|
15
|
+
%hr
|
16
|
+
|
17
|
+
%h3 Calendars
|
18
|
+
= paginate @calendars, param_name: :calendars_page
|
19
|
+
= render partial: 'appt/calendars/table', locals: { calendars: @calendars, external_calendar: @external_calendar }
|
20
|
+
= paginate @calendars, param_name: :calendars_page
|
21
|
+
|
22
|
+
%h3 Blocks
|
23
|
+
= paginate @blocks, param_name: :blocks_page
|
24
|
+
= render partial: 'appt/blocks/table', locals: { blocks: @blocks, external_calendar: @external_calendar }
|
25
|
+
= paginate @blocks, param_name: :blocks_page
|
26
|
+
|
@@ -2,4 +2,5 @@
|
|
2
2
|
= navbar_header brand: 'Appt', brand_link: root_path
|
3
3
|
= navbar_collapse do
|
4
4
|
= navbar_group do
|
5
|
-
= navbar_item Appt::Calendar.model_name.human(count: 2), calendars_path
|
5
|
+
= navbar_item Appt::Calendar.model_name.human(count: 2), calendars_path
|
6
|
+
= navbar_item 'External', external_calendars_path
|
data/config/routes.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
-
Appt::Engine.routes.draw do
|
2
|
-
resources :calendars do
|
3
|
-
resources :appointments
|
4
|
-
resources :blocks
|
5
|
-
end
|
6
|
-
|
7
|
-
|
1
|
+
Appt::Engine.routes.draw do
|
2
|
+
resources :calendars do
|
3
|
+
resources :appointments
|
4
|
+
resources :blocks
|
5
|
+
end
|
6
|
+
resources :external_calendars, path: 'external-calendars'
|
7
|
+
root to: 'home#index'
|
8
|
+
end
|
9
|
+
|
data/config/secrets.yml
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ical: https://www.google.com/calendar/ical/paul%40paultyng.net/private-24d987e941e289e867b98f70e5015a7d/basic.ics
|
@@ -1,9 +1,16 @@
|
|
1
|
-
class CreateApptCalendars < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :appt_calendars do |t|
|
4
|
-
t.string :name
|
5
|
-
|
6
|
-
t.
|
7
|
-
|
8
|
-
|
9
|
-
|
1
|
+
class CreateApptCalendars < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :appt_calendars do |t|
|
4
|
+
t.string :name
|
5
|
+
t.text :availability
|
6
|
+
t.string :timezone_name
|
7
|
+
t.integer :resolution_minutes
|
8
|
+
t.integer :min_hours
|
9
|
+
t.integer :max_days
|
10
|
+
t.integer :lock_hours
|
11
|
+
|
12
|
+
t.timestamps null: false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -1,8 +1,16 @@
|
|
1
|
-
class CreateApptBlocks < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :appt_blocks do |t|
|
4
|
-
|
5
|
-
t.
|
6
|
-
|
7
|
-
|
8
|
-
end
|
1
|
+
class CreateApptBlocks < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :appt_blocks do |t|
|
4
|
+
t.string :name
|
5
|
+
t.integer :calendar_id
|
6
|
+
t.date :day, null: false
|
7
|
+
t.string :start, null: false
|
8
|
+
t.string :end, null: false
|
9
|
+
t.integer :external_calendar_id
|
10
|
+
t.string :external_id
|
11
|
+
|
12
|
+
t.timestamps null: false
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
@@ -1,8 +1,17 @@
|
|
1
|
-
class CreateApptAppointments < ActiveRecord::Migration
|
2
|
-
def change
|
3
|
-
create_table :appt_appointments do |t|
|
4
|
-
|
5
|
-
t.
|
6
|
-
|
7
|
-
|
8
|
-
|
1
|
+
class CreateApptAppointments < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :appt_appointments do |t|
|
4
|
+
t.integer :calendar_id
|
5
|
+
t.date :day, null: false
|
6
|
+
t.string :start, null: false
|
7
|
+
t.string :end, null: false
|
8
|
+
t.string :email
|
9
|
+
t.string :firstname
|
10
|
+
t.string :lastname
|
11
|
+
t.string :phone
|
12
|
+
|
13
|
+
t.timestamps null: false
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class CreateApptAppointmentTypes < ActiveRecord::Migration
|
2
|
+
def change
|
3
|
+
create_table :appt_appointment_types do |t|
|
4
|
+
t.string :name, null: false
|
5
|
+
t.integer :duration_minutes
|
6
|
+
t.integer :before_minutes
|
7
|
+
t.integer :after_minutes
|
8
|
+
|
9
|
+
t.timestamps null: false
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
data/lib/appt.rb
CHANGED
@@ -1,23 +1,27 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
require
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
|
15
|
-
require
|
16
|
-
|
17
|
-
|
18
|
-
require
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
1
|
+
require 'activemodel/associations'
|
2
|
+
require 'bootstrap_form'
|
3
|
+
require 'bootstrap-sass'
|
4
|
+
require 'faraday'
|
5
|
+
require 'haml-rails'
|
6
|
+
require 'icalendar'
|
7
|
+
require 'icalendar/recurrence'
|
8
|
+
require 'jquery-rails'
|
9
|
+
require 'kaminari'
|
10
|
+
require 'phony'
|
11
|
+
require 'phony_rails'
|
12
|
+
require 'rails_bootstrap_navbar'
|
13
|
+
require 'simple_calendar'
|
14
|
+
require 'tod'
|
15
|
+
require 'workhours'
|
16
|
+
|
17
|
+
require 'bootstrap_month_calendar'
|
18
|
+
require 'workhours_serializer'
|
19
|
+
|
20
|
+
require 'appt/engine'
|
21
|
+
require 'appt/configuration'
|
22
|
+
require 'appt/icalendar_expander'
|
23
|
+
|
24
|
+
module Appt
|
25
|
+
extend Configuration
|
26
|
+
end
|
27
|
+
|