HTTP status codes in ABAP

I’ve been working a lot with OData and REST lately, outbound as well as inbound. Some of my code needs to handle HTTP status codes in different ways. Since I didn’t want to hard-code the status codes in my code, I started looking around for standard classes and interfaces which could be of use to me. This blog post is a brief summary of what I found.

Interface IF_HTTP_STATUS

The interface IF_HTTP_STATUS contains a list of status texts in the form of constants of data type STRING. Some examples are:

CONSTANTS reason_200 TYPE string VALUE 'OK' . "#EC NOTEXT
CONSTANTS reason_201 TYPE string VALUE 'Created' . "#EC NOTEXT

This interface is quite handy if you want to present the user with not only a status code but also the corresponding text.

Class CL_REST_STATUS_CODE

The classes CL_REST_STATUS_CODE and /IWCOR/CL_REST_STATUS_CODE also contain constants. The two classes are identical in regard to the constants but have some minor differences in one of the methods.

The constants in these two classes contain the numerical HTTP status codes of data type I. Some examples are:

constants GC_SUCCESS_OK type I value 200 . "#EC NOTEXT
constants GC_SUCCESS_CREATED type I value 201 . "#EC NOTEXT

The classes have some useful static methods as well:

  • Method GET_REASON_PHRASE: Takes in a status code and returns the corresponding status text. The method actually uses interface IF_HTTP_STATUS to accomplish this.
  • Method IS_CLIENT_ERROR: Takes in a status code and returns abap_true if the status code is in the range 400 – 499. Otherwise, the method returns abap_false.
  • Method IS_REDIRECTION: Takes in a status code and returns abap_true if the status code is in the range 300 – 399. Otherwise, the method returns abap_false.
  • Method IS_SUCCESS: Takes in a status code and returns abap_true if the status code is in the range 200 – 299. Otherwise, the method returns abap_false.
  • Method IS_SERVER_ERROR: Takes in a status code and returns abap_true if the status code is in the range 500 – 599. Otherwise, the method returns abap_false.
  • Method IS_ERROR: If one of the methods IS_CLIENT_ERROR and IS_SERVER_ERROR return abap_true, this method also returns abap_true. Otherwise, the method returns abap_false.

Limitations

The interface and the two classes mentioned in this blog post don’t contain constants for every HTTP status code mentioned in the Wikipedia article. However the most common ones are available, and for the use cases I’ve experienced so far the interface and the classes have been sufficient.

Happy coding!

The progress indicator in ABAP reports

I’m sure most of you have been asked to develop a report displaying a progress indicator during the execution of the report. I just developed a migration report which needs several minutes to execute, so I decided to put in a progress indicator.

When running the ATC check with abapOpenChecks for the report I’ve developed, I got an error telling me that function module SAPGUI_PROGRESS_INDICATOR should not be used. Even though this is documented in the documentation of check 53 for abapOpenChecks, an SAP Knowledge Base Search for information on what should be used instead of function module SAPGUI_PROGRESS_INDICATOR didn’t give me any results. The documentation at abapOpenChecks states that class CL_PROGRESS_INDICATOR should be used instead of the function module. I decided to write this short blog post to share this information with the community as well as to provide a tiny code example.

Instead of using the function module like this:

CALL FUNCTION 'SAPGUI_PROGRESS_INDICATOR'
  EXPORTING
    percentage = progress_percent
    text       = progress_text.

You can use the class like this:

cl_progress_indicator=>progress_indicate(
    i_text = |Processing: { current_record }/{ total_records }|
    i_output_immediately = abap_true ).

The result isn’t exactly the same since the function module only works with percentages. This could easily be achieved with the class as well, but in this case, I was more interested in seeing how many of the total records had been processed.

Happy coding!

My two first conference talks

I held my two first conference talks earlier this week at the Swedish SAP user group conference SAPSA IMPULS 2019, and in this post I will share some of my experiences. SAPSA IMPULS is a yearly conference targeting people working with SAP solutions in the Nordics, and the event had more than 800 attendees this year. This was a two days event, and I was scheduled to hold two different presentations, one on each day of the conference.

Day 1 – a broader appeal

On the first day I held a presentation titled “How to stay up-to-date within the SAP space with openSAP“. openSAP is an excellent MOOC platform to stay current with the SAP space and acquire new skills. I explained the structure and advantages of using MOOCs in general and openSAP in particular, to expand my knowledge within SAP. I also shared my personal experiences attending about 30 openSAP courses.

I was quite nervous before the talk, since I haven’t really done any public speaking. After having rigged up my computer, there were still about ten minutes to wait for the planned starting time of my talk. I found these ten minutes the most nervous part of the talk, since I was just standing around waiting. Thankfully a former colleague of mine came into the room about five minutes before my talk was to start, and we engaged in some small-talk which helped me relax. Once I got started presenting, my nerves calmed down. There were seven parallel tracks as I was presenting, and my presentation attracted about 70 people. This meant that the room was full, but not crowded. After the presentation, several attendees came forward to discuss some questions they had and they also gave me some positive feedback. I think that the talk was generally well-received.

Before holding my presentation, I had the opportunity to attend the key-note. This enabled me to tie the topic of the key-note back into my own presentation. The key-note was mainly about customer experiences and experience management. Since there is currently a course on the topic on openSAP, The Power of Experience Management, I referenced this in my presentation. Even though that this was just a small detail, I still think that it was a nice bridge between the key-note and my presentation.

I was also able to attend a few other sessions before I held my presentation. This made me realize that I had completely overlooked the fact that I should have included a slide presenting the organization I work for in my presentation. I was able to correct this mistake in the break before my presentation.

Day 2 – the technical niche talk

On the second day, I held a talk titled “Improving your ABAP code quality with open-source tools“. The talk was about how we have improved our code quality through the use of open-source tools at the organization where I work. The tools I explained and demonstrated are:

Since the session was only 20 minutes long, and I had a lot of material to cover, the talk felt a bit rushed. I feel very comfortable with the topic I presented, which probably made me talk a bit more than during my rehearsals of the talk. I felt much more relaxed than on day 1, most likely since the first talk was well received and I had already received positive feedback.

In retrospect, the talk might have been a little too technical for the audience of this conference. However, I still had about 20-25 people showing up. I was approached after this talk with some interesting comments and questions, so even though it was more of a niche talk, I’m happy that I gave it. I also learned a lot as I was preparing for the talk.

Key takeaways

I’m happy to have participated in the conference and to have shared my experiences and knowledge through the two talks. It was a learning experience both when it comes to the subjects I presented as well as regarding the whole process of writing presentation proposals, preparing and finally delivering a talk.

Some of my takeaways are:

  • Start preparing the talks early. I fine-tuned my talks over a period of about two months, and still ended up doing last-minute changes.
  • Ask someone to review your presentation. I asked my manager as well as my colleagues to review the presentations, and this gave me some valuable feedback.
  • Rehears, rehears, rehears. I practiced my talks alone as well as in front of my wife and kids, and it was important for discovering transitions in the presentations which weren’t smooth enough as well as practicing my presentation skills.
  • Try to engage in small-talk and use the opportunity to network ahead of a presentation. This helped me shift into a more talkative mode and made me more relaxed.
  • People want to hear your story. Both of my presentations contained personal stories about why the topics were important to me, and how they helped me to be successful in real-world situations. This made the presentations as well as me as a presenter easier to relate to.
  • Attend the key-note, so that you can relate to it in your own presentation if relevant.
  • Attend other sessions to see how the other presenters are presenting. This helped me fine-tune my presentation.

If you are considering submitting an abstract to a conference, just do it! It’s a fun learning experience!

Join Hacktoberfest and contribute to open-source software

I first heard about Hacktoberfest on a podcast a couple of months ago, and it sparked my interest since I’ve become more involved in open-source software projects lately. Hacktoberfest is a month-long event with the purpose of celebrating open-source software. The event is run by DigitalOcean and dev.to. Having lived in Munich for a couple of years, I, of course, find the name of the event amusing as well.

To participate, you need to sign up at hacktoberfest.digitalocean.com anytime between October 1 and October 31. Within the same time period, you need to submit at least four pull requests to public GitHub repositories. When you have done this, you will receive a Hacktoberfest T-shirt if you are one of the first 50,000 participants to successfully complete the Hacktoberfest challenge.

I think that this is an excellent event to promote open-source software, and I registered a few days ago. Once I got started, I was so inspired that I ended up creating more than double the amount of pull requests needed to receive the T-shirt.

My main focus was on abapOpenChecks, which is a GitHub repository providing open-source checks for SAP Code Inspector / ABAP Test Cockpit. I use this tool on a daily basis, so it feels great to be able to give back to the community of the project. I also created some minor pull requests for Kodboken.se, which is the educational material used by Kodcentrum where I’m volunteering as a coding instructor.

If you haven’t previously contributed to open-source software, this is the perfect time to get started! If you are already an open-source veteran, why not contribute some more and potentially receive a cool T-shirt?

I wish you a great October and happy coding!

Determine the current class and method names in ABAP

In some situations, it can be useful to be able to determine the names of the current class and method dynamically instead of using hard-coded strings. I find this particularly useful for logging purposes. I have a helper class which writes to the system log when certain exceptions are triggered. As input to this helper class (or rather the method writing to the system log) I want to provide the names of the class and method where the exception was raised.

Determining the name of the current class

SAP provides the class CL_ABAP_CLASSDESCR for determining some class attributes dynamically. To determine the name of the current class, the following code snippet can be used:

DATA(lv_class_name) = cl_abap_classdescr=>get_class_name( me ).

LV_CLASS_NAME will contain the class name in the following format: \CLASS=ZCL_MY_CLASS

Determining the name of the current method

The only approach I’ve found for determining the name of the current method is by using a function module to read the call stack. If you are aware of a better way of doing this, please leave a comment! The call stack approach looks like this:

DATA lt_callstack TYPE abap_callstack.

CALL FUNCTION 'SYSTEM_CALLSTACK'
  EXPORTING
    max_level = 1
  IMPORTING
    callstack = lt_callstack.

DATA(lv_method_name) = lt_callstack[ 1 ]-blockname.

LV_METHOD_NAME will contain the method name in the following format: MY_METHOD

Happy coding!

OData URL encoding of single quote ‘ (%27) in a search string

When developing an OData service for searching for contact persons by name, I came across a scenario where the name contains the special character ‘ (single quote character). As an example, the user would like to search for contact persons with the last name O’Reilly.

The URL encoding of the single quote character is %27. First I just tried the following:
https://servername/sap/opu/odata/SAP/Z_CONTACT_PERSON_SRV/ContactSet?$format=json&$filter=substringof(%27O%27Reilly%27,%20SearchText)

This lead to an error with HTTP status code 400 and the message “Invalid token detected at position &” with exception /IWCOR/CX_DS_EXPR_SYNTAX_ERROR.

As you might already have guessed, the issue is that %27 is used to enclose the search string. When adding a single %27 within the search string, the parser of the $filter expression gets confused.

The solution is to escape the single quote by adding a single quote before it:
https://servername/sap/opu/odata/SAP/Z_CONTACT_PERSON_SRV/ContactSet?$format=json&$filter=substringof(%27O%27%27Reilly%27,%20SearchText)

What are pragmas and pseudo comments in ABAP?

When using the ABAP Test Cockpit (ATC) for statically and dynamically checking the quality of your ABAP code and related repository objects, some of the warnings and errors identified by the ATC aren’t relevant in your specific scenario. Leaving errors and warnings unaddressed might cause confusion at some future point-in-time when you or another developer needs to change the code. In order to reduce the confusion, ABAP pragmas and pseudo comments can be used to blend out irrelevant warnings and errors. Let us take a detailed look at what pseudo comments and pragmas are and how to use them.

Pseudo Comments

Pseudo comments are program directives that influence check and test procedures. Pseudo comments have mostly become obsolete and have been replaced by pragmas or real additions.

ABAP Keyword Documentation

Even though mostly obsolete, the pseudo comments are still relevant for some scenarios in the code inspector.

A pseudo comment looks like this:

METHOD get_sources.
     SELECT source, textlong
       INTO TABLE @rt_source
       FROM tb006
       WHERE spras = @sy-langu. "#EC CI_SUBRC
   ENDMETHOD.

The pseudo comment "#EC CI_SUBRC is used to hide a message telling us that sy-subrc should be checked after a SELECT statement. In the example above the desired behavior is that an empty table should be returned if the SELECT statement is not successful, so there is no need to check the value of sy-subrc.

Sometimes a line of code generates several warnings or errors which you may want to hide. Using pseudo comments, it is only possible to specify one pseudo comment for each program line. When multiple pseudo comments are required, the statement must be divided up to span multiple lines.

Note that the pseudo comment is placed after the ending dot of a statement.

Pragmas

Pragmas are program directives that can be used to hide warnings from various check tools.

ABAP Keyword Documentation

Pragmas can be used to hide warnings from the ABAP compiler syntax check as well as from the extended program check.

The use of a pragma looks like this:

MESSAGE e001(ad) INTO DATA(lv_message) ##NEEDED.

The pragma ##NEEDED tells the check tools that even though the variable lv_message isn’t used for further processing, the variable is still needed. In this specific scenario, it is needed since we want to be able to perform a where-used search for the message AD 001 from transaction SE91.

Not that a pragma is placed before the dot or comma ending the statement to which the pragma is applied. If multiple pragmas are needed for the same statement, they can be placed one after the other separated by space. It is also possible to place pragmas in front of a colon (:) of a chained statement. This applies the pragma to the whole chained statement:

DATA ##NEEDED:
   gt_messages TYPE bapiret2_t,
   gt_sel_data TYPE ty_sel_data_tt.

Personally I think that this is hard to read, and I’d much rather use the following format:

DATA: gt_messages TYPE bapiret2_t ##NEEDED,
      gt_sel_data TYPE ty_sel_data_tt ##NEEDED.

Some pragmas can be used with parameters. The example mentioned in the ABAP Keyword Documentation is ##SHADOW, which can be used with an optional parameter. It looks like this: ##SHADOW[LOG]

I’ve never come across this in practice, so I don’t have much experience to share. Feel free to give some input on this by writing a comment on the blog post.

The mapping between obsolete pseudo comments and pragmas

As already mentioned, most pseudo comments are obsolete and have been replaced by pragmas. SAP provides a program which can be run to find the mapping between obsolete pseudo comments and pragmas. The name of the program is ABAP_SLIN_PRAGMAS. The main table used by the program is SLIN_DESC, if you prefer looking up the pseudo comments and programs directly in a table viewer like SE16.

As an example, the pseudo comment "#EC AUTFLD_MIS has been superseded by the pragma ##AUTH_FLD_MISSING.

The tables TRPRAGMA and TRPRAGMAT contain all pragmas.

I hope that this brief overview of pragmas and pseudo comments was useful. Please leave a comment if you think that I missed mentioning something crucial. Happy coding!

What is the difference between ` and ‘ in ABAP?

When defining a variable containing a text of some sort in ABAP, which of the following options do you typically use?

" Option 1
DATA(my_text) = `my text`.

 " Option 2
DATA(my_text) = 'my text'. 

Are you aware of the differences between these two lines of code? Let us look into the details.

Character literals

What we are looking at are two different types of character literals. The ABAP Keyword Documentation provides us with the following explanation:

“Character literals can be either text field literals or text string literals. A text field literal is a character string enclosed in single quotation marks ('); a text string literal is a character string enclosed in single backquotes (`).”

Revisiting our example, we can define the variables like this:

" Option 1
DATA(my_text_string_literal) = `my text`.

" Option 2
DATA(my_text_field_literal) = 'my text'.

my_text_string_literal has the actual type STRING and the technical type CString of length 7. my_text_field_literal has a generated actual type (e.g. %_T00006S00000000O0000000298) and the technical type C of length 7.

The text field literal can have a length between 1 and 255 characters, whereas a text string literal can have a length between 0 and 255 characters.

That the text field literal has a minimum length of 1 means that '' (no space) has the same meaning as ' ' (a space character).

When to use which character literal?

The SAP Styleguide for clean ABAP contains the following recommendation:

“Refrain from using ', as it adds a superfluous type conversion and confuses the reader whether he’s dealing with a CHAR or STRING”

The following is an example of a superfluous type conversion between CHAR (from the text field literal) and a STRING:

DATA example_string TYPE string.
example_string = 'QWERTY'.

Prefer the following assignment:

DATA example_string TYPE string.
example_string = `QWERTY`.

If however the data element of the variable has been defined as a CHAR, the text field literal should be used to avoid a type conversion:

DATA e_mail_address TYPE ad_smtpadr. " CHAR 241
e_mail_address = 'mail@test.com'.

I hope that this short post helped clarify the difference between ' and `. Happy coding!

Grace Murray Hopper – A computer pioneer

Grace Hopper was a computer pioneer and one of the first programmers. I just finished reading the book “Grace Murray Hopper: Working to create the future” by Carl J Schneider, and I’d like to share some of my takeaways with you.

Early life

Grace was born in New York City in 1906, well ahead of the computerization of the world. From a young age, she was an avid learner with an affinity for mathematics. In 1928 she graduated from Vassar College with a B.A. in mathematics and physics. Six years later she earned a Ph.D. in mathematics and mathematical physics at Yale. This was a rare accomplishment for a woman back in those days.

Even though she was very strong in theoretical matters, she was always looking for practical applications of her knowledge. As a teacher, she invested a lot of effort into showing her students how the theories she taught could be used to solve everyday problems.

One of my favorite quotes of the book is:

“I will never stop learning, because I have an insatiable curiosity. People who don’t keep on learning, die.”

Grace Hopper

The Mark computers

During World War II, Grace joined the U.S. Navy. The first order she received in 1944 was to join Commander Howard Aiken, Navy Bureau of Ordnance Computation Project, Harvard University, whose team was working on the large-scale digital computer Mark I.

“I always loved a good gadget. When I met Mark I, it was the biggest, fanciest gadget I’d ever seen. I had to find out how it worked.”

Grace Hopper

Grace was one of the programmers who worked on the computer. The programs created by the Harvard team were used to calculate tables to help naval gunners in aiming and for other military applications.

When Grace was working on troubleshooting the Mark II computer, she carried out the first “debugging” session in computer history. She removed a moth from the computer with tweezers after having found the bug with her purse mirror. As a modern-day programmer, I’m happy that I never have to debug at such a low level.

Commercial computing

In 1949 Grace joined the Eckert-Mauchly Corporation to work on the UNIVAC. This computer was developed for commercial applications, and was 1,000 times faster than the Mark I. Grace was looking for commercial applications of the computer by asking the questions “Can you find a way to make the computer do what you’re doing now by hand?” And, “Now we know we have this powerful thing here, what new problem could be solved with this?”

There weren’t many programmers around at this time in history, and Grace recruited her programmers from a pool of people who liked so solve crossword puzzles and read mystery stories. If she was able to identify problem-solving capabilities in them, she was sure that she would be able to teach them how to program.

In 1952, Grace was frustrated by the error-prone process of copying certain blocks of code manually. To avoid the copy-paste errors the programmers were repeatedly doing, she encouraged them to put the commonly used blocks of code into a shared library. She went on to create a program to translate the blocks of code into machine language, effectively creating the first compiler.

Grace wanted to create a more high-level compiler and a programming language using English words. In 1957, the efforts of Grace and her team resulted in FLOW-MATIC, which was the first English-like programming language. The language helped the UNIVAC understand twenty English statements. FLOW-MATIC had a strong influence on the development of COBOL, which resulted in Grace being named “the grand-mother of COBOL”.

Final thoughts

Grace Hopper was very influential as one of the first programmers of the world. She also helped to widen the areas of application of computers. I found her story to be inspirational, and I recommend you to familiarize yourself with her achievements during the child-hood of computing. She had an exploratory mindset and a liberal management style. I’d like to end with the following quote:

“Liberate what we need. Set it free. It’s easier to apologize after you do something than to ask permission to do it.”

Grace Hopper

How you can stay up-to-date through MOOCs

In this post, I’ll share with you how you can use MOOCs to stay up-to-date with your field and acquire new skills. I’ll also share some of my experiences after having completed about 30 MOOCs in the last year and a half.

What is a MOOC?

Let us start with the basics. According to Wikipedia, MOOC is defined as:

A massive open online course (MOOC /muːk/) is an online course aimed at unlimited participation and open access via the web.

Wikipedia

MOOCs are a kind of distance education where you can consume the learning content whenever and wherever it suits you. A MOOC is typically built with the following building blocks:

  • Video lectures
  • Self-tests
  • Assignments (weekly / finals)
  • Collaborative projects
  • Discussion forums
  • Downloads (videos, slides, and transcripts)

The following mind-map by Mathieu Plourde (Mathplourde on Flickr) [CC BY 2.0] gives a nice overview of the concept and the flexibility of its implementation:

MOOC poster mathplourde

My first contact with MOOCs

I stumbled into the world of MOOCs in February 2018 after having found the MOOC platform of SAP, named openSAP. SAP was offering a technical course on test-driven development in the programming language ABAP, which I enrolled for. The course was great, and it changed how I keep up to date as a developer.

What can you use MOOCs to learn?

The MOOCs I’ve attended mainly fall into the following three categories:

  1. Technical skills
  2. Thought leaders
  3. New products and solutions

Technical skills

Being a developer, I always look for material to expand and improve my technical skills. This can be anything from learning a new programming language or a test framework to learning about a new NoSQL database.

The courses I’ve attended in this category typically consist of a lot of coding exercises, which for me personally is a very good way of learning. MOOCs that fall into this category are typically the most work-intensive.

Thought leaders

As a developer, it is important to widen your perspective and be aware of greater trends in technology and society. Attending MOOCs within this space helps you to keep up with new business practices, and the lecturers are typically business and academic thought leaders as well as top politicians.

New products and solutions

When working with enterprise software, you as a developer will often be asked by your customers about new products and solutions which they have heard about. Even if you have not yet had the opportunity to do hands-on work with these products and solutions, I recommend attending MOOCs within this area to at least have an overview of which scenarios the solutions are applicable to.

When and how I consume MOOCs

The major advantage of MOOCs over traditional classroom training as I see it is that you can consume the course content whenever it suits your personal schedule.

I have a daily subway commute of about 45 minutes in total. As long as I can find a seat, I think that this is an ideal opportunity to use MOOCs. The MOOCs I’ve attended typically have units with a length of approximately 20 minutes, which means that I can squeeze in two units a day, just going to work and back. I find this to be a much more productive use of my time than just scrolling through social media posts or playing games on my phone.

The assignments typically require at least an hour of focused work and are not ideal for my commute. I typically do the assignments when the rest of the family is sleeping.

Nice side-effects of attending MOOCs

Apart from the learning itself, which is, of course, the most important part of attending MOOCs, I’ve encountered the following nice side-effects:

  • Certificates and badges to put on your LinkedIn profile: If you receive a certificate or a badge, make sure to put it on your LinkedIn profile. Since the words of the MOOC are searchable, people looking to connect with people with a certain skill will find you through these search terms.
  • Free books: In one MOOC I attended, the teachers were publishing a book on the topic simultaneously to bringing out the MOOC. Since I was one of the top 30 students of the course, I received a free copy of the book in hardcover.
  • Discount coupons: Several MOOCs I’ve attended have given me a discount coupon at the end of the course. The coupons have been for books, conferences and paid e-learnings.
  • Invitations to conferences: Just the other day, I received an invitation to attend a conference on a topic for which I had attended a MOOC. I’d still need to pay for the conference, but I wouldn’t have been aware of the conference if I hadn’t attended the MOOC.

Potential downsides to attending MOOCs

The potential downsides I’ve encountered when attending MOOCs are the following:

  • Opportunity cost: When attending a MOOC, there is an opportunity cost. I’ve noticed that I’ve read fewer books when attending MOOCs due to time constraints. Make sure that the courses you are attending are really worth the time investment you are making.
  • Marketing material in disguise: A few of the MOOCs I’ve attended have been little more than sales pitches and marketing material from a software vendor trying to push a new product. This becomes obvious at an early stage, so don’t hesitate to drop out of a course with these characteristics. I’ve come across these courses mainly in the new products and solutions category.

MOOC platforms

There are a lot of MOOC platforms out there. I just want to give you a few examples to get you started. If you have any recommendations, please feel free to share them in the comments section below.

freeCodeCamp

At freecodecamp.org you can attend an online coding boot camp completely free of charge. The focus is mainly on front end web development with courses on topics such as responsive web design, JavaScript, front end libraries, and APIs. There are a lot of hands-on coding exercises available.

openSAP

At open.sap.com there are hundreds of courses available. Many of them are focused on SAP specific solutions and technologies, but there are also a lot of general courses available. Some examples are courses on Java, Snap!, design thinking, digital transformation, and AI. The courses are offered free of charge to anyone interested.

Udacity

At udacity.com you can acquire tech skills within different areas, like programming and development, AI, cloud computing, and data science. I’ve only taken a JavaScript course at Udacity, and am not all too familiar with the platform.

Final words

I hope you enjoyed reading my thoughts and experiences around the topic of MOOCs. Let me know your thoughts in the comment section. I plan to write a follow-up post with my favorite MOOCs attended so far. Happy learning!